web-dev-qa-db-fra.com

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

CODE 1

#include<stdio.h>

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

CODE 2

#include<stdio.h>

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

CODE 1 et CODE 2 donnent tous deux la même sortie. mais argument 2 de la fonction principale dans CODE 1 et CODE 2 sont différents. 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 devons déclarer l'argument dans la fonction principale comme un pointeur vers un pointeur vers un caractère, c'est-à-dire ** argv. Comment est-il correct de déclarer comme dans CODE 1?

15
Jhansi Rani

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 vers un tableau de pointeurs. Rappelez-vous que vous pouvez toujours écrire:

 char *parray[100];
 char **x;

 x = &parray[0];

puis utilisez x à l'identique.

13
bmargulies

Fondamentalement, char * argv [] signifie tableau de pointeurs char, tandis que char ** argv signifie pointeur vers un pointeur char.

Dans tout tableau, le nom du tableau est un pointeur vers le premier élément du tableau, c'est-à-dire qu'il contient l'adresse du premier élément.

Ainsi, dans le code ci-dessous, dans le tableau de caractères x, x est un pointeur sur le premier élément, "1", qui est un caractère. C'est donc un pointeur vers un personnage.

Et dans le tableau arr, arr est le premier élément du pointeur, x, qui est lui-même un pointeur sur un caractère. Il s'agit donc d'un pointeur vers un autre pointeur.

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

Lors de la réception de quelque chose dans une fonction, la règle de base est que vous devez indiquer le type de la chose que vous recevez. Donc, soit vous dites simplement que vous souhaitez recevoir un caractère **, soit vous pouvez également dire caractère * arr [].

Dans le premier cas, nous n'avons pas besoin de penser quoi que ce soit de complexe. Nous savons simplement que nous recevons un tableau de char *. Ne le savons-nous pas? Donc, nous le recevons et l’utilisons.

Dans le second cas, c'est simple, comme je l'ai expliqué plus haut 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 choses que nous avons reçues, nous pouvons accéder aux éléments suivants en utilisant simplement l'annotation de tableau. C'est comme, nous avons reçu l'adresse de départ du tableau, nous pouvons sûrement passer aux éléments suivants, et comme nous savons que c'est le type, nous savons ce qu'il contient et comment nous pouvons l'utiliser plus loin. Nous savons 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);
}
12

Ce 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 est nommée 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ètres:

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

ou avec deux paramètres (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;dix) ou d'une autre manière définie par l'implémentation.

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

Il est clair que l'intention est que les deux déclarations soient identiques. De plus, la règle est décrite au §6.7.6.3/7:

Une déclaration d'un paramètre comme '' tableau de type '' doit être ajustée à '' pointeur qualifié vers type '', où les qualificateurs de type (le cas échéant) sont ceux spécifié dans le [ et ] de la dérivation de type tableau. ...

6
user3920237

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 vous convenir, vous obtenez cette erreur

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

si tu as **argv tu peux écrire

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

si tu as *argv[] puis

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

vous donne cet avertissement

avertissement: l'affectation à 'const char **' de 'char **' supprime les qualificatifs dans les types de pointeurs imbriqués

c'est donc techniquement une petite optimisation comme si vous aviez écrit const

2
jde-chil