web-dev-qa-db-fra.com

Comment comprendre le pointeur étoile * en C?

Je me bats avec le signe de pointeur *, je trouve cela très déroutant dans son utilisation dans les déclarations et les expressions.

Par exemple: 

int *i; // i is a pointer to an int

Mais quelle est la logique derrière la syntaxe? Que veut dire * juste avant le je? Prenons l'exemple suivant. S'il te plaît, corrige-moi là où je me trompe:

char **s;
char *(*s); // added parentheses to highlight precedence

Et c'est là que je perds le fil. Le * s entre parenthèses signifie: s est un pointeur? Mais un pointeur vers quoi? Et que signifie * hors des parenthèses: un pointeur sur ce qui pointe?

Donc, la signification de ceci est: Le pointeur indiquant ce que s indique est un pointeur sur un caractère?

Je suis à perte. Le signe * est-il interprété différemment dans les déclarations et les expressions? Si oui, comment est-il interprété différemment? Où vais-je mal?

31
Jeffrey

La règle de déclaration en c est la suivante: vous le déclarez tel que vous l’utilisez.

char *p signifie que vous avez besoin de *p pour obtenir le caractère,

char **p signifie que vous avez besoin de **p pour obtenir le caractère.

21
burningice

Prends ça comme ça:

int *i signifie que la valeur sur laquelle je pointe est un entier.

char **p signifie que p est un pointeur qui est lui-même un pointeur sur un caractère.enter image description here

62
pradeepchhetri
int i; //i is an int.
int *i; //i is a pointer to an int
int **i;//i is a pointer to a pointer to an int.

Le signe * est-il interprété différemment dans les déclarations et les expressions?

Oui. Ils sont complètement différents. dans une déclaration * est utilisé pour déclarer des pointeurs. Dans une expression, unary * est utilisé pour déréférencer un pointeur (ou en tant qu'opérateur de multiplication binaire)

Quelques exemples:

int i = 10; //i is an int, it has allocated storage to store an int.
int *k; // k is an uninitialized pointer to an int. 
        //It does not store an int, but a pointer to one.
k = &i; // make k point to i. We take the address of i and store it in k
int j = *k; //here we dereference the k pointer to get at the int value it points
            //to. As it points to i, *k will get the value 10 and store it in j
48
nos

Les déclarations en C sont centrées sur l'expression, ce qui signifie que la forme de la déclaration doit correspondre à la forme de l'expression dans le code exécutable.

Par exemple, supposons que nous ayons un pointeur sur un entier nommé p. Nous voulons accéder à la valeur entière désignée par p, donc nous dereference le pointeur, comme suit:

x = *p; 

Le type de expression*p est int; par conséquent, la déclaration de p prend la forme

int *p;

Dans cette déclaration, int est le spécificateur de type type et *p est le déclarateur. Le déclarant introduit le nom de l'objet en cours de déclaration (p), ainsi que des informations de type supplémentaires non fournies par le spécificateur de type. Dans ce cas, l'information de type supplémentaire est que p est un type de pointeur. La déclaration peut être lue comme suit: "p est de type pointeur sur int" ou "p est un pointeur sur type int". Je préfère utiliser le deuxième formulaire, d'autres préfèrent le premier. 

C'est un accident de la syntaxe C et C++ que vous pouvez écrire cette déclaration sous la forme int *p; ou int* p;. Dans les deux cas, il est analysé comme suit: int (*p); - en d'autres termes, le * est toujours associé au nom de la variable et non au spécificateur de type. 

Supposons maintenant que nous avons un tableau de pointeurs sur int et que nous voulons accéder à la valeur pointée par le ième élément du tableau. Nous inscrivons dans le tableau et déréférencons le résultat, comme suit:

x = *ap[i]; // parsed as *(ap[i]), since subscript has higher precedence
            // than dereference.

De nouveau, le type de expression*ap[i] est int, la déclaration de ap est donc

int *ap[N];

où le déclarant *ap[N] signifie que ap est un tableau de pointeurs sur int

Et juste pour faire passer le message, supposons maintenant que nous avons un pointeur sur un pointeur sur int et que nous voulons accéder à cette valeur. De nouveau, nous déférenceons le pointeur, puis nous déréférencons ce résultat pour obtenir la valeur entière:

x = **pp; // *pp deferences pp, then **pp dereferences the result of *pp

Puisque le type de l'expression **pp est int, la déclaration est

int **pp;

Le déclarant **pp indique que pp est un pointeur sur un autre pointeur sur int

La double indirection apparaît souvent, généralement lorsque vous souhaitez modifier une valeur de pointeur que vous transmettez à une fonction, telle que:

void openAndInit(FILE **p)
{
  *p = fopen("AFile.txt", "r");
  // do other stuff
}

int main(void)
{
  FILE *f = NULL;
  ...
  openAndInit(&f);
  ...
}

Dans ce cas, nous voulons que la fonction mette à jour la valeur de f; pour ce faire, nous devons passer un pointeur sur f. Puisque f est déjà un type de pointeur (FILE *), cela signifie que nous passons un pointeur sur un FILE *, d'où la déclaration de p en tant que FILE **p. N'oubliez pas que expression*p dans openAndInit fait référence au même objet que l'expression f dans main

Dans les déclarations et les expressions, [] et () ont une priorité supérieure à celle de * unaire. Par exemple, *ap[i] est interprété comme *(ap[i]); l'expression ap[i] est un type de pointeur et le * déréférence ce pointeur. Ainsi, ap est un tableau de pointeurs. Si vous voulez déclarer un pointeur dans un tableau, vous devez explicitement regrouper le * avec le nom du tableau, comme suit:

int (*pa)[N]; // pa is a pointer to an N-element array of int

et lorsque vous souhaitez accéder à une valeur du tableau, vous devez respecter pa avant d'appliquer l'indice:

x = (*pa)[i];

De même avec des fonctions:

int *f(); // f is a function that returns a pointer to int
...
x = *f(); // we must dereference the result of f() to get the int value

int (*f)(); // f is a pointer to a function that returns an int
...
x = (*f)(); // we must dereference f and execute the result to get the int value
10
John Bode

Ma méthode préférée pour analyser les déclarateurs compliqués est la règle clockwise-spiral .

Fondamentalement, vous commencez à partir de l'identifiant et suivez une spirale dans le sens des aiguilles d'une montre. Voir le lien pour savoir exactement comment il est utilisé.

L'article ne mentionne pas deux choses:

1- Vous devez séparer le spécificateur de type (int, char, etc.) du déclarant, analyser le déclarateur, puis ajouter le spécificateur de type.

2- Si vous rencontrez des crochets qui désignent un tableau, veillez à lire également les crochets suivants (le cas échéant).

9
tiftik

int * i signifie que i est un pointeur sur int (lecture arrière, * en tant que pointeur) .char **p et char *(*p) signifient tous deux un pointeur sur un pointeur sur char. 

Voici quelques autres exemples

int* a[3] // a est un tableau de 3 pointeurs sur int

int (*a)[3] // a est un pointeur sur un tableau de 3 ints

5
Armen Tsirunyan

Vous avez la réponse dans vos questions.

En effet, une étoile double est utilisée pour indiquer un pointeur à l'autre.

2
Shamim Hafiz

La déclaration * in signifie que la variable est un pointeur sur une autre variable/constante. ce qui signifie qu'il peut contenir l'adresse de la variable du type. Par exemple: char *c; signifie que c peut contenir l'adresse à un caractère, alors que int *b signifie que b peut contenir l'adresse d'un int, le type de la référence est important car, dans l'arithmétique des pointeurs, pointer + 1 est en réalité pointer + (1 * sizeof(*pointer)).

Le * dans l'expression signifie "la valeur stockée dans l'adresse" donc si c est un pointeur sur un caractère, alors *c est le caractère spécifique.

char *(*s); signifie que s est un pointeur sur un pointeur sur char. s ne contient donc pas l'adresse d'un caractère, mais l'adresse de la variable qui contient l'adresse d'un caractère.

1
MByD

Déclarer &a signifie qu'il pointe sur *i. Après tout, il s’agit d’un pointeur sur *int. Un entier est à pointer *i. Mais si consider j = *k est le pointeur sur le pointeur this, cela signifie que &k sera la valeur de k et k aura un pointeur sur *int.

0
ShoveItUpYour____

voici un peu d'information

         variable    pointer
declaring     &a           p

reading/      a            *p

processing
0
Akshatha