O que main () deve retornar em C e C ++?

694

Qual é a maneira correta (mais eficiente) de definir a main()função em C e C ++ - int main()ou void main()- e por quê? Se int main()então return 1ou return 0?


Existem inúmeras duplicatas dessa pergunta, incluindo:

Relacionado:

Joel
fonte
28
Eu ainda acho que é bastante vago também. Definir "mais eficiente" para mim. Eficiente em que sentido? No sentido de ocupar menos memória? No sentido de correr mais rápido? Consigo ver as respostas úteis, mas ainda acho que a pergunta está mal formulada.
Onorio Catenacci
7
Pish elegante, o contexto de eficiente é óbvio aqui, especialmente com os exemplos (que provavelmente existem para esclarecer a definição de 'eficiente'). Espero que o pobre buffer não se arraste para um buraco e se arrependa totalmente da pergunta. Pode-se dizer, independentemente de void ou int, um valor é retornado, portanto não afeta o tamanho do arquivo, as operações executadas nem a memória alocada. E as pessoas, na maioria dos sistemas operacionais, tendem a retornar 0 com sucesso, e outra coisa com - outro - sucesso ou falha - mas não há um padrão. Por fim, não há diferença de eficiência de maneira óbvia.
KIT10
"correto (mais eficiente)" não faz sentido. Eficiente é uma coisa, correto é outra. mainé chamado uma vez (e em C ++ pode ser chamado apenas uma vez: sem recursão). Se você não deseja que a execução gaste muito tempo main, não invoque o programa várias vezes: faça com que o programa implemente a repetição.
Kaz
2
Acho interessante que nenhuma das respostas, até onde posso dizer, forneça um exemplo completo de trabalho, incluindo as #includedeclarações
puk 11/11/13
3
Os valores de retorno não fazem sentido em uma plataforma sem SO. Você não está retornando a nada. Se você entrar returnem main(...)um dispositivo incorporado, seu sistema entrará em um estado imprevisível e sua lavadora de roupas se tornará autoconsciente e tentará matá-lo. Então, nós usamos void main()nesse caso. Essa é uma prática padrão do setor em embarcações bare-metal.
3Dave

Respostas:

569

O valor de retorno para mainindica como o programa foi encerrado. A saída normal é representada por um valor de retorno 0 de main. A saída anormal é sinalizada por um retorno diferente de zero, mas não existe um padrão para a interpretação dos códigos diferentes de zero. Conforme observado por outros, void main()é proibido pelo padrão C ++ e não deve ser usado. As mainassinaturas válidas do C ++ são:

int main()

e

int main(int argc, char* argv[])

que é equivalente a

int main(int argc, char** argv)

Também é importante notar que, em C ++, int main()pode ser deixado sem uma declaração de retorno; nesse momento, o padrão é retornar 0. Isso também ocorre com um programa C99. Se return 0;deve ser omitido ou não, está aberto ao debate. O intervalo de assinaturas principais válidas do programa C é muito maior.

Eficiência não é um problema com a mainfunção. Só pode ser inserido e deixado uma vez (marcando o início e o término do programa) de acordo com o padrão C ++. Para C, a reinserção main()é permitida, mas deve ser evitada.

workmad3
fonte
69
Main pode ser inserido / esquerda várias vezes, mas esse programa provavelmente não iria ganhar nenhum prêmio de design;)
korona
13
O C99 também possui o recurso incorreto do C ++ que atingir o final da função main () é equivalente a retornar 0 - se main () estiver definido para retornar um tipo compatível com int (seção 5.1.2.2.3).
Jonathan Leffler
62
reinserir main não é válido em C ++. Explicitamente no padrão, 3.6.1.3 afirma que 'main não deve ser usado em um programa' #
workmad3 15/10/08
117
stdlib.h fornece EXIT_SUCCESS e EXIT_FAILURE para este fim
argila
20
0 e diferente de zero estão corretos, mas totalmente sem sentido para alguém que lê seu código. Esta pergunta prova que as pessoas não sabem o que são códigos válidos / inválidos. EXIT_SUCCESS / EXIT_FAILURE são muito mais claros.
JaredPar 15/10/08
169

A resposta aceita parece ser direcionada para C ++, então pensei em adicionar uma resposta que pertença a C, e isso difere de algumas maneiras.

ISO / IEC 9899: 1989 (C90):

main() deve ser declarado como:

int main(void)
int main(int argc, char **argv)

Ou equivalente. Por exemplo, int main(int argc, char *argv[])é equivalente ao segundo. Além disso, o inttipo de retorno pode ser omitido, pois é o padrão.

Se uma implementação permitir, main()pode ser declarada de outras maneiras, mas isso torna a implementação do programa definida e não está mais em conformidade estrita.

O padrão define 3 valores para retorno que estão estritamente em conformidade (ou seja, não se baseiam no comportamento definido pela implementação): 0e EXIT_SUCCESSpara uma finalização bem-sucedida e EXIT_FAILUREpara uma finalização malsucedida. Quaisquer outros valores não são padrão e a implementação é definida. main()deve ter uma returndeclaração explícita no final para evitar comportamento indefinido.

Finalmente, não há nada errado do ponto de vista dos padrões com a chamada main()de um programa.

ISO / IEC 9899: 1999 (C99):

Para C99, tudo é o mesmo que acima, exceto:

  • O inttipo de retorno não pode ser omitido.
  • Você pode omitir a declaração de retorno de main(). Se você faz, e main()terminou, há um implícito return 0.
Chris Young
fonte
1
@Lundin Eu não acho que você precise de uma citação para dizer que alguém tem permissão para criar um compilador que aceite programas em conformidade com padrões não-padrão, ou para ter um compilador não-padrão. Isso é conhecimento comum e bom senso
KABoissonneault
4
@KABoissonneault O comportamento definido pela implementação é um termo do padrão, em oposição ao comportamento completamente não documentado. Se você implementar algo listado como comportamento definido pela implementação, ainda seguirá o padrão. Nesse caso, C89, que foi citado, não lista esse comportamento definido pela implementação, daí a necessidade de citação, para provar que ele não está apenas inventando coisas do nada.
Lundin
1
@Lundin Você está vendo isso da maneira errada. O que estamos falando não é um comportamento definido pela implementação; estamos falando de uma implementação que se desvia do padrão, se assim o desejar. É mais como uma criança desobedecendo aos pais: você não precisa de uma citação dos pais para lhe dizer de que maneira uma criança pode ir contra o que os pais disseram. Você só sabe que o momento a criança escolhe a fazê-lo, eles não são mais compatíveis com guildelines dos pais
KABoissonneault
2
@KABoissonneault A parte que citei no meu comentário é definitivamente sobre o comportamento definido pela implementação (em oposição às extensões não-padrão do compilador .) Portanto, estou falando sobre o comportamento definido pela implementação. Se você está tendo um monólogo sobre outra coisa, boa sorte com isso.
Lundin
1
@Lundin Acho que o texto da citação é confuso (a parte em que eles dizem "mas isso define a implementação do programa"), mas tenho certeza de que a pessoa estava falando sobre comportamento fora do padrão (como dito em "Se uma implementação permite "e" e não está mais em conformidade estrita [com o padrão] ") em oposição ao comportamento definido da implementação real. A pessoa deve definitivamente reformular sua resposta, mas eu ainda não acho que uma citação do padrão é necessária em que
KABoissonneault
117

Padrão C - Ambiente Hospedado

Para um ambiente hospedado (esse é o normal), o padrão C11 (ISO / IEC 9899: 2011) diz:

5.1.2.2.1 Inicialização do programa

A função chamada na inicialização do programa é nomeada main. A implementação declara nenhum protótipo para esta função. Deve ser definido com um tipo de retorno de inte sem parâmetros:

int main(void) { /* ... */ }

ou com dois parâmetros (referidos aqui como argce argv, embora qualquer nome possa ser usado, pois são locais para a função na qual são declarados):

int main(int argc, char *argv[]) { /* ... */ }

ou equivalente; 10) ou de alguma outra maneira definida pela implementação.

Se forem declarados, os parâmetros para a função principal devem obedecer às seguintes restrições:

  • O valor de argcdeve ser não negativo.
  • argv[argc] deve ser um ponteiro nulo.
  • Se o valor de argcfor maior que zero, os membros da matriz argv[0]por meio de argv[argc-1]inclusões deverão conter ponteiros para seqüências de caracteres, que recebem valores definidos pela implementação pelo ambiente host antes da inicialização do programa. A intenção é fornecer as informações do programa determinadas antes da inicialização do programa de outro local do ambiente hospedado. Se o ambiente host não for capaz de fornecer seqüências de caracteres com letras maiúsculas e minúsculas, a implementação deve garantir que as sequências sejam recebidas em letras minúsculas.
  • Se o valor de argcfor maior que zero, a cadeia apontada por argv[0] representa o nome do programa; argv[0][0]deve ser o caractere nulo se o nome do programa não estiver disponível no ambiente host. Se o valor argcfor maior que um, as cordas apontado por argv[1]meio argv[argc-1] representam os parâmetros do programa.
  • Os parâmetros argce argve as cordas apontado pela argvmatriz deve ser modificáveis pelo programa, e mantêm seus valores última armazenados entre inicialização do programa e encerramento do programa.

10) Assim, intpode ser substituído por um nome typedef definido como int, ou o tipo de argvpode ser escrito como char **argve assim por diante.

Encerramento do programa em C99 ou C11

O valor retornado main()é transmitido para o 'ambiente' de uma maneira definida pela implementação.

5.1.2.2.3 Finalização do programa

1 Se o tipo de retorno da mainfunção for compatível int, um retorno da chamada inicial para a mainfunção é equivalente a chamar a exitfunção com o valor retornado pela mainfunção como argumento; 11) alcançar o }que encerra a mainfunção retorna um valor 0. Se o tipo de retorno não for compatível int, o status de finalização retornado ao ambiente host não é especificado.

11) De acordo com 6.2.4, a vida útil dos objetos com duração de armazenamento automático declarada main terminará no primeiro caso, mesmo onde não teria no segundo.

Observe que 0é obrigatório como 'sucesso'. Você pode usar EXIT_FAILUREe EXIT_SUCCESSde, <stdlib.h>se preferir, mas 0 está bem estabelecido e 1. também. Consulte também Códigos de saída maiores que 255 - possíveis? .

No C89 (e, portanto, no Microsoft C), não há nenhuma declaração sobre o que acontece se a main()função retornar, mas não especificar um valor de retorno; portanto, leva a um comportamento indefinido.

7.22.4.4 A exitfunção

Finally5 Finalmente, o controle é retornado ao ambiente host. Se o valor de statusfor zero ou EXIT_SUCCESS, uma forma definida pela implementação do status de finalização bem-sucedida será retornada. Se o valor de statusfor EXIT_FAILURE, será retornada uma forma definida pela implementação do encerramento sem êxito do status . Caso contrário, o status retornado é definido pela implementação.

C ++ padrão - ambiente hospedado

O padrão C ++ 11 (ISO / IEC 14882: 2011) diz:

3.6.1 Função principal [basic.start.main]

¶1 Um programa deve conter uma função global chamada main, que é o início designado do programa. [...]

§ 2 Uma implementação não deve predefinir a função principal. Esta função não deve estar sobrecarregada. Ele deve ter um tipo de retorno do tipo int, mas, caso contrário, seu tipo é definido como implementação. Todas as implementações devem permitir as duas seguintes definições de main:

int main() { /* ... */ }

e

int main(int argc, char* argv[]) { /* ... */ }

Nesta última forma, argcserá o número de argumentos transmitidos ao programa a partir do ambiente em que o programa é executado. Se argcfor diferente de zero estes argumentos devem ser fornecidos no argv[0] meio argv[argc-1]como indicadores para os caracteres iniciais de cordas de vários bytes de terminação nula (NTMBSs) (17.5.2.1.4.2) e argv[0]deve ser o ponteiro para o carácter inicial de uma NTMBS que representa o nome usado para invocar o programa ou "". O valor de argcdeve ser não negativo. O valor de argv[argc] deve ser 0. [Nota: Recomenda-se que outros parâmetros (opcionais) sejam adicionados depois argv. - end note]

§3 A função mainnão deve ser usada dentro de um programa. A ligação (3.5) de mainé definida pela implementação. [...]

¶5 Uma declaração de retorno em main tem o efeito de deixar a função principal (destruindo qualquer objeto com duração de armazenamento automático) e chamar std::exitcom o valor de retorno como argumento. Se o controle chegar ao final do main sem encontrar uma declaração de retorno, o efeito é o de executar

return 0;

O padrão C ++ diz explicitamente "Ele [a função principal] deve ter um tipo de retorno do tipo int, mas, caso contrário, seu tipo é definido pela implementação" e requer as mesmas duas assinaturas que o padrão C para ser suportado como opções. Portanto, um 'void main ()' não é diretamente permitido pelo padrão C ++, embora não haja nada que possa ser feito para interromper uma implementação fora do padrão que permita alternativas. Observe que o C ++ proíbe o usuário de chamar main(mas o padrão C não).

Há um parágrafo de §18.5 Início e término no padrão C ++ 11 que é idêntico ao parágrafo de §7.22.4.4 A exitfunção no padrão C11 (citado acima), além de uma nota de rodapé (que simplesmente documenta EXIT_SUCCESSe EXIT_FAILUREestá definida in <cstdlib>).

Padrão C - Extensão comum

Classicamente, os sistemas Unix suportam uma terceira variante:

int main(int argc, char **argv, char **envp) { ... }

O terceiro argumento é uma lista terminada em nulo de ponteiros para seqüências de caracteres, cada um dos quais é uma variável de ambiente que possui um nome, um sinal de igual e um valor (possivelmente vazio). Se você não usar isso, ainda poderá acessar o ambiente via ' extern char **environ;'. Essa variável global é única entre as do POSIX, pois não possui um cabeçalho que a declare.

Isso é reconhecido pela norma C como uma extensão comum, documentada no anexo J:

J.5.1 Argumentos do ambiente

¶1 Em um ambiente hospedado, a função principal recebe um terceiro argumento,, char *envp[]que aponta para uma matriz de ponteiros terminada em nulo para char, cada um dos quais aponta para uma cadeia que fornece informações sobre o ambiente para esta execução do programa (5.1. 2.2.1)

Microsoft C

O compilador do Microsoft VS 2010 é interessante. O site diz:

A sintaxe da declaração para main é

 int main();

ou, opcionalmente,

int main(int argc, char *argv[], char *envp[]);

Como alternativa, as funções maine wmainpodem ser declaradas como retornando void(sem valor de retorno). Se você declarar mainou wmainretornar nulo, não poderá retornar um código de saída para o processo pai ou sistema operacional usando uma instrução de retorno. Para retornar um código de saída quando mainou wmainé declarado como void, você deve usar a exitfunção

Não está claro para mim o que acontece (qual código de saída é retornado ao pai ou SO) quando um programa void main()sai - e o site da MS também fica silencioso.

Curiosamente, a MS não prescreve a versão de dois argumentos main()exigida pelos padrões C e C ++. Apenas prescreve uma forma de três argumentos em que o terceiro argumento é char **envpum ponteiro para uma lista de variáveis ​​de ambiente.

A página da Microsoft também lista algumas outras alternativas - wmain()que exigem amplas cadeias de caracteres e muito mais.

A versão do Microsoft Visual Studio 2005 desta página não lista void main()como alternativa. As versões do Microsoft Visual Studio 2008 em diante.

Padrão C - Ambiente Independente

Como observado anteriormente, os requisitos acima se aplicam aos ambientes hospedados. Se você estiver trabalhando com um ambiente independente (que é a alternativa a um ambiente hospedado), o padrão terá muito menos a dizer. Para um ambiente independente, a função chamada na inicialização do programa não precisa ser chamada maine não há restrições em seu tipo de retorno. O padrão diz:

5.1.2 Ambientes de execução

Dois ambientes de execução são definidos: autônomo e hospedado. Nos dois casos, a inicialização do programa ocorre quando uma função C designada é chamada pelo ambiente de execução. Todos os objetos com duração de armazenamento estático devem ser inicializados (definidos com seus valores iniciais) antes da inicialização do programa. A maneira e o momento dessa inicialização não são especificados. A finalização do programa retorna o controle para o ambiente de execução.

5.1.2.1 Ambiente independente

Em um ambiente independente (no qual a execução do programa C pode ocorrer sem qualquer benefício de um sistema operacional), o nome e o tipo da função chamada na inicialização do programa são definidos pela implementação. Quaisquer recursos de biblioteca disponíveis para um programa independente, além do conjunto mínimo exigido pela cláusula 4, são definidos pela implementação.

O efeito da finalização do programa em um ambiente independente é definido pela implementação.

A referência cruzada à cláusula 4 Conformidade refere-se a isso:

A5 Um programa estritamente conforme deve usar apenas os recursos do idioma e da biblioteca especificados nesta Norma. 3) Não deve produzir resultados dependentes de qualquer comportamento não especificado, indefinido ou definido pela implementação, e não deve exceder nenhum limite mínimo de implementação.

¶6 As duas formas de implementação em conformidade são hospedadas e independentes . Uma implementação hospedada em conformidade deve aceitar qualquer programa estritamente em conformidade. A implementação independente em conformidade aceitará qualquer programa rigorosamente de acordo em que o uso das características especificadas na cláusula biblioteca (cláusula 7) se limita ao conteúdo dos cabeçalhos padrão <float.h>, <iso646.h>, <limits.h>, <stdalign.h>, <stdarg.h>, <stdbool.h>, <stddef.h>, <stdint.h>, e <stdnoreturn.h>. Uma implementação em conformidade pode ter extensões (incluindo funções adicionais da biblioteca), desde que não alterem o comportamento de nenhum programa estritamente em conformidade. 4)

¶7 Um programa em conformidade é aquele que é aceitável para uma implementação em conformidade. 5)

3) Um programa estritamente conforme pode usar recursos condicionais (consulte 6.10.8.3), desde que o uso seja protegido por uma diretiva de pré-processamento de inclusão condicional apropriada, usando a macro relacionada. Por exemplo:

#ifdef __STDC_IEC_559__ /* FE_UPWARD defined */
    /* ... */
    fesetround(FE_UPWARD);
    /* ... */
#endif

4) Isso implica que uma implementação em conformidade não reserva outros identificadores além daqueles explicitamente reservados nesta Norma.

5) Os programas estritamente conformes devem ser portáteis ao máximo entre as implementações conformes. Os programas em conformidade podem depender de recursos não portáteis de uma implementação em conformidade.

É perceptível que o único cabeçalho necessário para um ambiente independente que realmente define quaisquer funções é <stdarg.h>(e mesmo essas podem ser - e geralmente são - apenas macros).

C ++ padrão - ambiente independente

Assim como o padrão C reconhece o ambiente hospedado e independente, o mesmo ocorre com o padrão C ++. (Cotações da ISO / IEC 14882: 2011.)

1.4 Conformidade da implementação [intro.compliance]

¶7 Dois tipos de implementações são definidos: uma implementação hospedada e uma implementação independente . Para uma implementação hospedada, este Padrão Internacional define o conjunto de bibliotecas disponíveis. Uma implementação independente é aquela na qual a execução pode ocorrer sem o benefício de um sistema operacional e possui um conjunto de bibliotecas definido pela implementação que inclui determinadas bibliotecas de suporte ao idioma (17.6.1.3).

A8 Uma implementação em conformidade pode ter extensões (incluindo funções adicionais da biblioteca), desde que não alterem o comportamento de nenhum programa bem formado. São necessárias implementações para diagnosticar programas que usam extensões mal formadas de acordo com esta Norma. Tendo feito isso, no entanto, eles podem compilar e executar esses programas.

Each9 Cada implementação deve incluir documentação que identifique todas as construções suportadas condicionalmente que não suportam e define todas as características específicas da localidade. 3

3) Esta documentação também define o comportamento definido pela implementação; ver 1.9.

17.6.1.3 Implementações autônomas [conformidade]

Dois tipos de implementações são definidos: hospedado e autônomo (1.4). Para uma implementação hospedada, este Padrão Internacional descreve o conjunto de cabeçalhos disponíveis.

Uma implementação independente possui um conjunto de cabeçalhos definido pela implementação. Este conjunto deve incluir pelo menos os cabeçalhos mostrados na Tabela 16.

A versão fornecido do cabeçalho <cstdlib>declarará, pelo menos, as funções abort, atexit, at_quick_exit, exit, e quick_exit(18.5). Os outros cabeçalhos listados nesta tabela devem atender aos mesmos requisitos de uma implementação hospedada.

Tabela 16 - Cabeçalhos C ++ para implementações independentes

Subclause                           Header(s)
                                    <ciso646>
18.2  Types                         <cstddef>
18.3  Implementation properties     <cfloat> <limits> <climits>
18.4  Integer types                 <cstdint>
18.5  Start and termination         <cstdlib>
18.6  Dynamic memory management     <new>
18.7  Type identification           <typeinfo>
18.8  Exception handling            <exception>
18.9  Initializer lists             <initializer_list>
18.10 Other runtime support         <cstdalign> <cstdarg> <cstdbool>
20.9  Type traits                   <type_traits>
29    Atomics                       <atomic>

Que tal usar int main()em C?

O padrão §5.1.2.2.1 do padrão C11 mostra a notação preferida -  int main(void)- mas também existem dois exemplos no padrão que mostram int main(): §6.5.3.4 ¶8 e §6.7.6.3 ¶20 . Agora, é importante notar que os exemplos não são 'normativos'; eles são apenas ilustrativos. Se houver erros nos exemplos, eles não afetam diretamente o texto principal do padrão. Dito isto, eles são fortemente indicativos do comportamento esperado; portanto, se o padrão incluir int main()um exemplo, isso sugere que int main()não é proibido, mesmo que não seja a notação preferida.

6.5.3.4 Os operadores sizeofe_Alignof

...

¶8 EXEMPLO 3 Neste exemplo, o tamanho de uma matriz de comprimento variável é calculado e retornado de uma função:

#include <stddef.h>

size_t fsize3(int n)
{
    char b[n+3]; // variable length array
    return sizeof b; // execution time sizeof
}
int main()
{
    size_t size;
    size = fsize3(10); // fsize3 returns 13
    return 0;
}
Jonathan Leffler
fonte
@DavidBowling: Uma definição de função como int main(){ … }especifica que a função não aceita argumentos, mas não fornece um protótipo de função, AFAICT. Pois main()isso raramente é um problema; significa que, se você tiver chamadas recursivas main(), os argumentos não serão verificados. Para outras funções, é mais um problema - você realmente precisa de um protótipo no escopo quando a função é chamada para garantir que os argumentos estejam corretos.
precisa
1
@ DavidBowling: Você normalmente não liga de forma main()recursiva, fora de lugares como o IOCCC. Eu tenho um programa de teste que faz isso - principalmente para novidades. Se você tem int i = 0; int main() { if (i++ < 10) main(i, i * i); return 0; }e compila com o GCC e não o inclui -Wstrict-prototypes, ele é compilado corretamente sob avisos rigorosos. Se for main(void), não consegue compilar.
precisa
61

Eu acredito que main()deve retornar EXIT_SUCCESSou EXIT_FAILURE. Eles são definidos emstdlib.h

dmityugov
fonte
20
0 também é padrão.
Chris Young
2
@ ChrisYoung Existe EXIT_SUCCESSe EXIT_FAILUREporque alguns sistemas operacionais históricos (VMS?) Usaram um número diferente de 0 para indicar sucesso. Hoje é 0 em todos os lugares.
fuz 17/09/14
5
@FUZxxl você está correto, mas isso não está em conflito com o meu comentário. EXIT_SUCCESS pode realmente ser diferente de zero, mas todos os padrões (C89, C99, C11) definem 0 (assim como EXIT_SUCCESS) também são uma forma definida pela implementação do encerramento bem-sucedido do status.
Chris Young
2
@FUZxxl: É verdade que o VMS usou valores ímpares (como 1) para indicar sucesso e valores pares (como 0) para indicar falha. Infelizmente, o padrão ANSI C original foi interpretado como significando que EXIT_SUCCESS tinha que ser 0; portanto, retornar EXIT_SUCCESS do main obteve exatamente o comportamento errado no VMS. A coisa portátil a fazer para o VMS era usar exit(EXIT_SUCCESS), o que sempre fazia a coisa certa.
Adrian McCarthy
1
5.1.2.2.3 "Se o tipo de retorno da função principal for compatível com int, um retorno da chamada inicial para a função principal será equivalente a chamar a função de saída com o valor retornado pela função principal como argumento; 11) alcançar o} que encerra a função principal retorna um valor de 0. "
Lundin
38

Observe que os padrões C e C ++ definem dois tipos de implementações: autônomo e hospedado.

  • Ambiente hospedado C90

    Formulários permitidos 1 :

    int main (void)
    int main (int argc, char *argv[])
    
    main (void)
    main (int argc, char *argv[])
    /*... etc, similar forms with implicit int */

    Comentários:

    Os dois primeiros são explicitamente declarados como os formulários permitidos, os outros são implicitamente permitidos porque o C90 permitiu "int implícito" para os parâmetros de tipo e função de retorno. Nenhum outro formulário é permitido.

  • Ambiente independente C90

    Qualquer forma ou nome de main é permitido 2 .

  • Ambiente hospedado C99

    Formulários permitidos 3 :

    int main (void)
    int main (int argc, char *argv[])
    /* or in some other implementation-defined manner. */

    Comentários:

    O C99 removeu "int implícito" e, portanto, main()não é mais válido.

    Uma frase estranha e ambígua "ou de alguma outra maneira definida pela implementação" foi introduzida. Isso pode ser interpretado como "os parâmetros para int main()variar" ou "principal pode ter qualquer forma definida pela implementação".

    Alguns compiladores optaram por interpretar o padrão da última maneira. Indiscutivelmente, não se pode afirmar facilmente que eles não estão em conformidade estrita citando o padrão em si, pois é ambíguo.

    No entanto, permitir formas completamente selvagens de main()provavelmente não era (?) A intenção dessa nova frase. A lógica C99 (não normativa) implica que a sentença se refere a parâmetros adicionais para int main 4 .

    No entanto, a seção para encerramento do programa do ambiente hospedado continua discutindo sobre o caso em que main não retorna int 5 . Embora essa seção não seja normativa sobre como main deve ser declarado, ela definitivamente implica que main pode ser declarado de uma maneira completamente definida pela implementação, mesmo em sistemas hospedados.

  • Ambiente independente C99

    Qualquer forma ou nome de main é permitido 6 .

  • Ambiente hospedado C11

    Formulários permitidos 7 :

    int main (void)
    int main (int argc, char *argv[])
    /* or in some other implementation-defined manner. */
  • Ambiente independente C11

    Qualquer forma ou nome de main é permitido 8 .


Observe que int main()nunca foi listado como um formulário válido para qualquer implementação hospedada de C em qualquer uma das versões acima. Em C, diferente de C ++, ()e (void)tem significados diferentes. O primeiro é um recurso obsoleto que pode ser removido do idioma. Consulte as instruções futuras sobre o idioma C11:

6.11.6 Declaradores de função

O uso de declaradores de função com parênteses vazios (não declaradores de tipo de parâmetro no formato de protótipo) é um recurso obsoleto.


  • Ambiente hospedado em C ++ 03

    Formulários permitidos 9 :

    int main ()
    int main (int argc, char *argv[])

    Comentários:

    Observe o parêntese vazio no primeiro formulário. C ++ e C são diferentes nesse caso, porque em C ++ isso significa que a função não aceita parâmetros. Mas em C, isso significa que pode levar qualquer parâmetro.

  • Ambiente independente C ++ 03

    O nome da função chamada na inicialização é definido pela implementação. Se for nomeado main(), deve seguir os formulários 10 :

    // implementation-defined name, or 
    int main ()
    int main (int argc, char *argv[])
  • Ambiente hospedado em C ++ 11

    Formulários permitidos 11 :

    int main ()
    int main (int argc, char *argv[])

    Comentários:

    O texto do padrão foi alterado, mas tem o mesmo significado.

  • Ambiente independente C ++ 11

    O nome da função chamada na inicialização é definido pela implementação. Se for nomeado main(), deve seguir os formulários declarados 12 :

    // implementation-defined name, or 
    int main ()
    int main (int argc, char *argv[])

Referências

  1. ANSI X3.159-1989 2.1.2.2 Ambiente hospedado. "Inicialização do programa"

    A função chamada na inicialização do programa é denominada main. A implementação declara nenhum protótipo para esta função. Deve ser definido com um tipo de retorno int e sem parâmetros:

    int main(void) { /* ... */ } 

    ou com dois parâmetros (referidos aqui como argc e argv, embora qualquer nome possa ser usado, pois são locais para a função na qual são declarados):

    int main(int argc, char *argv[]) { /* ... */ }
  2. ANSI X3.159-1989 2.1.2.1 Ambiente independente:

    Em um ambiente independente (no qual a execução do programa C pode ocorrer sem qualquer benefício de um sistema operacional), o nome e o tipo da função chamada na inicialização do programa são definidos pela implementação.

  3. ISO 9899: 1999 5.1.2.2 Ambiente hospedado -> 5.1.2.2.1 Inicialização do programa

    A função chamada na inicialização do programa é denominada main. A implementação declara nenhum protótipo para esta função. Deve ser definido com um tipo de retorno int e sem parâmetros:

    int main(void) { /* ... */ } 

    ou com dois parâmetros (referidos aqui como argc e argv, embora qualquer nome possa ser usado, pois são locais para a função na qual são declarados):

    int main(int argc, char *argv[]) { /* ... */ }

    ou equivalente; 9) ou de alguma outra maneira definida pela implementação.

  4. Fundamentação da Norma Internacional - Linguagens de Programação - C, Revisão 5.10. 5.1.2.2 Ambiente hospedado -> 5.1.2.2.1 Inicialização do programa

    O comportamento dos argumentos para main e da interação de saída, main e atexit (consulte §7.20.4.2) foi codificado para reduzir uma variedade indesejada na representação de strings de argv e no significado dos valores retornados por main.

    A especificação de argc e argv como argumentos para main reconhece uma prática anterior extensa. argv [argc] deve ser um ponteiro nulo para fornecer uma verificação redundante para o final da lista, também com base na prática comum.

    main é a única função que pode ser declarada portably com zero ou dois argumentos. (O número de argumentos de outras funções deve corresponder exatamente entre invocação e definição.) Esse caso especial simplesmente reconhece a prática generalizada de deixar de lado os argumentos para main quando o programa não acessa as seqüências de argumentos do programa. Enquanto muitas implementações suportam mais de dois argumentos para manter, essa prática não é abençoada nem proibida pela Norma; um programa que define main com três argumentos não está em conformidade estrita (consulte §J.5.1.).

  5. ISO 9899: 1999 5.1.2.2 Ambiente hospedado -> 5.1.2.2.3 Finalização do programa

    Se o tipo de retorno da função principal for compatível com int, um retorno da chamada inicial para a função principal equivale a chamar a função de saída com o valor retornado pela função principal como argumento; 11) atingindo o }que termina a função principal retorna um valor 0. Se o tipo de retorno não for compatível com int, o status de finalização retornado ao ambiente host não será especificado.

  6. ISO 9899: 1999 5.1.2.1 Ambiente independente

    Em um ambiente independente (no qual a execução do programa C pode ocorrer sem qualquer benefício de um sistema operacional), o nome e o tipo da função chamada na inicialização do programa são definidos pela implementação.

  7. ISO 9899: 2011 5.1.2.2 Ambiente hospedado -> 5.1.2.2.1 Inicialização do programa

    Esta seção é idêntica à C99 citada acima.

  8. ISO 9899: 1999 5.1.2.1 Ambiente independente

    Esta seção é idêntica à C99 citada acima.

  9. ISO 14882: 2003 3.6.1 Função principal

    Uma implementação não deve predefinir a função principal. Esta função não deve estar sobrecarregada. Ele deve ter um tipo de retorno do tipo int, mas, caso contrário, seu tipo é definido pela implementação. Todas as implementações devem permitir as duas seguintes definições de main:

    int main() { /* ... */ }

    e

    int main(int argc, char* argv[]) { /* ... */ }
  10. ISO 14882: 2003 3.6.1 Função principal

    É definido pela implementação se um programa em um ambiente independente é necessário para definir uma função principal.

  11. ISO 14882: 2011 3.6.1 Função principal

    Uma implementação não deve predefinir a função principal. Esta função não deve estar sobrecarregada. Ele deve ter um tipo de retorno do tipo int, mas, caso contrário, seu tipo é definido pela implementação. Todas as implementações devem permitir tanto

    - uma função de () retornando int e

    - uma função de (int, ponteiro para ponteiro para char) retornando int

    como o tipo de main (8.3.5).

  12. ISO 14882: 2011 3.6.1 Função principal

    Esta seção é idêntica à C ++ 03 citada acima.

Lundin
fonte
Uma pergunta: Os padrões C ++ significam que a assinatura da função de inicialização em ambientes independentes também é definida como implementação? Por exemplo, uma implementação poderia ter definido a função de inicialização como: int my_startup_function ()ou, int my_startup_function (int argc, char *argv[])mas pode ter, por exemplo: char my_startup_function (long argc, int *argv[])também uma função de inicialização? Eu acho que não, certo? Além disso, isso não é ambíguo também?
Utku
@Utku Ele pode ter qualquer assinatura, desde que não seja nomeado main()porque deve usar uma das assinaturas listadas. Eu imaginaria o esmagadoramente mais comum void my_startup_function (), pois não faz sentido retornar do programa em sistemas independentes.
Lundin
1
Eu vejo. Mas, se é permitido usar qualquer nome e assinatura para a função de inicialização, por que não permitir também usar uma assinatura diferente main? Desculpe se essa não é uma pergunta inteligente, mas eu não conseguia entender o raciocínio por trás.
Utku
@Utku C e C ++ são diferentes lá. Quanto ao motivo pelo qual o C ++ impõe isso, não faço ideia, não há lógica. Suspeito que o principal culpado (trocadilho intencional) seja Stroustrup, que logo declarou que o principal deve retornar int, ponto final. Porque quando ele criou a primeira versão do C ++, ele estava acostumado apenas a sistemas hospedados. No post vinculado, Stroustrup ainda parece alheio à existência de implementações independentes: por exemplo, ele se refere ignoradamente ao subcapítulo de implementação hospedado do padrão C, ignorando a existência do capítulo 5.1.2.1.
Lundin
1
O que é notável no rascunho padrão do C11 é que, embora func()seja considerado obsoleto, o próprio rascunho usa int main()em seus próprios exemplos.
Antti Haapala
29

Retorne 0 em caso de sucesso e diferente de zero por erro. Esse é o padrão usado pelos scripts UNIX e DOS para descobrir o que aconteceu com o seu programa.

Lou Franco
fonte
8

main() nos tipos de retorno não especificados em C89 e K&R C, o padrão é 'int'.

return 1? return 0?
  1. Se você não escrever uma declaração de retorno int main(), o fechamento {retornará 0 por padrão.

  2. return 0ou return 1será recebido pelo processo pai. Em um shell, ele entra em uma variável do shell e, se você estiver executando seu programa, forme um shell e não a utilize, não precisará se preocupar com o valor de retorno de main().

Consulte Como posso obter o que minha função principal retornou? .

$ ./a.out
$ echo $?

Dessa forma, você pode ver que é a variável $?que recebe o byte menos significativo do valor de retorno de main().

Nos scripts Unix e DOS, return 0em caso de sucesso e diferente de zero para erro, geralmente são retornados. Este é o padrão usado pelos scripts Unix e DOS para descobrir o que aconteceu com o seu programa e controlar todo o fluxo.

Jeegar Patel
fonte
4
A rigor, $?não é uma variável de ambiente; é uma variável predefinida (ou incorporada) do shell. A diferença é difícil de detectar, mas se você executar env(sem argumentos), ele imprime o ambiente e $?não será mostrado no ambiente.
Jonathan Leffler
1
Retornando 0 automaticamente quando as principais "quedas do fim" estão apenas em C ++ e C99 em diante, não em C90.
Kaz
Erro de digitação: "fechamento {" deve ser }. O SO não permitirá que eu faça uma edição tão pequena.
Spencer
7

Lembre-se de que, mesmo retornando um int, alguns sistemas operacionais (Windows) truncam o valor retornado para um único byte (0-255).

Ferruccio
fonte
4
O Unix faz o mesmo, assim como a maioria dos outros sistemas operacionais. Sei que o VMS faz coisas estranhas tão incríveis que retornar qualquer coisa que não seja EXIT_SUCCESS ou EXIT_FAILURE está pedindo problemas.
Leon Timmermans
2
O MSDN implora para diferir: quando relatado pelo mscorlib, um código de saída é um número inteiro de 32 bits assinado . Isso parece implicar que as bibliotecas de tempo de execução C que truncam os códigos de saída estão com defeito.
Sim, isso está incorreto. No Windows, um número inteiro de 32 bits é retornado (e convertido em unsigned). É o mesmo em sistemas UNIX com números inteiros de 32 bits. Mas as conchas no estilo UNIX em ambos os sistemas normalmente retêm apenas um número inteiro de 8 bits não assinado.
John McFarlane
4

O valor de retorno pode ser usado pelo sistema operacional para verificar como o programa foi fechado.

O valor de retorno 0 geralmente significa OK na maioria dos sistemas operacionais (os que eu consigo pensar de qualquer maneira).

Também pode ser verificado quando você chama um processo por conta própria e vê se o programa foi encerrado e finalizado corretamente.

NÃO é apenas uma convenção de programação.

Yochai Timmer
fonte
Não há nada na pergunta indicando que um sistema operacional esteja presente. Retornar um valor não faz sentido em um sistema independente.
Lundin
3

O valor de retorno main()mostra como o programa foi encerrado. Se o valor de retorno for zero, significa que a execução foi bem-sucedida, enquanto qualquer valor diferente de zero representará que algo deu errado na execução.

fuddin
fonte
1
Este é um comentário, não uma resposta para a pergunta.
Lundin
2

Fiquei com a impressão de que o padrão especifica que main não precisa de um valor de retorno, pois um retorno bem-sucedido foi baseado no SO (zero em um pode ser um sucesso ou uma falha em outro); portanto, a ausência de retorno foi uma sugestão para o compilador para inserir o retorno bem-sucedido.

No entanto, geralmente retorno 0.

graham.reeds
fonte
C99 (e C ++ 98) permitem omitir a declaração de retorno de main; C89 não permite que você omita a declaração de retorno.
Jonathan Leffler
Este é um comentário, não uma resposta.
Lundin 07/07
Isso não fornece uma resposta para a pergunta. Para criticar ou solicitar esclarecimentos a um autor, deixe um comentário abaixo da postagem.
Steve Lillis
6
@SteveLillis: Em 2008, o SO não tinha uma seção de comentários.
graham.reeds
2

Retornar 0 deve informar ao programador que o programa concluiu o trabalho com êxito.

Vamsi Pavan Mahesh
fonte
Retornar 1 de main()normalmente sinaliza que ocorreu um erro; retornando 0 sinais de sucesso. Se seus programas sempre falharem, 1 está OK, mas não é a melhor idéia.
Jonathan Leffler
1
@ JonathanLeffler: O significado de retornar 1de mainé definido pela implementação. Os valores só definiu-idioma são 0, EXIT_SUCCESS(muitas vezes definido como 0), e EXIT_FAILURE. No OpenVMS, return 1;denota finalização bem-sucedida .
53014 Keith Thompson
O VMS não é 'normal' - no sentido do que eu disse. Não é algo como 'qualquer valor estranho é sucesso; mesmo valores são falha 'no VMS?
Jonathan Leffler
2

Omitir return 0

Quando um programa C ou C ++ chega ao final do maincompilador, o código é gerado automaticamente para retornar 0, portanto, não há necessidade de colocar return 0;explicitamente no final de main.

Nota: quando faço essa sugestão, é quase invariavelmente seguida por um dos dois tipos de comentários: "Eu não sabia disso". ou "Isso é um mau conselho!" Minha lógica é que é seguro e útil confiar no comportamento do compilador explicitamente suportado pelo padrão. Para C, desde C99; veja ISO / IEC 9899: 1999, seção 5.1.2.2.3:

[...] um retorno da chamada inicial para a mainfunção é equivalente a chamar a exitfunção com o valor retornado pela mainfunção como argumento; atingir o }que termina a mainfunção retorna um valor 0.

Para C ++, desde o primeiro padrão em 1998; veja ISO / IEC 14882: 1998, seção 3.6.1:

Se o controle chegar ao final do main sem encontrar uma declaração de retorno, o efeito é o de executar o retorno 0;

Todas as versões dos dois padrões desde então (C99 e C ++ 98) mantiveram a mesma idéia. Contamos com funções-membro geradas automaticamente em C ++, e poucas pessoas escrevem return;instruções explícitas no final de uma voidfunção. Razões contra a omissão parecem resumir-se a "parece estranho" . Se, como eu, você estiver curioso sobre a justificativa para a mudança no padrão C, leia esta pergunta . Observe também que, no início dos anos 90, isso era considerado "prática descuidada" porque era um comportamento indefinido (embora amplamente aceito) na época.

Além disso, as Diretrizes Principais do C ++ contêm várias instâncias de omissão return 0;no final maine nenhuma instância na qual um retorno explícito é gravado. Embora ainda não exista uma diretriz específica sobre esse tópico específico nesse documento, isso parece pelo menos um endosso tácito da prática.

Então, eu advogo a omissão; outros discordam (muitas vezes veementemente!) De qualquer forma, se você encontrar um código que o omita, saberá que ele é explicitamente suportado pelo padrão e saberá o que isso significa.

Edward
fonte
2
Esse é um péssimo conselho, porque os compiladores que implementam apenas o C89, e não qualquer padrão posterior, ainda são extremamente comuns (escrevo isso em 2017) e permanecerão extremamente comuns no futuro próximo. Por exemplo, a última vez que verifiquei nenhuma versão dos compiladores da Microsoft implementou o C99, e eu entendo que isso também é típico para compiladores de sistemas incorporados que não são GCC.
Zwol 13/05
4
@zwol: Qualquer pessoa que não tenha escolha a não ser usar um compilador desatualizado por 28 anos provavelmente tem mais problemas do que decidir se deve incluir explicitamente return 0;, no entanto, eu observaria que muitos compiladores daquela época também implementaram um implícito return 0;antes mesmo de padronizado.
Edward
2
Na verdade, trabalho bastante em sistemas embarcados e não encontro um compilador que não suporte implícito return 0há mais de uma década. Também as versões atuais do Microsoft C também são compatíveis . Talvez sua informação esteja desatualizada?
Edward
2
Aprecio que isso seja controverso em C, quase (por @zwol). Em C ++, qualquer controvérsia em torno disso é pura bobagem.
Lightness Races em órbita em
2
@ Edward Eu não disse que a controvérsia não existia, eu disse que era um absurdo: P
Lightness Races in Orbit
1

O que retornar depende do que você deseja fazer com o executável. Por exemplo, se você estiver usando seu programa com um shell de linha de comando, será necessário retornar 0 para um sucesso e um zero para falha. Você poderá usar o programa em shells com processamento condicional, dependendo do resultado do seu código. Além disso, você pode atribuir qualquer valor diferente de zero de acordo com sua interpretação, por exemplo, para erros críticos, diferentes pontos de saída do programa podem encerrar um programa com diferentes valores de saída e que está disponível para o shell de chamada que pode decidir o que fazer inspecionando o valor retornado. Se o código não se destina ao uso com shells e o valor retornado não incomoda ninguém, ele pode ser omitido. Eu pessoalmente uso a assinaturaint main (void) { .. return 0; .. }

phoxis
fonte
O formato de main () é determinado pela implementação, o que significa compilador. O programador não escolhe qual formulário escolher, exceto quando um compilador oferece suporte a vários formulários.
precisa
@Lundin O tipo de retorno será implementado pela implementação. Mas o valor a ser retornado é decidido pelo programador. A Seção 5.1.2.2.3 da C99 menciona que o tipo de retorno de mainé compatível int. Portanto, retornar intnão será um problema. Embora outros tipos de retorno sejam permitidos, nesse caso a variável de ambiente que possui o valor de retorno não será especificada. Mas se um programador faz return 0;isso no bash, pode ser usado para criar ramificações.
Phoxis
1

Se você realmente tiver problemas relacionados à eficiência de retornar um número inteiro de um processo, provavelmente evite chamar esse processo tantas vezes que esse valor de retorno se torne um problema.

Se você estiver fazendo isso (chame um processo tantas vezes), deverá encontrar uma maneira de colocar sua lógica diretamente dentro do chamador ou em um arquivo DLL, sem alocar um processo específico para cada chamada; as várias alocações de processos trazem a você o problema de eficiência relevante nesse caso.

Em detalhes, se você quiser apenas saber se retornar 0 é mais ou menos eficiente que retornar 1, em alguns casos, pode depender do compilador, mas genericamente, supondo que sejam lidos da mesma fonte (local, campo, constante, incorporado). no código, resultado da função etc.) requer exatamente o mesmo número de ciclos de relógio.

Luca C.
fonte
1

Aqui está uma pequena demonstração do uso de códigos de retorno ...

Ao usar as várias ferramentas fornecidas pelo terminal Linux, é possível usar o código de retorno, por exemplo, para tratamento de erros após a conclusão do processo. Imagine que o seguinte arquivo de texto myfile esteja presente:

Este é um exemplo para verificar como o grep funciona.

Quando você executa o comando grep, um processo é criado. Uma vez concluído (e não quebrou), ele retorna algum código entre 0 e 255. Por exemplo:

$ grep order myfile

Se você fizer

$ echo $?
$ 0

você receberá um 0. Por quê? Como o grep encontrou uma correspondência e retornou um código de saída 0, que é o valor usual para sair com êxito. Vamos dar uma olhada novamente, mas com algo que não está dentro do nosso arquivo de texto e, portanto, nenhuma correspondência será encontrada:

$ grep foo myfile
$ echo $?
$ 1

Como o grep falhou ao combinar o token "foo" com o conteúdo do nosso arquivo, o código de retorno é 1 (este é o caso usual em que ocorre uma falha, mas conforme indicado acima, você tem muitos valores para escolher).

Agora, o seguinte script bash (simplesmente digite-o em um terminal Linux), apesar de muito básico, deve dar uma idéia do tratamento de erros:

$ grep foo myfile
$ CHECK=$?
$ [ $CHECK -eq 0] && echo 'Match found'
$ [ $CHECK -ne 0] && echo 'No match was found'
$ No match was found

Após a segunda linha, nada é impresso no terminal, já que "foo" fez o grep retornar 1 e verificamos se o código de retorno do grep era igual a 0. A segunda instrução condicional ecoa sua mensagem na última linha, pois é verdadeira devido a CHECK == 1.

Como você pode ver se está chamando esse e aquele processo, às vezes é essencial ver o que ele retornou (pelo valor de retorno de main ()).

rbaleksandar
fonte
Em um script de shell, você usaria if grep foo myfile; then echo 'Match found'; else echo 'No match was found'; fi- testando o status de retorno diretamente. Se você deseja capturar o status (para relatórios, etc), usa uma atribuição. Você pode usar if grep foo myfile; CHECK=$?; [ "$CHECK" = 0 ]; then echo 'Match found'; else echo 'No match was found'; fiou pode usar três linhas. Você também pode usar as opções -se -qpara grepimpedir que correspondências ou mensagens de erro de rotina apareçam. No entanto, essas são minúcias do shell - o ponto principal, que o status de saída pode ser útil - está OK.
Jonathan Leffler
1

Qual é a maneira correta (mais eficiente) de definir a função main () em C e C ++ - int main () ou void main () - e por quê?

Essas palavras "(mais eficientes)" não mudam a questão. A menos que você esteja em um ambiente independente, existe uma maneira universalmente correta de declarar main(), e é como retornar int.

O que deve main()retornar em C e C ++?

Não é o que deve main() retornar, é o que faz main() retorno. main()é, obviamente, uma função que alguém chama. Você não tem nenhum controle sobre o código que chama main(). Portanto, você deve declarar main()com uma assinatura do tipo correto para corresponder ao chamador. Você simplesmente não tem escolha no assunto. Você não precisa se perguntar o que é mais ou menos eficiente, ou o que é melhor ou pior, ou algo assim, porque a resposta já está perfeitamente bem definida, para você, pelos padrões C e C +. Apenas siga-os.

Se int main (), retorne 1 ou retorne 0?

0 para sucesso, diferente de zero para falha. Novamente, não é algo que você precisa (ou consegue) escolher: é definido pela interface com a qual você deveria estar em conformidade.

Steve Summit
fonte