32 votes

Memcpy (& a + 1, & b + 1, 0) est-il défini en C11?

Cette question suit cette question précédente au sujet de la definedness d' memcpy(0, 0, 0), qui a été établi de façon concluante à un comportement indéfini.

Le site question, la réponse repose sur le contenu de C11 de la clause 7.1.4:1

Chacun des énoncés suivants s'applique à moins d'indication contraire dans la description détaillée qui suit: Si un argument à une fonction a une valeur non valide (par exemple une valeur en dehors du domaine de la fonction, ou un pointeur en dehors de l'espace d'adressage du programme, ou un pointeur null, [...]) [...] le comportement est indéfini. [...]

La fonction standard memcpy() attend des pointeurs vers void et const void, comme suit:

void *memcpy(void * restrict s1, const void * restrict s2, size_t n);

La question vaut la peine de demander à tous les uniquement parce qu'il y a deux notions de "validité" des pointeurs dans la norme: il y a les pointeurs qui peuvent valablement être obtenus par le biais de l'arithmétique de pointeur et peut valablement être comparé avec <, > pour les autres pointeurs à l'intérieur du même objet. Et il y a des pointeurs qui sont valides pour un déréférencement. La première classe comprend "un passé" pointeurs comme &a + 1 et &b + 1 dans l'extrait suivant, alors que la seconde classe ne comprend pas ces comme valide.

char a;
const char b = '7';
memcpy(&a + 1, &b + 1, 0);

Si l'extrait ci-dessus sera considérée comme comportement défini, à la lumière du fait que les arguments de l' memcpy() sont typés comme des indicateurs d' void de toute façon, la question de leur validité ne peut pas être sur déréférencement les. Ou doit - &a + 1 et &b + 1 être considéré comme "en dehors de l'espace d'adressage du programme"?

C'est important pour moi parce que je suis dans le processus de formalisation des effets de la norme C fonctions. J'avais écrit une pré-condition de l' memcpy() comme requires \valid(s1+(0 .. n-1));,jusqu'à ce qu'il a été signalé à mon attention que la GCC 4.9 avait commencé de façon agressive optimiser la bibliothèque des appels de fonction au-delà de ce qui est exprimé dans la formule ci-dessus (en effet). La formule \valid(s1+(0 .. n-1)) dans ce langage de spécification est équivalent à true lorsque n est 0, et ne capture pas le comportement non défini que GCC 4.9 s'appuie sur les optimiser.

18voto

ouah Points 75311

C11 dit:

(C11, 7.24.2.1p2) "La fonction memcpy copie n caractères de l'objet pointé par s2 dans l'objet pointé par s1."

&a + 1 lui-même est un pointeur valide pour l'addition d'entiers mais &a + 1 n'est pas un pointeur vers un objet, donc l'appel invoque un comportement non défini.

5voto

user3958062 Points 61

Alors que la réponse "correcte" selon la norme semble être en désaccord, je peux le trouver que de mauvaise foi qu'après un int[6]; int b[6]; l'ensemble de

memcpy(a+0, b+0, 6);
memcpy(a+1, b+1, 5);
memcpy(a+2, b+2, 4);
memcpy(a+3, b+3, 3);
memcpy(a+4, b+4, 2);
memcpy(a+5, b+5, 1);

doit être valide (et copier une zone qui se termine à la fin de l'tableaux), tandis que

memcpy(a+6, b+6, 0);

est valable dans la lumière du comte, mais pas les adresses. C'est la fin même de la zone copiée!

Personnellement, j'aurais de se pencher vers la définition de memcpy(0,0,0) étant valable aussi bien (avec la justification de tout simplement demander pointeurs valides mais pas d'objets) mais au moins c'est un cas singulier, tandis que la "fin du tableau" cas est une exception à un autre motif régulier pour la copie d'une zone à la fin d'un tableau.

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