Pourquoi cette arithmétique de pointeur n’est-elle pas autorisée dans C?

char arr[] = "Hello"; arr = arr + 1; // error occurs 

Autant que je sache, une expression qui a un type de tableau est convertie en type de pointeur qui pointe vers l’élément initial du tableau. Par conséquent, j’attendais arr = arr + 1 (le pointeur sur le premier élément (arr) du tableau devient le pointeur sur le deuxième élément du tableau) pour fonctionner. Pourquoi cela ne fonctionne-t-il pas en C?

arr + 1 est en effet un pointeur sur le deuxième élément du tableau (ie &arr[1] ).

Cependant, cela ne signifie pas que vous pouvez en quelque sorte écrire cette valeur dans arr . Vous ne pouvez pas le faire pour au moins deux raisons.

Tout d’abord, arr est un tableau d’éléments de caractères, pas un pointeur. Il y a une incompatibilité de type évidente ici.

Deuxièmement, étant un tableau, arr une valeur non modifiable . Vous ne pouvez pas changer vous-même, vous ne pouvez que changer ses éléments (cette distinction est un peu difficile à saisir, mais elle existe).

Enfin, si nous ignorons les complexités plus profondes et que nous nous concentrons sur ce qui se passe formellement au plus haut niveau, en raison de la dégradation du type de tableau, votre expression est équivalente à

 (char *) arr = (char *) arr + 1; 

L’affectation est impossible car la partie gauche résulte d’une conversion de type [implicite]. En C, les conversions produisent toujours des valeurs. Vous ne pouvez pas affecter de valeurs.

En d’autres termes, ce n’est pas le “arithmétique du pointeur” qui est interdit ici. L’arithmétique du pointeur est correcte. C’est ce que vous faites avec le résultat de l’arithmétique de ce pointeur qui provoque l’erreur.

Les tableaux sont des lvalues ​​non modifiables. Ils ne peuvent être l’opérande gauche d’un opérateur d’affectation.

C11-§6.3.2.1:

Une lvalue modifiable est une lvalue qui n’a pas de type tableau , n’a pas un type incomplet, […]

§6.5.16 / 2:

Un opérateur d’affectation doit avoir une valeur modifiable comme opérande gauche.

Dans la déclaration

 arr = arr + 1; 

arr est l’opérande gauche de l’opérateur = et est de type tableau. Il ne peut pas être modifié.
Donc, ce n’est pas l’arithmétique du pointeur mais la contrainte par le langage sur l’opérateur d’affectation qui est la raison de l’erreur syntaxique.


Notez que dans certains contextes, les tableaux se désintègrent en un pointeur vers son premier élément, bien que les pointeurs et les tableaux soient différents. Les tableaux ne sont pas des pointeurs . Ce n’est que l’arithmétique du pointeur et l’indexation du tableau qui sont équivalents. Par exemple

 char *ptr = &arr[0] + 1 => &(*(arr + 0)) + 1 => &(*arr) + 1 => arr + 1 // * and & nullify each other 

En effet, les tableaux sont similaires aux pointeurs, sauf qu’ils ne peuvent pas être modifiés. Cependant, vous pouvez modifier un pointeur qui pointe vers un tableau. Pour l’exemple ci-dessus, vous pouvez faire comme ceci:

 char arr[]="Hello"; char *ptr=arr; ptr=ptr+1; 

Initialement, le pointeur ptr pointera sur le premier caractère du tableau, c’est-à-dire 'H' et après avoir modifié la valeur, il pointera vers le second caractère, par exemple 'e' . Vous pouvez également effectuer les opérations suivantes:

 char arr[]="Hello"; char *ptr=arr; ptr=arr+1; 

Les deux produisent le même effet, ce qui montre que arr+1 est bien l’arithmétique du pointeur. Cependant, vous ne pouvez pas modifier la valeur de arr car son type est celui d’un tableau de caractères et non pointeur vers un tableau de caractères.

Autant que je sache, une expression qui a un type de tableau est convertie en type de pointeur qui pointe vers l’élément initial du tableau.

C’est vrai dans la plupart des contextes. Ce n’est pas vrai dans les contextes suivants:

  1. Lorsque vous utilisez l’adresse de l’opérateur ( &arr ). Le type de &arr est char (*)[6] . Ce n’est pas char** .

  2. Lorsque vous utilisez l’opérateur sizeof . sizeof(arr) est 6 . Si cela avait été un pointeur, ce serait la taille d’un pointeur (4 ou 8 dans la plupart des plates-formes communes).

  3. Utilisé comme LHS d’un opérateur d’affectation. Une variable de type tableau n’est pas modifiable.

Par conséquent, j’attendais arr = arr + 1 (le pointeur sur le premier élément (arr) du tableau devient le pointeur sur le deuxième élément du tableau) pour fonctionner. Pourquoi ça ne marche pas en C?

Le RHS de l’expression correspond à un pointeur sur le deuxième élément de arr . Cependant, cette ligne ne fonctionne pas en raison de (3) ci-dessus. arr n’est pas une valeur modifiable. Il ne peut pas être utilisé comme LHS d’un opérateur d’affectation.

char[] n’est pas un pointeur, alors que char* est un pointeur. Cela fonctionne, mais c’est une mauvaise solution:

 int main() { char *arr = "Hello"; arr = arr + 1; // Wrong! printf("%s\n", arr); // Output: ello } 

Si arr est alloué en tas avec malloc vous pouvez obtenir une fuite de mémoire si free mémoire free commençant par arr+1 n’est pas arr .

Mais vous pouvez faire quelque chose comme ceci:

 int main() { char arr[] = "Hello"; char *wordFromSecondLetter = &arr[0] + 1; printf("%s\n", wordFromSecondLetter); // Output: ello } 

Ou comme ça

 int main() { char arr[] = "Hello"; printf("%s\n", &arr[1]); // Output: ello } 

Parce que arr n’est pas un pointeur mais un tableau de caractères. Vous pouvez le vérifier en vérifiant sizeof arr . Pour obtenir un pointeur sur char, vous devez utiliser char *arr = "Hello"; .

La plus grande différence entre un pointeur et un tableau est que vous pouvez directement affecter une valeur à un pointeur, mais vous ne pouvez pas le faire avec un tableau.

En fait, lorsque vous écrivez arr + 1 , arr “décroît” vers le pointeur vers son premier élément, c’est-à-dire arr == &arr[0] . Donc, arr + 1 est une arithmétique de pointeur légal, mais donner sa valeur à arr , qui est un tableau, est illégal.