Est-ce que les compilateurs transfèrent le type effectif via memcpy / memmove

Selon N1570 6.5 / 6:

Si une valeur est copiée dans un object sans type déclaré à l’aide de memcpy ou memmove, ou est copiée sous la forme d’un tableau de type caractère, le type effectif de l’object modifié pour cet access et pour les access ultérieurs qui ne modifient pas la valeur est le type. type effectif de l’object à partir duquel la valeur est copiée, s’il en a un.

Cela suggérerait que même sur un système où “long” et un autre type entier ont la même représentation, les éléments suivants invoqueraient un comportement non défini:

#if ~0UL == ~0U #define long_equiv int #elif ~0UL == ~0ULL #define long_equiv long long #else #error Oops #endif long blah(void) { long l; long_equiv l2; long_equiv *p = malloc(sizeof (long)); l = 1234; memcpy(p, &l, sizeof (long)); l2 = *p; free(p); // Added to address complaint about leak return l2; } 

Puisque les données pointées par l clairement un type effectif long et que l’object pointé par p n’a pas de type déclaré, la memcpy doit définir le type effectif de stockage sur long . Etant donné que l’utilisation d’une valeur de type long_equiv pour lire un object avec un type effectif de type long n’est pas autorisée, le code invoquerait le comportement non défini.

Étant donné qu’avant C99, memcpy était l’un des moyens standard de copier des données d’un type sur un autre type de stockage, les nouvelles règles relatives à memcpy nombreux codes existants d’appeler le comportement non memcpy . Si la règle avait plutôt été que l’utilisation de memcpy pour écrire dans la mémoire allouée laisse la destination sans aucun type effectif, le comportement serait défini.

Existe-t-il des compilateurs qui ne se comportent pas comme si memcpy le type effectif de la destination non défini lorsqu’il était utilisé pour copier des informations dans un stockage alloué, ou l’utilisation de memcpy à des fins de traduction des données devrait-elle être considérée comme “sûre”? Si certains compilateurs appliquent le type effectif de la source à la destination, quelle serait la bonne façon de copier des données sans recourir à des types? Qu’entend-on par “copié en tant que tableau de type de caractère”?

La norme C dit que le type effectif est transféré. Par conséquent, par définition, tous les compilateurs conformes transfèrent le type effectif.

Votre exemple de code provoque un comportement indéfini en enfreignant la règle d’alias ssortingcte, car une valeur de type effectif long est lue par une lvalue de type long long .

C’était également vrai dans C89, je ne suis pas sûr de ce que vous évoquez à propos de “nouvelles règles dans C99” (à part le fait que long long n’était pas dans C89).

Il est vrai que, lorsque C était normalisé, certains codes existants avaient un comportement indéfini. Et il est également vrai que les gens continuent à écrire du code avec un comportement indéfini.

Qu’entend-on par “copié comme un tableau de type de caractère”?

Cela signifie que vous copiez caractère par caractère à l’aide d’un type de caractère.

Quelle serait la bonne façon de copier des données de manière indépendante du type?

Il n’est pas possible “d’effacer le type effectif”, pour autant que je sache. Pour lire correctement une valeur utilisant un long long * , vous devez pointer vers un emplacement de type effectif long long .

Dans votre code, par exemple:

 // If we have confirmed that long and long long have the same size and representation long long x; memcpy(&x, p, sizeof x); return x; 

Le pseudonyme d’union est une autre option.

Si vous n’aimez pas tout cela, alors comstackz avec -fno-ssortingct-aliasing .

Expérimentalement, gcc 6.2 se comporte de manière qui ne serait justifiable qu’en considérant que memmove transfère le type effectif de la source à la destination. Si gcc peut déterminer que les pointeurs source et cible correspondent, il considérera l’opérande mémoire comme lisible uniquement via son type effectif antérieur, plutôt que comme une mémoire qui a été écrite en dernier en utilisant un type de caractère et peut donc être utilisée en utilisant n’importe quel type. Un tel comportement serait injustifiable sans la règle permettant à la memcpy de transférer des informations de type effectif.

D’autre part, le comportement de gcc n’est parfois pas justifiable en vertu d’une règle. Il n’est donc pas nécessairement clair si le comportement de gcc est une conséquence de l’interprétation de la norme donnée par ses auteurs, ou s’il est simplement enfreint. Par exemple, s’il peut déterminer que la cible de destination de memcpy contient le même motif binary constant que la source, il traitera la memcpy comme une opération non-op même si la source contenait le type qui serait ensuite utilisé pour lire le stockage de destination. et la destination contenait un type différent que le compilateur avait décidé de ne pas aliaser lors de la prochaine lecture.