Il y a un modèle en traitant des tableaux et des fonctions; c'est juste un peu difficile à voir au début.
Lorsque vous traitez des tableaux, il est utile de se rappeler ce qui suit: lorsqu'une expression de tableau apparaît dans la plupart des contextes, le type de l'expression est implicitement converti de "tableau à N éléments de T" en "pointeur vers T", et sa valeur est définie pour pointer vers le premier élément du tableau. Les exceptions à cette règle sont lorsque l'expression de tableau apparaît comme un opérande soit le &
ou les sizeof
opérateurs, ou lorsqu'elle est une chaîne littérale utilisé comme un initialiseur dans une déclaration.
Ainsi, lorsque vous appelez une fonction avec une expression de tableau comme argument, la fonction recevra un pointeur, pas un tableau:
int arr[10];
...
foo(arr);
...
void foo(int *arr) { ... }
C'est pourquoi vous n'utilisez pas l' &
opérateur pour les arguments correspondant à "% s" dans scanf()
:
char str[STRING_LENGTH];
...
scanf("%s", str);
En raison de la conversion implicite, scanf()
reçoit une char *
valeur qui pointe vers le début du str
tableau. Cela est vrai pour toute fonction appelée avec une expression de tableau comme argument (à peu près n'importe laquelle des str*
fonctions *scanf
et des *printf
fonctions, etc.).
En pratique, vous n'appellerez probablement jamais une fonction avec une expression de tableau à l'aide de l' &
opérateur, comme dans:
int arr[N];
...
foo(&arr);
void foo(int (*p)[N]) {...}
Un tel code n'est pas très courant; vous devez connaître la taille du tableau dans la déclaration de fonction, et la fonction ne fonctionne qu'avec des pointeurs vers des tableaux de tailles spécifiques (un pointeur vers un tableau de 10 éléments de T est d'un type différent d'un pointeur vers un tableau de 11 éléments de T).
Lorsqu'une expression de tableau apparaît en tant qu'opérande de l' &
opérateur, le type de l'expression résultante est "pointeur vers un tableau à N éléments de T", ou T (*)[N]
, qui est différent d'un tableau de pointeurs ( T *[N]
) et un pointeur vers le type de base ( T *
).
Lorsque vous traitez des fonctions et des pointeurs, la règle à retenir est la suivante: si vous souhaitez modifier la valeur d'un argument et la refléter dans le code appelant, vous devez passer un pointeur sur la chose que vous souhaitez modifier. Encore une fois, les tableaux jettent un peu d'une clé à molette dans les travaux, mais nous traiterons d'abord les cas normaux.
N'oubliez pas que C transmet tous les arguments de fonction par valeur; le paramètre formel reçoit une copie de la valeur du paramètre réel et toute modification du paramètre formel n'est pas reflétée dans le paramètre réel. L'exemple courant est une 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);
Vous obtiendrez la sortie suivante:
avant l'échange: a = 1, b = 2
après l'échange: a = 1, b = 2
Les paramètres formels x
et y
sont des objets distincts de a
et b
, de sorte que les modifications apportées à x
et y
ne sont pas reflétées dans a
et b
. Puisque nous voulons modifier les valeurs de a
et b
, nous devons leur passer des pointeurs vers la fonction swap:
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 l'échange: a = 1, b = 2
après l'échange: a = 2, b = 1
Notez que, dans la fonction de swap, nous ne changeons pas les valeurs de x
et y
, mais les valeurs de quoi x
et y
pointer vers . Écrire dans *x
est différent de l'écrire dans x
; nous ne mettons pas à jour la valeur en x
soi, nous obtenons un emplacement x
et mettons à jour la valeur à cet emplacement.
Cela est également vrai si nous voulons modifier une valeur de pointeur; si on écrit
int myFopen(FILE *stream) {stream = fopen("myfile.dat", "r"); }
...
FILE *in;
myFopen(in);
alors nous modifions la valeur du paramètre d'entrée stream
, pas ce stream
vers quoi pointe , donc changer stream
n'a aucun effet sur la valeur de in
; pour que cela fonctionne, nous devons passer un pointeur au pointeur:
int myFopen(FILE **stream) {*stream = fopen("myFile.dat", "r"); }
...
FILE *in;
myFopen(&in);
Encore une fois, les tableaux jettent un peu d'une clé à molette dans les œuvres. Lorsque vous passez une expression de tableau à une fonction, ce que la fonction reçoit est un pointeur. En raison de la définition de l'indexation de tableau, vous pouvez utiliser un opérateur d'indice sur un pointeur de la même manière que 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 du tableau ne peuvent pas être affectés; c'est-à-dire que vous ne pouvez pas faire quelque chose comme
int a[10], b[10];
...
a = b;
vous devez donc être prudent lorsque vous traitez avec des pointeurs vers des tableaux; quelque chose comme
void (int (*foo)[N])
{
...
*foo = ...;
}
ne fonctionnera pas.