Différence entre char * argv et char ** argv pour le deuxième argument de main ()

CODE 1

#include int main(int argc, char *argv[]) { int j; printf("%d", argv[1][0]); return 0; } 

CODE 2

 #include int main(int argc, char **argv) { int j; printf("%d", argv[1][0]); return 0; } 

CODE 1 et CODE 2 donnent tous deux le même résultat. mais l’ argument 2 de la fonction principale dans CODE 1 et CODE 2 est différent. Un tableau de pointeurs est créé au-dessus de la section de données au moment de la compilation. argv est un tableau de pointeurs. Ensuite, nous devrions déclarer l’argument dans la fonction principale en tant que pointeur à pointeur, c’est-à-dire ** argv. Comment est-il correct de déclarer comme dans CODE 1?

    Fondamentalement, char * argv [] signifie un tableau de pointeurs de caractères, alors que char ** argv signifie un pointeur sur un pointeur de caractères.

    Dans n’importe quel tableau, le nom du tableau est un pointeur sur le premier élément du tableau, c’est-à-dire qu’il contient l’adresse du premier élément.

    Donc, dans le code donné ci-dessous, dans char array x, x est un pointeur sur le premier élément, ‘1’, qui est un caractère. C’est donc un pointeur sur un personnage.

    Et dans array arr, arr est le premier élément du pointeur, x, qui est lui-même un pointeur sur un caractère. Donc, c’est un pointeur vers un autre pointeur.

    Par conséquent, x est char * et arr est char **.

    Lorsque vous recevez quelque chose dans une fonction, la règle de base est que vous devez indiquer le type de ce que vous recevez. Donc, soit vous dites simplement que vous voulez recevoir un caractère **, soit vous pouvez aussi dire car * arr [].

    Dans le premier cas, nous n’avons pas besoin de penser à quelque chose de complexe. Nous soaps simplement que nous recevons un tableau de caractères *. Ne soaps-nous pas cela? Donc, nous le recevons et l’utilisons.

    Dans le second cas, il est simple, comme je l’ai expliqué ci-dessus, que arr est un caractère **, vous pouvez le mettre tel quel et le recevoir en toute sécurité. Maintenant que le système connaît le type de contenu que nous avons reçu, nous pouvons accéder aux éléments suivants en utilisant simplement une annotation de tableau. C’est comme si nous avions reçu l’adresse de départ du tableau, nous pouvons sûrement passer aux éléments suivants, et comme nous soaps qu’il s’agit d’un type, nous soaps ce qu’il contient et comment nous pouvons l’utiliser. Nous soaps qu’il contient un pointeur sur char, nous pouvons donc également y accéder légalement.

     void func1(char* arr[]) { //function body } void func2(char** arr) { //function body } int main() { //x, y and z are pointer to char char x[3]={'1', '2', '3'}; char y[3]={'4', '5', '6'}; char z[3]={'7', '8', '9'}; //arr is pointer to char pointer char* arr[3]={x, y, z}; func1(arr); func2(arr); } 

    Il est fondamental de c que char** x et char* x[] sont deux façons d’exprimer la même chose . Les deux déclarent que le paramètre reçoit un pointeur sur un tableau de pointeurs. Rappelez-vous que vous pouvez toujours écrire:

      char *parray[100]; char **x; x = &parray[0]; 

    puis utilisez x à l’identique.

    Ils sont exactement les mêmes. Le § 5.1.2.2.2 de la norme C11 stipule:

    La fonction appelée au démarrage du programme s’appelle main . L’implémentation ne déclare aucun prototype pour cette fonction. Il doit être défini avec un type de retour de int et sans paramètre:

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

    ou avec deux parameters (appelés ici argc et argv , bien que tous les noms puissent être utilisés, car ils sont locaux à la fonction dans laquelle ils sont déclarés):

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

    ou équivalent; 10) ou d’une autre manière définie par la mise en oeuvre.

    10) Ainsi, int peut être remplacé par un nom typedef défini comme int , ou le type argv peut être écrit char ** argv , et ainsi de suite.

    L’intention est clairement que les deux déclarations soient identiques. En plus de cela, la règle est décrite au § 6.7.6.3 / 7:

    La déclaration d’un paramètre en tant que “tableau de type ” doit être ajustée en “” pointeur qualifié vers type “, les qualificateurs de type (le cas échéant) étant ceux spécifiés dans les [ et ] de la dérivation de type de tableau. …

    déclarer un tableau comme celui-ci

     char array[] 

    le rend const, ce qui signifie que vous NE POUVEZ PAS avoir le code suivant

     char array[] = "hello"; array = "hey"; 

    même si la deuxième chaîne est plus petite et devrait correspondre à vous obtenez cette erreur

    erreur: le type de tableau ‘char [6]’ n’est pas assignable

    si vous avez **argv vous pouvez écrire

     main(int argc, char **argv) { char **other_array; /* * do stuff with other_array */ argv = other_array; } 

    si vous avez *argv[] alors

     main(int argc, char *argv[]) { char **other_array; /* * do stuff with other_array */ argv = other_array; } 

    vous donne cet avertissement

    warning: assigner à ‘const char **’ à partir de ‘char **’ ignore les qualificatifs dans les types de pointeurs nesteds

    il est donc techniquement une petite optimisation, comme si vous aviez écrit const