Por que argc não é uma constante?

104
int main( const int argc , const char[] const argv)

Como Effective C ++ Item # 3 afirma "Use const sempre que possível", eu começo a pensar "por que não fazer esses parâmetros 'constantes' const"?

Existe algum cenário em que o valor de argcé modificado em um programa?

Dinushan
fonte
38
Você é livre para declarar argccomo const.
Oliver Charlesworth
14
Tenho visto muitos códigos de qualidade de produção que fazem isso:--argc
Aniket Inge
2
Estou pensando que tem a ver com o legado C, mas não saberia como.
Luis Machuca
13
Suspeito que "Use const sempre que possível" se refere a coisas passadas por referência, onde qualquer modificação dentro da função persiste depois que a função é encerrada. Isso não é verdade com coisas passadas por valor, como um número inteiro, então não há nada a ganhar com isso const; na verdade, passar argccomo um const intmeio que você não pode usar argccomo, digamos, um contador dentro da função.
Steve Melnikoff
4
@SteveMelnikoff: Não concordo que não haja nada a ganhar fazendo constum parâmetro de passagem por valor. Consulte, por exemplo, stackoverflow.com/a/8714278/277304 e stackoverflow.com/a/117557/277304
leonbloy

Respostas:

114

Nesse caso, a história é um fator. C definiu essas entradas como "não constantes" e a compatibilidade com (uma boa parte do) código C existente era um objetivo inicial do C ++.

Algumas APIs do UNIX, como getopt, realmente manipulam argv[], portanto, não podem ser feitas constpor esse motivo também.

(À parte: Curiosamente, embora getopto protótipo de sugira que ele não irá modificar, argv[]mas pode modificar as strings apontadas, a página do manual do Linux indica que getoptpermuta seus argumentos, e parece que eles sabem que estão sendo malcriados . A página do manual no Open O grupo não menciona esta permutação.)

Colocando constem argce argvnão comprar muito, e seria invalidar alguns old-school práticas, tais como a programação:

// print out all the arguments:
while (--argc)
    std::cout << *++argv << std::endl;

Escrevi esses programas em C e sei que não estou sozinho. Copiei o exemplo de algum lugar .

Joe Z
fonte
1
-1 Re "Algumas APIs do UNIX, como getopt, realmente manipulam argv [], portanto, não pode ser feito const por esse motivo também.". Pode-se adicionar livremente um nível superior consta argv, sem afetar o que qualquer função C pode fazer. Além disso, getopté declarado como int getopt(int argc, char * const argv[], const char *optstring);. E aqui o constnão é o nível superior, mas declara que os ponteiros são const, uma promessa de não modificá-los (embora as strings para as quais eles apontam possam ser modificadas).
Saúde e hth. - Alf
2
Sinto muito, os documentos que olhei são inconsistentes: a assinatura não permite tal permutação. Veja o exemplo . No entanto, o texto a seguir diz que sim. Então, estou removendo o downvote.
Saúde e hth. - Alf
1
Ou seja, você terá que fazer algumas edições para "desbloquear" para que eu possa remover o downvote. E não, você está entendendo mal o protótipo. Veja o exemplo que forneci e o erro de compilação, "atribuição de localização somente leitura".
Saúde e hth. - Alf
1
@ Cheersandhth.-Alf: É interessante ... Eu pesquisei o protótipo no cabeçalho e estava char* const* ___argvlá, mas a interface realmente adere const char **. Tanta confusão. Eu havia confundido a precedência do []e do *com respeito ao const. Me desculpe.
Joe Z
1
FYI: O GNU getopt()sabe que não segue o padrão porque presta atenção à POSIXLY_CORRECTvariável de ambiente para fazê-lo se comportar corretamente. Em particular, POSIX não permuta os argumentos e não procura opções após o primeiro argumento não opcional.
Jonathan Leffler
36

O padrão C (ISO / IEC 9899: 2011) diz:

5.1.2.2.1 Inicialização do programa

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

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

ou com dois parâmetros (referidos aqui como argce argv, embora quaisquer nomes possam ser usados, 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.

§2 Se forem declarados, os parâmetros da mainfunção 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]até argv[argc-1]inclusive devem conter ponteiros para strings, que recebem valores definidos pela implementação pelo ambiente host antes da inicialização do programa. A intenção é fornecer ao programa as informações determinadas antes da inicialização do programa de outro local no ambiente hospedado. Se o ambiente host não for capaz de fornecer strings com letras maiúsculas e minúsculas, a implementação deve garantir que as strings sejam recebidas em letras minúsculas.
  • Se o valor de argcfor maior que zero, a string 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 do host. Se o valor de argcfor maior que um, as strings apontadas por argv[1]através 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 de typedef definido como int, ou o tipo de argvpode ser escrito como char **argv, e assim por diante.

Observe o último marcador. Diz que ambos argce argvdevem ser modificáveis. Eles não precisam ser modificados, mas podem ser modificados.

Jonathan Leffler
fonte
Interessante. Em uma nota semi-relacionada, encontrei um bug que causou travamento que acabou sendo porque o QApplication(argc,argv)construtor do Qt foi definido com argcpor referência . Isso me surpreendeu.
HostileFork diz não confie em SE
23

argcnormalmente não é uma constante porque a assinatura da função para main()datas anteriores const.

Visto que argc é uma variável de pilha, alterá-lo não afetará nada além do seu próprio processamento de linha de comando.

Você é, naturalmente, livre para declará-lo, constse quiser.

arrasar
fonte
8

Um nível superior constem um argumento formal não faz parte do tipo de função. Você pode adicioná-lo ou removê-lo como quiser: isso afeta apenas o que você pode fazer com o argumento na implementação da função.

Portanto, argcvocê pode adicionar livremente um const.

Mas para argvvocê não pode fazer os dados de caractere constsem, portanto, alterar a assinatura da função. O que significa que não é uma das mainassinaturas de função padrão e não terá que ser reconhecida como uma mainfunção. Portanto, não é uma boa ideia.


Um bom motivo para não usar os mainargumentos padrão em programas que não sejam de brinquedo é que no Windows eles não podem representar argumentos de programa reais, como nomes de arquivo com caracteres internacionais. Isso ocorre porque no Windows eles são codificados por convenção muito forte como Windows ANSI. No Windows, você pode implementar algum recurso de acesso de argumento mais portátil em termos da GetCommandLinefunção API.


Resumindo, nada impede que você adicione consta argc, mas a maior utilidade constem argvforneceria uma mainfunção não padrão , muito provavelmente não reconhecida como tal. Felizmente (de uma forma irônica), há boas razões para não usar os mainargumentos padrão para código sério portátil. Simplesmente, para a prática, eles suportam apenas ASCII antigo, com apenas letras do alfabeto inglês.

Saúde e hth. - Alf
fonte
1
Se você estiver desenvolvendo para Windows com cygwin ou outro libc que suporte UTF-8 (até onde eu sei, cygwin é o único no momento, mas tenho alguém trabalhando em outra opção), a mainassinatura padrão funciona bem e você pode receber argumentos Unicode arbitrários.
R .. GitHub PARAR DE AJUDAR O ICE
@R também funcionam bem na maioria das plataformas * nix. a questão não é se existem plataformas onde eles funcionam. mas, muito boa iniciativa , e por acaso estou fazendo o mesmo, mais ou menos a sério
Saúde e alegria. - Alf
4

A assinatura de mainé uma espécie de artefato histórico de C. Historicamente C, não const.

No entanto, você pode declarar seu parâmetro, constpois os efeitos de const são apenas em tempo de compilação.

George
fonte
Quando você diz "C histórico", quão histórico estamos falando aqui?
Joe Z
8
constfoi adicionado a C89 / C90; antes disso, não fazia parte de C.
Jonathan Leffler
@JonathanLeffler: Sabe, eu tinha esquecido disso. Aprendi C em 1992. Antes disso, eu era um hacker Pascal, BASIC e assembly.
Joe Z
2

Porque argcé uma variável local (e, em C ++, não é uma referência ou algo assim), e porque o lugar especial de mainsignifica que as travessuras de compatibilidade com versões anteriores garantem a ele uma grande margem de manobra sem nenhuma razão convincente para forçar os aplicativos a torná-lo constante.

main() {}

int main() {}

main() { return 0; }

main(int argc, char* argv[]) { return 0; }

int main(const int argc, const char** argv) { /* no return*/ }

essas e muitas outras variações serão compiladas em uma ampla variedade de compiladores C e C ++.

Portanto, em última análise, não é que argc não seja const, apenas que não precisa ser, mas pode ser, se você quiser.

http://ideone.com/FKldHF , exemplo C:

main(const int argc, const char* argv[]) { return 0; }

http://ideone.com/m1qc9c , exemplo C ++

main(const int argc) {}
kfsone
fonte
Observe que as variantes sem um tipo de retorno explícito não são mais válidas no C99, muito menos no C11, embora os compiladores continuem a permiti-las por motivos de compatibilidade com versões anteriores. Os compiladores C ++ não devem aceitar as variantes sem um tipo de retorno.
Jonathan Leffler
@JonathanLeffler Sim, mas o último exemplo de ideone lá ( ideone.com/m1qc9c ) é G ++ 4.8.2 com -std=c++11. O Clang também aceita, mas dá warning: only one parameter on 'main' declaration [-Wmain], e o MSVC apenas protesta sobre o especificador de tipo de retorno ausente e a falta de uma referência a argc. Novamente, 'travessuras' e 'margem de manobra' :)
kfsone
1

Além das razões históricas, uma boa razão para manter argc e argv non- consté que a implementação do compilador não sabe o que você vai fazer com os argumentos de main, ela apenas sabe que deve fornecer esses argumentos.

Quando você está definindo suas próprias funções e protótipos associados, você sabe quais parâmetros pode fazer conste quais sua função irá modificar.

Levado ao extremo, você poderia declarar que todos os parâmetros para todas as funções deveriam ser declarados const, e então se você tivesse uma razão para alterá-los (por exemplo, diminuir um índice para pesquisar em um array), você teria que fazer não constvariáveis locais e copie os constvalores dos argumentos nessas variáveis. Isso torna o trabalho agitado e LOC extra sem nenhum benefício real. Um analisador estático decente pegará se você não estiver modificando o valor de um argumento e recomendará que você faça o parâmetro const.

Sam Skuce
fonte