web-dev-qa-db-fra.com

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

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

Pour 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, je m'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?

40
Jin

arr + 1 est en effet un pointeur vers le deuxième élément du tableau (c'est-à-dire &arr[1]).

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

Premièrement, arr est un tableau d'éléments char, pas un pointeur. Il y a un décalage évident de type ici.

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

Enfin, si nous ignorons simplement les subtilités plus profondes et nous concentrons sur ce qui se passe formellement au niveau supérieur, en raison de désintégration de type tablea votre expression équivaut à

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

L'affectation est impossible car le côté gauche est le résultat d'une conversion de type [implicite]. En type C, les conversions produisent toujours des valeurs r. Vous ne pouvez pas attribuer à rvalues.

En d'autres termes, ce n'est pas "l'arithmétique des pointeurs" qui n'est pas autorisée ici. L'arithmétique du pointeur est correcte. C'est ce que vous faites avec le résultat de l'arithmétique du pointeur qui provoque l'erreur.

55
AnT

Les tableaux sont des valeurs l non modifiables. Ils ne peuvent pas être l'opérande gauche d'un opérateur d'affectation.

C11-§6.3.2.1:

A lvalue modifiable est une lvalue qui n'a pas de type tablea, n'a pas de type incomplet, [...]

§6.5.16/2:

Un opérateur d'assignation doit avoir une valeur l modifiable comme opérande gauche.

Dans la déclaration

arr = arr + 1; 

arr est l'opérande gauche de = 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 du 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 sur son premier élément, bien que les pointeurs et les tableaux soient de types différents. Les tableaux ne sont pas des pointeurs . Seuls l'arithmétique du pointeur et l'indexation des tableaux sont équivalents. Par exemple

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

En effet, les tableaux sont similaires aux pointeurs, sauf qu'ils ne peuvent pas être modifiés. Cependant, vous pouvez modifier un pointeur pointant 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 vers le premier caractère du tableau, c'est-à-dire 'H' et après avoir modifié la valeur, il pointera vers le deuxième caractère, c'est-à-dire '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 qui montre que arr+1 est en effet l'arithmétique des pointeurs. Cependant, vous ne pouvez pas modifier la valeur de arr car son type est celui d'un tableau de caractères et non un pointeur sur un tableau de caractères.

10
Natesh Raina

Pour 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. Lors de l'utilisation de l'opérateur addressof (&arr). Le type de &arr Est char (*)[6]. Ce n'est pas char**.

  2. Lors de l'utilisation de l'opérateur sizeof. sizeof(arr) est 6. S'il s'agissait d'un pointeur, il aurait la taille d'un pointeur (4 ou 8 sur la plupart des plates-formes courantes).

  3. Lorsqu'il est utilisé comme LHS d'un opérateur d'affectation. Une variable de type tableau n'est pas modifiable.

Par conséquent, je m'attendais à ce que arr = arr + 1 (Le pointeur sur le premier élément (arr) du tableau devienne le pointeur sur le deuxième élément du tableau) fonctionne. Pourquoi cela ne fonctionne-t-il pas en C?

Le RHS de l'expression est évalué comme un pointeur vers 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.

8
R Sahu

char[] n'est pas un pointeur, tandis que char* est un pointeur. Cela fonctionne, mais ce n'est pas la bonne 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 démarre arr+1 pas arr.

Mais vous pouvez faire quelque chose comme ça:

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
}
4
Inline

Parce que arr n'est pas un pointeur mais un tableau de caractères. Vous pouvez le vérifier en cochant 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 attribuer une valeur à un pointeur, mais vous ne pouvez pas le faire à un tableau.

En fait, lorsque vous écrivez arr + 1, arr "se désintègre" du pointeur sur son premier élément, c'est-à-dire arr == &arr[0]. Alors arr + 1 est une arithmétique légale du pointeur, mais donner sa valeur à arr, qui est un tableau, est illégal.

3
nalzok