En C, que signifie une déclaration de variable avec deux astérisques (**)?

Je travaille avec C et je suis un peu rouillé. Je suis conscient que * a trois utilisations:

  1. Déclarer un pointeur.
  2. Déréférencement d’un pointeur.
  3. Multiplication

Cependant, qu’est-ce que cela signifie quand il y a deux astérisques ( ** ) avant une déclaration de variable:

 char **aPointer = ... 

Merci,

Scott

Il déclare un pointeur sur un pointeur de caractère .

L’utilisation d’un tel pointeur serait de faire des choses comme:

 void setCharPointerToX(char ** character) { *character = "x"; //using the dereference operator (*) to get the value that character points to (in this case a char pointer } char *y; setCharPointerToX(&y); //using the address-of (&) operator here printf("%s", y); //x 

Voici un autre exemple:

 char *original = "awesomeness"; char **pointer_to_original = &original; (*pointer_to_original) = "is awesome"; printf("%s", original); //is awesome 

Utilisation de ** avec des tableaux:

 char** array = malloc(sizeof(*array) * 2); //2 elements (*array) = "Hey"; //equivalent to array[0] *(array + 1) = "There"; //array[1] printf("%s", array[1]); //outputs There 

L’opérateur [] sur les tableaux effectue essentiellement une arithmétique de pointeur sur le pointeur avant. La méthode d’évaluation du array[1] est la suivante:

 array[1] == *(array + 1); 

C’est l’une des raisons pour lesquelles les indices de tableau commencent à 0 , pour les raisons suivantes:

 array[0] == *(array + 0) == *(array); 

C et C ++ permettent l’utilisation de pointeurs pointant vers des pointeurs (disons cinq fois plus vite). Regardez le code suivant:

 char a; char *b; char **c; a = 'Z'; b = &a; // read as "address of a" c = &b; // read as "address of b" 

La variable a contient un caractère. La variable b pointe vers un emplacement en mémoire qui contient un caractère. La variable c pointe vers un emplacement en mémoire qui contient un pointeur qui pointe vers un emplacement en mémoire qui contient un caractère.

Supposons que la variable a stocke ses données à l’adresse 1000 (BEWARE: les exemples d’emplacements mémoire sont totalement constitués). Supposons que la variable b stocke ses données à l’adresse 2000 et que la variable c stocke ses données à l’adresse 3000. Compte tenu de tout cela, nous avons la structure de mémoire suivante:

 MEMORY LOCATION 1000 (variable a): 'Z' MEMORY LOCATION 2000 (variable b): 1000 <--- points to memory location 1000 MEMORY LOCATION 3000 (variable c): 2000 <--- points to memory location 2000 

Cela signifie qu’un aPointer pointe sur un pointeur de caractère.

Alors

 aPointer: pointer to char pointer *aPointer :pointer to char **aPointer: char 

Un exemple de son utilisation est la création d’un tableau dynamic de chaînes c

char **aPointer = (char**) malloc(num_ssortingngs);

aPointer vous donne un caractère , qui peut être utilisé pour représenter une chaîne terminée par zéro.

 *aPointer = (char*)malloc( ssortingng_len + 1); //aPointer[0] *(aPointer + 1) = (char*)malloc( ssortingng_len + 1); //aPointer[1] 

Il déclare aPointer tant que pointeur sur un pointeur sur char.

Les déclarations en C sont centrées sur les types d’ expressions ; son nom commun est “déclaration imite l’utilisation”. Par exemple, supposons que nous ayons un pointeur sur int nommé p et que nous souhaitons accéder à la valeur entière vers laquelle il pointe actuellement. Nous déréférencerions le pointeur avec l’opérateur unaire * , comme ceci:

 x = *p; 

Le type de l’ expression *p est int , la déclaration de la variable de pointeur p est donc

 int *p; 

Dans ce cas, aPointer est un pointeur sur un pointeur sur char; si nous voulons obtenir la valeur du caractère qu’il pointe actuellement, nous devrions le déréférencer deux fois:

 c = **aPointer; 

Donc, suivant la logique ci-dessus, la déclaration de la variable de pointeur aPointer est

 char **aPointer; 

parce que le type de l’ expression **aPointer est char .

Pourquoi auriez-vous jamais un pointeur sur un pointeur? Il apparaît dans plusieurs contextes:

  • Vous voulez qu’une fonction modifie une valeur de pointeur; un exemple est la fonction de bibliothèque strtol , dont le prototype (à partir de C99) est
      
    long strtol(const char * ressortingct str, char ** ressortingct ptr, int base); 

    Le deuxième argument est un pointeur sur un pointeur sur char; lorsque vous appelez strtol , vous transmettez l’adresse d’un pointeur à char en tant que deuxième argument, et après l’appel, il pointe vers le premier caractère de la chaîne qui n’a pas été converti.

  • Rappelez-vous que dans la plupart des contextes, une expression de type “tableau N-élément de T” est implicitement convertie en type “pointeur sur T” et sa valeur est l’adresse du premier élément du tableau. Si “T” est “pointeur sur caractère”, une expression de type “tableau de N éléments de pointeur sur caractère” sera convertie en “pointeur à caractère différent”. Par exemple:
    void foo(char **arr) { size_t i = 0; for (i = 0; arr[i] != NULL; i++) printf("%s\n", arr[i]); } void bar(void) { char *ptrs[N] = {"foo", "bar", "bletch", NULL}; foo(ptrs); // ptrs decays from char *[N] to char ** }
    void foo(char **arr) { size_t i = 0; for (i = 0; arr[i] != NULL; i++) printf("%s\n", arr[i]); } void bar(void) { char *ptrs[N] = {"foo", "bar", "bletch", NULL}; foo(ptrs); // ptrs decays from char *[N] to char ** } 

  • Vous souhaitez allouer dynamicment un tableau multidimensionnel:
     #define ROWS ... #define COLS ... ... char **arr = malloc(sizeof *arr * ROWS); if (arr) { size_t i; for (i = 0; i < ROWS; i++) { arr[i] = malloc(sizeof *arr[i] * COLS); if (arr[i]) { size_t j; for (j = 0; j < COLS; j++) { arr[i][j] = ...; } } } } 

Ceci est un pointeur sur un pointeur sur char .