Devo usar char ** argv ou char * argv []?


125

Estou apenas aprendendo C e queria saber qual deles devo usar no meu método principal. Existe alguma diferença? Qual é o mais comum?


2
Eu prefiro 'char ** argv' e, portanto, o vejo com mais frequência, mas ambos estão corretos e eu não mudaria uma declaração simplesmente porque estava escrita 'char * argv []'.
23611 Jonathan Leffler

+1 porque as questões mais profundas da matriz vs. ponteiro são importantes para entender bem.
RBerteig

2
Realmente, muito boa pergunta. Obrigado.
Frank V

5
Leia a seção 6 das perguntas frequentes do comp.lang.c ; é a melhor explicação da relação entre matrizes C e ponteiros que eu já vi. Dois pontos relevantes: 1. char **argvé exatamente equivalente a char *argv[]uma declaração de parâmetro (e apenas como uma declaração de parâmetro). 2. matrizes não são ponteiros.
22412 Keith Thompson

Respostas:


160

Como você está aprendendo C, eu recomendo que você tente realmente entender as diferenças entre matrizes e ponteiros primeiro, em vez das coisas comuns .

Na área de parâmetros e matrizes, existem algumas regras confusas que devem ser claras antes de prosseguir. Primeiro, o que você declara em uma lista de parâmetros é tratado como especial. Existem situações em que as coisas não fazem sentido como parâmetro de função em C. Essas são

  • Funções como parâmetros
  • Matrizes como parâmetros

Matrizes como parâmetros

O segundo talvez não esteja imediatamente claro. Mas fica claro quando você considera que o tamanho de uma dimensão de matriz faz parte do tipo em C (e uma matriz cujo tamanho de dimensão não é fornecido possui um tipo incompleto). Portanto, se você criar uma função que assume um valor por matriz (recebe uma cópia), poderá fazê-lo apenas para um tamanho! Além disso, as matrizes podem se tornar grandes e C tenta ser o mais rápido possível.

Em C, por esses motivos, os valores de matriz não existem. Se você deseja obter o valor de uma matriz, o que obtém é um ponteiro para o primeiro elemento dessa matriz. E aqui, na verdade, já está a solução. Em vez de desenhar um parâmetro de matriz inválido antecipadamente, um compilador C transformará o tipo do respectivo parâmetro em um ponteiro. Lembre-se disso, é muito importante. O parâmetro não será uma matriz, mas será um ponteiro para o respectivo tipo de elemento.

Agora, se você tentar passar uma matriz, o que é passado é um ponteiro para o primeiro elemento das matrizes.

Excursão: Funções como parâmetros

Para conclusão, e porque acho que isso ajudará você a entender melhor o assunto, vamos ver qual é o estado das coisas quando você tenta ter uma função como parâmetro. De fato, primeiro não fará sentido. Como um parâmetro pode ser uma função? Ah, queremos uma variável nesse local, é claro! Então, o que o compilador faz quando isso acontece é, novamente, transformar a função em um ponteiro de função . Tentar passar uma função passará um ponteiro para a respectiva função. Portanto, o seguinte é o mesmo (análogo ao exemplo da matriz):

void f(void g(void));
void f(void (*g)(void));

Observe que parênteses *gsão necessários. Caso contrário, especificaria uma função retornando void*, em vez de um ponteiro para uma função retornando void.

Voltar para matrizes

Agora, eu disse no começo que matrizes podem ter um tipo incompleto - o que acontece se você ainda não fornecer um tamanho. Como já descobrimos que um parâmetro de matriz não existe, mas qualquer parâmetro de matriz é um ponteiro, o tamanho da matriz não importa. Isso significa que o compilador traduzirá todos os itens a seguir e todos são a mesma coisa:

int main(int c, char **argv);
int main(int c, char *argv[]);
int main(int c, char *argv[1]);
int main(int c, char *argv[42]);

Obviamente, não faz muito sentido ser capaz de colocar qualquer tamanho nele, e é apenas jogado fora. Por esse motivo, o C99 criou um novo significado para esses números e permite que outras coisas apareçam entre os colchetes:

// says: argv is a non-null pointer pointing to at least 5 char*'s
// allows CPU to pre-load some memory. 
int main(int c, char *argv[static 5]);

// says: argv is a constant pointer pointing to a char*
int main(int c, char *argv[const]);

// says the same as the previous one
int main(int c, char ** const argv);

As duas últimas linhas dizem que você não poderá alterar "argv" dentro da função - ela se tornou um ponteiro const. Apenas alguns compiladores C suportam esses recursos do C99. Mas esses recursos deixam claro que a "matriz" não é realmente uma. É um ponteiro.

Uma palavra de alerta

Observe que tudo o que eu disse acima é verdadeiro somente quando você tem uma matriz como parâmetro de uma função. Se você trabalha com matrizes locais, uma matriz não será um ponteiro. Ele se comportará como um ponteiro, porque, como explicado anteriormente, uma matriz será convertida em um ponteiro quando seu valor for lido. Mas não deve ser confundido com ponteiros.

Um exemplo clássico é o seguinte:

char c[10]; 
char **c = &c; // does not work.

typedef char array[10];
array *pc = &c; // *does* work.

// same without typedef. Parens needed, because [...] has 
// higher precedence than '*'. Analogous to the function example above.
char (*array)[10] = &c;

2
Não tinha idéia este existe: argv * [estático 1]
superlukas

12

Você também pode usar. Eles são completamente equivalentes. Veja os comentários de litb e sua resposta .

Realmente depende de como você deseja usá-lo (e você pode usá-lo em qualquer caso):

// echo-with-pointer-arithmetic.c
#include <stdio.h>
int main(int argc, char **argv)
{
  while (--argc > 0)
  {
    printf("%s ", *++argv);
  }
  printf("\n");
  return 0;
}

// echo-without-pointer-arithmetic.c
#include <stdio.h>
int main(int argc, char *argv[])
{
  int i;
  for (i=1; i<argc; i++)
  {
    printf("%s ", argv[i]);
  }
  printf("\n");
  return 0;
}

Quanto ao que é mais comum - não importa. Qualquer programador C experiente que leia seu código verá os dois intercambiáveis ​​(sob as condições corretas). Assim como um falante de inglês experiente lê "eles são" e "eles são" com a mesma facilidade.

Mais importante é que você aprende a lê-los e reconhece como eles são semelhantes. Você estará lendo mais código do que escreve e precisará estar igualmente confortável com ambos.


7
char * argv [] é 100% equivalente a char ** argv quando usado como um tipo de parâmetro de uma função. nenhuma "const" envolvida, também não implicitamente. Ambos são ponteiros para ponteiros para caracteres. É diferente em relação ao que você declara. Mas o compilador ajusta o tipo do parâmetro para ser um ponteiro para um ponteiro, mesmo que você tenha dito que é uma matriz. Assim, os seguintes são todos iguais: void f (char * p [100]); void f (char * p []); n vazio f (caractere ** p);
Johannes Schaub - litb 23/04/09

4
No C89 (que a maioria das pessoas usa), também não há como tirar proveito do fato de que você o declarou como uma matriz (portanto, semanticamente, não importa se você declarou um ponteiro ou uma matriz lá - ambas serão consideradas como ponteiro). A partir do C99, você pode se beneficiar da declaração como uma matriz. O seguinte diz: "p é sempre não nulo e aponta para uma região com pelo menos 100 bytes": void f (char p [estático 100]); Observe que, em termos de tipo, p ainda é um ponteiro.
Johannes Schaub - litb 23/04/09

5
(em particular, & p fornecerá um caractere **, mas não um caractere ( ) [100], o que seria o caso se p * fosse uma matriz). Estou surpreso que ninguém tenha mencionado uma resposta ainda. Eu considero muito importante entender.
Johannes Schaub - litb 23/04/09

Pessoalmente, prefiro char**porque me lembra que não deve ser tratado como uma matriz real, como fazer sizeof[arr] / sizeof[*arr].
precisa saber é o seguinte

9

Não faz diferença, mas eu uso char *argv[]porque mostra que é uma matriz de tamanho fixo de cadeias de comprimento variável (que geralmente são char *).



4

Realmente não faz diferença, mas o último é mais legível. O que você recebe é uma série de indicadores de caracteres, como diz a segunda versão. No entanto, ele pode ser convertido implicitamente em um ponteiro de caractere duplo, como na primeira versão.


2

você deve declará-lo como char *argv[], devido a todas as maneiras equivalentes de declará-lo, que se aproximam mais de seu significado intuitivo: uma matriz de strings.


1

char ** → ponteiro para ponteiro de caractere e char * argv [] significa matriz de ponteiros de caractere. Como podemos usar o ponteiro em vez de uma matriz, ambos podem ser usados.


0

Não vejo mérito especial de usar uma das abordagens em vez da outra - use a convenção mais alinhada com o restante do seu código.


-2

Se você precisar de um número variável ou dinâmico de strings, char ** poderá ser mais fácil de trabalhar. Se seu número de strings for fixo, char * var [] seria o preferido.


-2

Sei que isso está desatualizado, mas se você está apenas aprendendo a linguagem de programação C e não está fazendo nada de importante com ela, não use as opções de linha de comando.

Se você não estiver usando argumentos de linha de comando, também não use. Apenas declare a função principal como int main() se você

  • Deseja que o usuário do seu programa possa arrastar um arquivo para ele, para que você possa alterar o resultado do programa com ele ou
  • Quer lidar com opções de linha de comando ( -help, /?, ou qualquer outra coisa que vai atrás program nameno terminal ou prompt de comando)

use o que fizer mais sentido para você. Caso contrário, basta usar int main() Afinal, se você quiser adicionar opções de linha de comando, poderá editá-las facilmente mais tarde.


Isso não responde à pergunta.
Lundin 24/05
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.