358 votes

Pointeurs en c : quand utiliser l’esperluette et l’astérisque ?

Je commence tout juste avec les pointeurs, et je suis un peu confus. Je connais & signifie que l'adresse d'une variable et qu' * peut être utilisé devant un pointeur de variable pour obtenir la valeur de l'objet pointé par le pointeur. Mais les choses sont différentes lorsque vous travaillez avec des tableaux, chaînes de caractères, ou lorsque vous avez les appels de fonctions avec le pointeur de la copie d'une variable. Il est difficile de voir un modèle de logique dans tout cela.

Quand dois-je utiliser & et *?

682voto

Dan Olson Points 11210

Vous avez des pointeurs et des valeurs:

int* p; // pointer to an integer
int i; // integer value

Vous tournez un pointeur sur une valeur, *:

int i2 = *p; // integer value

Vous mettez une valeur à un pointeur avec &:

int* p2 = &i; // pointer to an integer

Edit: Dans le cas des tableaux, ils sont traités à peu près comme des pointeurs. Si vous pensez à eux comme des pointeurs, vous serez en utilisant * pour obtenir les valeurs à l'intérieur d'eux comme expliqué ci-dessus, mais il y a aussi un autre, beaucoup plus souvent à l'aide de l' [] opérateur:

int a[2];  // array of integers
int i = *a; // the value of the first element of a
int i2 = a[0]; // another way to get the first element

Pour obtenir le deuxième élément:

int a[2]; // array
int i = *(a + 1); // the value of the second element
int i2 = a[1]; // the value of the second element

Si l' [] d'indexation de l'opérateur est une forme particulière de l' * de l'opérateur, et il fonctionne comme ceci:

a[i] == *(a + i);  // these two statements are the same thing

36voto

John Bode Points 33046

Il s'agit d'un modèle lorsque vous traitez avec des tableaux et des fonctions; c'est juste un peu difficile à voir au premier abord.

Lorsque vous traitez avec des tableaux, il est utile de rappeler ce qui suit: lorsqu'un tableau de l'expression apparaît dans la plupart des contextes, le type de l'expression est implicitement converti à partir "N-élément de tableau de T" à "pointeur de T", et sa valeur est définie pour pointer vers le premier élément dans le tableau. Les exceptions à cette règle lorsque la rangée expression apparaît comme un opérande de l' & ou sizeof opérateurs, ou quand c'est un littéral de chaîne utilisé comme un initialiseur dans une déclaration.

Ainsi, lorsque vous appelez une fonction avec un tableau d'expression comme un argument, la fonction reçoit un pointeur, pas un tableau:

int arr[10];
...
foo(arr);
...

void foo(int *arr) { ... }

C'est pourquoi vous n'avez pas utiliser l' & de l'opérateur pour les arguments correspondant à "%s" scanf():

char str[STRING_LENGTH];
...
scanf("%s", str);

En raison de la conversion implicite, scanf() reçoit un char * valeur qui pointe vers le début de l' str tableau. Cela est vrai pour toute fonction appelée avec un tableau d'expression comme un argument (à peu près tout de l' str* fonctions, *scanf et *printf des fonctions, etc.).

Dans la pratique, vous n'aurez probablement jamais appeler une fonction avec un tableau de l'expression à l'aide de l' & de l'opérateur, comme dans:

int arr[N];
...
foo(&arr);

void foo(int (*p)[N]) {...}

Ce code n'est pas très commun; vous devez connaître la taille du tableau dans la déclaration de la fonction, et la fonction ne fonctionne qu'avec des pointeurs vers des tableaux de tailles (un pointeur vers un tableau de 10 éléments de T est un type différent d'un pointeur sur un 11-élément de tableau de T).

Lorsqu'un tableau de l'expression apparaît comme un opérande de l' & de l'opérateur, le type de l'expression qui en résulte est "pointeur de N-élément de tableau de T", ou T (*)[N], ce qui est différent à partir d'un tableau de pointeurs (T *[N]) et un pointeur vers le type de base (T *).

Lorsque vous traitez avec les fonctions et les pointeurs, la règle à retenir est la suivante: si vous souhaitez modifier la valeur d'un argument et qui reflètent le code appelant, vous devez passer un pointeur vers la chose que vous voulez modifier. Encore une fois, les tableaux de jeter un peu de bâtons dans les travaux, mais nous allons traiter le cas de la première.

Rappelez-vous que C passe tous les arguments d'une fonction par valeur, le paramètre formel reçoit une copie de la valeur dans le paramètre réel, et toute modification du paramètre formel ne sont pas reflétées dans le paramètre réel. L'exemple le plus connu est une fonction d'échange:

void swap(int x, int y) { int tmp = x; x = y; y = x; }
...
int a = 1, b = 2;
printf("before swap: a = %d, b = %d\n", a, b);
swap(a, b);
printf("after swap: a = %d, b = %d\n", a, b);

Vous obtenez le résultat suivant:

avant de swap: a = 1, b = 2
après swap: a = 1, b = 2

Les paramètres formels x et y sont des objets distincts à partir d' a et b, de sorte que les modifications à l' x et y ne sont pas reflétés dans a et b. Puisque nous voulons modifier les valeurs de a et b, il nous faut passer des pointeurs de la fonction d'échange:

void swap(int *x, int *y) {int tmp = *x; *x = *y; *y = tmp; }
...
int a = 1, b = 2;
printf("before swap: a = %d, b = %d\n", a, b);
swap(&a, &b);
printf("after swap: a = %d, b = %d\n", a, b);

Maintenant, votre sortie sera

avant de swap: a = 1, b = 2
après swap: a = 2, b = 1

Notez que, dans la fonction de permutation, on ne change pas les valeurs de x et y, mais les valeurs de ce que l' x et y point de. L'écriture d' *x est différente de l'écriture à l' x; nous ne sommes pas la mise à jour de la valeur en x lui-même, nous obtenons un emplacement de x et mise à jour de la valeur de cet emplacement.

Cela est d'autant plus vrai si l'on souhaite modifier une valeur de pointeur; si nous écrire

int myFopen(FILE *stream) {stream = fopen("myfile.dat", "r"); }
...
FILE *in;
myFopen(in);

ensuite, nous allons modifier la valeur du paramètre d'entrée stream, pas de quoi stream de points à, la modification stream n'a pas d'effet sur la valeur de in; dans l'ordre pour que cela fonctionne, il nous faut passer un pointeur vers le pointeur:

int myFopen(FILE **stream) {*stream = fopen("myFile.dat", "r"); }
...
FILE *in;
myFopen(&in);

Encore une fois, les tableaux de jeter un peu de bâtons dans les œuvres. Lorsque vous passez un tableau de l'expression d'une fonction, ce que la fonction reçoit un pointeur. En raison de la façon dont des indices de tableaux est défini, vous pouvez utiliser un indice de l'opérateur sur un pointeur de la même façon, vous pouvez l'utiliser sur un tableau:

int arr[N];
init(arr, N);
...
void init(int *arr, int N) {size_t i; for (i = 0; i < N; i++) arr[i] = i*i;}

Notez que les objets d'ensemble ne peut pas être affectée; c'est à dire, vous ne pouvez pas faire quelque chose comme

int a[10], b[10];
...
a = b;

donc, vous voulez être prudent lorsque vous traitez avec des pointeurs vers des tableaux; quelque chose comme

void (int (*foo)[N])
{
  ...
  *foo = ...;
}

ne fonctionne pas.

14voto

smerlin Points 3276

Oui, cela peut être assez compliqué, car l' * est utilisé à de nombreuses fins différentes en C/C++.

Lors de l' * apparaît en face d'un déjà déclaré variable/fonction, cela signifie que: a) * donne accès à la valeur de la variable (si le type de cette variable est de type pointeur, ou à une surcharge de l' * opérateur).
b) * a le sens de l'opérateur produit, dans ce cas, il y a une autre variable à gauche de l' *

Lors de l' * apparaît dans une variable ou d'une déclaration de fonction, il signifie que la variable est un pointeur:

int int_value = 1;
int * int_ptr; //can point to another int variable
int   int_array1[10]; //can contain up to 10 int values, basically int_array1 is an pointer aswell which points to the first int of the array
//int   int_array2[]; //illegal, without initializer list..
int int_array3[] = {1,2,3,4,5};  // these two
int int_array4[5] = {1,2,3,4,5}; // are indentical

void func_takes_int_ptr1(int *int_ptr){} // these two are indentical
void func_takes int_ptr2(int int_ptr[]){}// and legal

lors de l' & apparaît dans une variable ou une déclaration de fonction, cela signifie généralement que cette variable est une référence à une variable de ce type.

lors de l' & apparaît en face d'une variable déclarée, il renvoie l'adresse de cette variable

En outre, vous devriez savoir, que lors du passage d'un tableau à une fonction, vous aurez toujours de passer le tableau de la taille de ce tableau aswell, sauf lorsque le tableau est quelque chose comme un 0 à terminaison cstring (char tableau).

5voto

Jay Conrod Points 12375

Lorsque vous déclarez un pointeur de variable ou paramètre de la fonction, utilisez l' *:

int *x = NULL;
int *y = malloc(sizeof(int)), *z = NULL;
int* f(int *x) {
    ...
}

NB: chaque variable déclarée besoin de sa propre *.

Lorsque vous voulez prendre l'adresse d'une valeur, d'utilisation et d'. Si vous voulez lire ou d'écrire la valeur d'un pointeur, utilisation *.

int a;
int *b;
b = f(&a);
a = *b;

a = *f(&a);

Les tableaux sont généralement traités comme des pointeurs. Lorsque vous déclarez un tableau en paramètre à une fonction, vous pouvez tout aussi facilement déclarer qu'il est un pointeur (c'est à dire la même chose). Lorsque vous passer un tableau à une fonction, vous êtes en fait en passant un pointeur vers le premier élément.

Pointeurs de fonction sont les seules choses qui ne sont pas tout à fait suivi les règles. Vous pouvez prendre l'adresse d'une fonction sans l'aide de &, et vous pouvez appeler un pointeur de fonction sans utiliser *.

5voto

Prasoon Saurav Points 47488

Je pense que vous êtes un peu confus. Vous devriez lire un bon tutoriel/livre sur les pointeurs.

Ce tutoriel est très bon pour les débutants(explique clairement ce qu' & et * ). Et oui n'oubliez pas de lire le livre les Pointeurs en C par Kenneth Lattes.

La différence entre & et * est très clair.

Exemple:

#include <stdio.h>

int main(){
  int x, *p;

  p = &x;         /* initialise pointer(take the address of x) */
  *p = 0;         /* set x to zero */
  printf("x is %d\n", x);
  printf("*p is %d\n", *p);

  *p += 1;        /* increment what p points to i.e x */
  printf("x is %d\n", x);

  (*p)++;         /* increment what p points to i.e x */
  printf("x is %d\n", x);

  return 0;
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X