Por que precisamos de argc enquanto sempre há um nulo no final de argv?

116

Parece que argv[argc]é sempre NULL, então acho que podemos percorrer a lista de argumentos sem argc. Um único whileloop fará isso.

Se há sempre um NULLno final de argv, por que precisamos de um argc?

StarPinkER
fonte
17
Provavelmente é uma questão de conveniência. Fornece ao programador uma maneira fácil de escapar mais cedo se não houver argumentos suficientes, sem loop. Caso contrário, certamente teríamos funções chamadas int argc(char *argv[])fazendo exatamente isso :-))
cnicutar
5
Só para ficar claro, "\0"não é o mesmo que ponteiro NULL ( 0é equivalente a NULL em C ++)
Mats Petersson
31
Por que precisamos que argv [argc] seja NULL se temos argc?
Ambroz Bizjak
7
De que outra forma você determinaria o número de argumentos em tempo constante?
avakar
4
Não pense que as tags linux / unix são apropriadas aqui, pois esse comportamento deve ser verdadeiro para todos os compiladores em todos os sistemas operacionais.
Darrel Hoffman

Respostas:

106

Sim, argv[argc]==NULLé garantido. Veja C11 5.1.2.2.1 Inicialização do programa (ênfase minha)

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

O valor de argc deve ser não negativo. argv [argc] deve ser um ponteiro nulo.

Fornecer, argcportanto, não é vital, mas ainda é útil. Entre outras coisas, permite verificar rapidamente se o número correto de argumentos foi passado.

Edit: A questão foi alterada para incluir C ++. n3337 draft 3.6.1 Função principal diz

2 ... argc será o número de argumentos transmitidos ao programa a partir do ambiente em que o programa é executado. .... O valor de argc deve ser não negativo. O valor de argv [argc] deve ser 0 .

simonc
fonte
36
E argcpoderia ser muito grande, porque o shell está fazendo de expansão (por isso, ls *o *é expandida pelo shell antes execvede /bin/lsexecutável). No meu sistema, posso ter argcvárias centenas de milhares.
Basile Starynkevitch
Isso é muito legal, isso nunca me ocorreu pois sempre considerei argcsuficiente, mas posso definitivamente pensar em situações em que essa garantia seria relevante e até exigida. +1
Thomas
6
@BasileStarynkevitch O tempo de simplesmente percorrer uma matriz de valores de ponteiro uma vez a mais até NULL, para obter a contagem, é minúsculo em comparação com o tempo já gasto na geração da matriz de ponteiros e ainda mais irrelevante em comparação ao uso real de cada valor de argumento no programa. E se apenas verificar se a contagem de argumentos é maior do que N, não é necessário percorrer o array inteiro. Ainda assim, concordo plenamente, isso argcfoi e é uma coisa boa.
hyde
43

Sim, argv[argc]é garantido que seja um ponteiro nulo. argcé usado por conveniência.

Citando a explicação oficial do Racional C99, observe as palavras verificação redundante :

Fundamentação do padrão internacional - linguagens de programação - C §5.1.2.2.1 Inicialização do programa

A especificação de argce argvcomo argumentos para mainreconhecer a 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.

Yu Hao
fonte
19

É por razões históricas e compatibilidade com o código antigo. Originalmente, não havia garantia de que existiria um ponteiro nulo como o último elemento da matriz argv. Mas argc sempre existiu.

zentrunix
fonte
... O que é uma pena, de certa forma. Se tivéssemos int main(char *argv[], int argc, ...), alguns programas poderiam simplesmente omitir o argcporque não precisam dele. O oposto (necessário, argcmas não argv) provavelmente nunca é útil em um programa real.
hyde,
@hyde: veja o comentário acima de Basile Starynkevitch
zentrunix
6

Nós "precisamos" disso, porque é exigido por vários padrões.

Somos livres para ignorar o valor completamente, mas como é o primeiro parâmetro de main, devemos tê-lo na lista de parâmetros. Em C ++ (e provavelmente em dialetos C não padrão), você pode simplesmente omitir o nome do parâmetro, como este snippet C ++ (fácil de converter para C):

#include <stdio.h> // C-compatible include, guarantees puts in global namespace

// program will print contents of argv, one item per line, starting from argv[0]

int main(int /*argc*/, char *argv[]) { // uncomment argc for C

    //(void)argc; // uncomment statement for C

    for (int i=0; argv[i]; ++i) {
        puts(argv[i]);
    }

    return 0;
}

No C padrão, com configurações de avisos comuns, o parâmetro não utilizado gera um aviso, que pode ser corrigido por uma instrução como a (void)argc;que faz com que o nome seja usado sem gerar nenhum código.

argcé bom ter, porque de outra forma muitos programas precisariam percorrer os parâmetros para obter a contagem. Além disso, em muitas linguagens de programação com arrays que têm comprimento, não existe nenhum argcparâmetro, há apenas um array com os itens.

hyde
fonte
A pergunta é sobre C e C ++. Em C, o nome do parâmetro é obrigatório.