34 votes

`* ((* (& Array + 1)) - 1)` est-il sûr d'utiliser le dernier élément d'un tableau automatique?

Supposons que je veuille obtenir le dernier élément d'un tableau automatique dont la taille est inconnue. Je sais que je peux utiliser l'opérateur sizeof pour obtenir la taille du tableau et obtenir le dernier élément en conséquence.

L'utilisation de *((*(&array + 1)) - 1) sûre?

Comme:

 char array[SOME_SIZE] = { ... };
printf("Last element = %c", *((*(&array + 1)) - 1));
 
 int array[SOME_SIZE] = { ... };
printf("Last element = %d", *((*(&array + 1)) - 1));
 

etc

22voto

Peter Points 4026

Non ce n'est pas.

&array est de type pointeur sur char[SOME_SIZE] (dans le premier exemple donné). Cela signifie que &array + 1 indique la mémoire immédiatement après la fin de array . Déréférencer cela (comme dans (*(&array+1)) donne un comportement indéfini.

Pas besoin d'analyser plus loin. Une fois qu'une partie d'une expression donne un comportement indéfini, c'est l'expression entière qui le fait.

18voto

ameyCU Points 2095

Je ne pense pas qu'il est sécuritaire.

À partir de la norme, comme @dasblinkenlight cité dans sa réponse (maintenant supprimé) il y a aussi quelque chose que je voudrais ajouter:

C99 Section 6.5.6.8 -

[...]
si l'expression P pointe vers le dernier élément d'un tableau d'objet, l'expression (P)+1 points [...]
Si le résultat des points l'un après le dernier élément de l'objet tableau, il ne doit pas être utilisé comme opérande de unaire * l'opérateur qui est évaluée.

Donc, comme il dit , nous ne devrions pas faire ce *(&array + 1) que ça va au-delà du dernier élément du tableau et ainsi de * ne doit pas être utilisé.

Comme aussi il est bien connu que le déréférencement de pointeurs pointant vers un non autorisée emplacement de mémoire conduit à un comportement indéterminé .

12voto

Filipe Gonçalves Points 7558

Je crois que c'est un comportement indéfini pour les raisons Peter mentionne dans sa réponse.

Il y a un énorme débat sur *(&array + 1). D'une part, de déférence &array + 1 semble être légal, car c'est seulement en changeant le type d' T (*)[] de retour à l' T [], mais d'un autre côté, c'est toujours un pointeur non initialisé, non utilisés et non alloué de la mémoire.

Ma réponse repose sur les éléments suivants:

C99 6.5.6.7 (la Sémantique de l'additif opérateurs)

Pour l'application de ces opérateurs, un pointeur vers un objet qui est pas un élément d'un tableau se comporte comme un pointeur vers le premier élément d'un tableau de longueur avec le type de l'objet comme sa type d'élément.

Depuis &array n'est pas un pointeur vers un objet qui est un élément d'un tableau, puis en fonction de cela, cela signifie que le code est équivalent à:

char array_equiv[1][SOME_SIZE] = { ... };
/* ... */
printf("Last element = %c", *((*(&array_equiv[0] + 1)) - 1));

C'est, &array est un pointeur sur un tableau de 10 caractères, de sorte qu'il se comporte comme un pointeur vers le premier élément d'un tableau de longueur 1, où chaque élément est un tableau de 10 caractères.

Maintenant, qui, ensemble, avec la clause qui suit (déjà mentionné dans d'autres réponses; cet extrait est manifestement volé ameyCU de la réponse):

C99 Section 6.5.6.8 -

[...]
si l'expression P pointe vers le dernier élément d'un tableau d'objet, l'expression (P)+1 points [...]
Si le résultat des points l'un après le dernier élément de l'objet tableau, il ne doit pas être utilisé comme opérande de unaire * l'opérateur qui est évaluée.

Fait-il assez clair que c'est UB: il est équivalent à un déréférencement d'un pointeur qui pointe un passé le dernier élément de l' array_equiv.

Oui, dans le monde réel, il probablement œuvres, comme dans la réalité, le code d'origine n'a pas vraiment de déréférencer un emplacement de mémoire, c'est surtout un type de conversion à partir d' T (*)[] de T [], mais je suis assez sûr qu'à partir d'une norme stricte conformité point de vue, c'est un comportement indéfini.

2voto

Viktor Toth Points 437

Il est probablement plus sûr, mais il ya quelques mises en garde.

Supposons que nous avons

T array[LEN];

Ensuite, &array est de type T(*)[LEN].

Ensuite, &array + 1 est de nouveau de type T(*)[LEN], en pointant juste après la fin du tableau original.

Ensuite, *(&array + 1) est de type T[LEN], qui peut être converti implicitement en T*, continue de pointer juste après la fin du tableau original. (Si nous NE le déréférencement d'un emplacement de mémoire non valide: l' * opérateur n'est pas évalué).

Ensuite, *(&array + 1) - 1 est de type T*, pointant à la dernière matrice de localisation.

Enfin, nous déréférencement de cela (ce qui est légitime si la longueur du tableau n'est pas nul): *(*(&array + 1) - 1) donne le dernier élément du tableau, une valeur de type T.

Notez que la seule fois où nous avons fait de déréférencer un pointeur est dans cette dernière étape.

Maintenant, le potentiel des mises en garde.

Tout d'abord, *(&array + 1) officiellement apparaît comme une tentative de déréférencer un pointeur qui pointe vers un emplacement de mémoire non valide. Mais il ne l'est pas vraiment. C'est la nature de la matrice de pointeurs: cette forme de déréférencement ne change que le type de l'indicateur, ne fait pas l'entraîner dans une tentative de récupérer la valeur de la position référencée. C'est, array est de type T[LEN] , mais il peut être implicitement converti en type &T, pointant vers le premier élément du tableau; &array est un pointeur de type T[LEN], pointant vers le début du tableau; *(&array+1) est de nouveau de type T[LEN] qui peut être implicitement converti en type &T. À aucun moment, est un pointeur fait déréférencé.

Deuxièmement, &array + 1 peut en fait être une adresse non valide, mais il ne l'est pas vraiment: Mon C++11 manuel de référence me dit explicitement que "la Prise d'un pointeur vers l'élément d'un au-delà de la fin d'un tableau est garanti pour fonctionner", et une déclaration similaire est également faite dans le K&R, de sorte que je crois qu'il a toujours été la norme de comportement.

Enfin, dans le cas d'un zéro-longueur du tableau, l'expression déréférence l'emplacement de la mémoire juste avant le tableau, qui peut être non alloué/non valide. Mais cette question pourrait également se poser si on a utilisé une approche plus conventionnelle à l'aide de sizeof() sans tester différente de zéro d'abord la longueur.

En bref, je ne crois pas qu'il y a quelque chose d'indéfini ou dépendant de l'implémentation à propos de cette expression du comportement.

1voto

hans lepoeter Points 29

À mon humble avis qui pourrait fonctionner, mais est sans doute imprudent. Vous devriez examiner attentivement vos sw conception et demandez-vous pourquoi vous voulez la dernière entrée du tableau. C'est le contenu de la matrice complètement inconnu pour vous, ou est-il possible de définir la structure en termes de c les structures et les syndicats. Si c'est le cas, rester à l'écart de complexes opérations de pointeur dans un tableau de char, par exemple, et de définir les données correctement dans vous code en c, dans les structures et les syndicats, dans la mesure du possible.

Ainsi, au lieu de :

 printf("Last element = %c", *((*(&array + 1)) - 1));

Il pourrait être :

 printf("Checksum = %c", myStruct.MyUnion.Checksum);

Ceci permet de clarifier votre code. La dernière lettre de votre tableau ne signifie rien pour une personne qui n'est pas familier avec ce qui est dans ce tableau. myStruct.myUnion.La somme de contrôle du sens à n'importe qui. L'étude de la myStruct structure pourrait expliquer l'ensemble de la structure de données à n'importe qui. Veuillez utiliser quelque chose comme ça si elle peut être déclarée dans une telle voie. Si vous êtes dans la situation rare, vous ne pouvez pas, étude réponses ci-dessus, ils font du bon sens, je pense que

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