web-dev-qa-db-fra.com

Devrais-je utiliser char ** argv ou char * argv [] en C?

J'apprends juste C et je me demandais laquelle de ces méthodes je devrais utiliser dans ma méthode principale. Y a-t-il une différence?

Edit: Alors, lequel est le plus commun à utiliser?

121
Kredns

Comme vous venez d’apprendre le C, je vous recommande d’essayer de bien comprendre les différences entre les tableaux et les pointeurs d’abord au lieu des des choses communes .

Dans le domaine des paramètres et des tableaux, il y a quelques règles prêtant à confusion qui doivent être claires avant de continuer. Tout d'abord, ce que vous déclarez dans une liste de paramètres est traité de manière spéciale. Il y a de telles situations où les choses n'ont pas de sens en tant que paramètre de fonction dans C. Ce sont:

  • Fonctionne comme paramètre
  • Tableaux en paramètres

Tableaux en paramètres

La seconde n'est peut-être pas immédiatement claire. Mais cela devient clair lorsque vous considérez que la taille d'une dimension de tableau fait partie du type en C (et qu'un tableau dont la taille de dimension n'est pas indiquée possède un type incomplet). Donc, si vous voulez créer une fonction qui prend un tableau par valeur (reçoit une copie), alors il ne pourrait le faire que pour une taille! De plus, les tableaux peuvent devenir volumineux et C essaie d'être aussi rapide que possible.

En C, pour ces raisons, tableau-valeurs n'existent pas. Si vous voulez obtenir la valeur d'un tableau, vous obtenez plutôt un pointeur sur le premier élément de ce tableau. Et c'est en fait déjà la solution. Au lieu de dessiner un paramètre de tableau invalide au début, un compilateur C transformera le type du paramètre correspondant sera un pointeur. Rappelez-vous ceci, c'est très important. Le paramètre ne sera pas un tableau, mais un pointeur sur le type d'élément respectif.

Maintenant, si vous essayez de passer un tableau, ce qui est passé à la place est un pointeur sur le premier élément du tableau.

Excursion: Fonctionne comme paramètre

Pour terminer, et parce que je pense que cela vous aidera à mieux comprendre la question, examinons l’état de la situation lorsque vous essayez d’avoir une fonction comme paramètre. En effet, d’abord, cela n’a aucun sens. Comment un paramètre peut-il être une fonction? Hein, on veut une variable à cet endroit, bien sûr! Donc, ce que le compilateur fait quand cela se produit est, encore une fois, de transformer la fonction en un pointeur de fonction. Essayer de passer une fonction passera un pointeur sur cette fonction respective. Ainsi, les éléments suivants sont identiques (analogue à l'exemple de tableau):

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

Notez que les parenthèses autour de *g est nécessaire. Sinon, cela spécifierait une fonction retournant void*, au lieu d'un pointeur sur une fonction renvoyant void.

Retour aux tableaux

Maintenant, j'ai dit au début que les tableaux peuvent avoir un type incomplet - ce qui arrive si vous ne donnez pas encore une taille. Comme nous avons déjà pensé qu’un paramètre de tableau n’existait pas et que tout paramètre de tableau était un pointeur, la taille du tableau importait peu. Cela signifie que le compilateur traduira tout ce qui suit, et que tous sont la même chose:

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]);

Bien sûr, cela n’a pas beaucoup de sens de pouvoir y mettre une taille quelconque, et c’est tout simplement jeté. Pour cette raison, C99 a donné un nouveau sens à ces nombres et permet de faire apparaître d'autres éléments entre crochets:

// 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);

Les deux dernières lignes indiquent que vous ne pourrez pas changer "argv" dans la fonction - c'est devenu un pointeur const. Seuls quelques compilateurs C supportent ces fonctionnalités C99. Mais ces caractéristiques montrent clairement que le "tableau" n'en est pas un. C'est un pointeur.

Un mot d'avertissement

Notez que tout ce que j'ai dit ci-dessus n'est vrai que lorsque vous avez un tableau en tant que paramètre d'une fonction. Si vous travaillez avec des tableaux locaux, un tableau ne sera pas un pointeur. Il se comportera comme un pointeur, car, comme expliqué précédemment, un tableau sera converti en pointeur lorsque sa valeur sera lue. Mais il ne faut pas confondre avec les pointeurs.

Un exemple classique est le suivant:

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;
157

Vous pouvez utiliser l'un ou l'autre, cela dépend de la façon dont vous voulez l'utiliser. char* argv[] est (principalement) équivalent à char ** argv. Les deux formes sont des pointeurs sur des pointeurs sur char, la seule différence est que avec char *argv[] vous informez le compilateur que la valeur de argv ne changera pas (bien que les valeurs sur lesquelles il pointe puissent encore le faire). En fait, je me trompe, ils sont complètement équivalents. Voir les commentaires de litb et sa réponse .

Cela dépend vraiment de la façon dont vous voulez l’utiliser (et vous pouvez l’utiliser de toute façon):

// 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;
}

Quant à ce qui est plus commun - ce n'est pas grave. Tout programmeur C expérimenté lisant votre code verra les deux comme interchangeables (dans les bonnes conditions). Tout comme un orateur anglais expérimenté lit "ils" et "ils sont" tout aussi facilement.

Le plus important est que vous appreniez à les lire et à reconnaître leur ressemblance. Vous lirez plus de code que vous n'en aurez écrit et vous devrez être également à l'aise avec les deux.

12
rampion

Vous pouvez utiliser l'un des deux formulaires, comme dans C, les tableaux et les pointeurs sont interchangeables dans les listes de paramètres de fonction. Voir http://en.wikipedia.org/wiki/C_ (langage_programmation) # Array-pointer_interchangeability .

9
lothar

Cela ne fait aucune différence, mais j'utilise char *argv[] Car cela montre qu'il s'agit d'un tableau de taille fixe de chaînes de longueur variable (qui sont généralement char *).

8
Zifre

Cela ne fait pas vraiment de différence, mais ce dernier est plus lisible. Ce qui vous est donné est un tableau de pointeurs de caractères, comme le dit la deuxième version. Il peut cependant être implicitement converti en un pointeur à double caractère, comme dans la première version.

4
jalf

vous devriez le déclarer comme char *argv[], en raison de toutes les manières équivalentes de le déclarer, se rapproche le plus de son sens intuitif: un tableau de chaînes.

1
Andras

char ** → pointeur sur caractère pointeur et caractère * argv [] signifie un tableau de pointeurs de caractère. Comme nous pouvons utiliser un pointeur au lieu d'un tableau, les deux peuvent être utilisés.

1
Alphaneo

Je ne vois pas l'intérêt d'utiliser l'une ou l'autre approche au lieu de l'autre. Utilisez la convention qui correspond le mieux au reste de votre code.

0
Christoffer