Il s'agit d'une longue réponse traitant en profondeur d'un sujet délicat.
TL;DR
Je ne suis pas d'accord avec le analyse par Dror K .
Le problème principal est une mauvaise compréhension de ce que signifie le §6.2.1 ¶6 des normes C99 et C11, et son application inappropriée à une simple affectation d'un nombre entier telle que :
fam_ptr->nonfam_member = 23;
Cette mission est no autorisé à modifier tout octet de remplissage dans la structure pointée par fam_ptr
. Par conséquent, l'analyse fondée sur la présomption que cela peut modifier les octets de remplissage dans la structure est erronée.
Contexte
En principe, je ne suis pas terriblement préoccupé par la norme C99 et ses corrigenda. ses corrigenda ; ils ne sont pas la norme actuelle. Cependant, l'évolution de la spécification des membres de tableaux flexibles est informative.
La norme C99 - ISO/IEC 9899:1999 - comportait 3 rectificatifs techniques :
- TC1 publié le 2001-09-01 (7 pages),
- TC2 publié le 2004-11-15 (15 pages),
- TC3 publié le 2007-11-15 (10 pages).
C'est le CT3, par exemple, qui a déclaré que gets()
était obsolète et déprécié, ce qui a conduit à sa suppression de la norme C11.
La norme C11 - ISO/IEC 9899:2011 - comporte un corrigendum technique corrigendum technique, mais qui fixe simplement la valeur de deux macros laissées accidentellement laissées dans le formulaire 201ymmL
- les valeurs requises pour __STDC_VERSION__
y __STDC_LIB_EXT1__
ont été corrigés à la valeur 201112L
. (Vous pouvez voir le TC1 - formellement "ISO/IEC 9899:2011/Cor.1:2012(en) Technologies de l'information - Langages de programmation - C TECHNIQUE CORRIGENDUM 1" - à l'adresse https://www.iso.org/obp/ui/#iso:std:iso-iec:9899:ed-3:v1:cor:1:v1:en . Je n'ai pas encore trouvé comment le télécharger, mais c'est tellement simple que cela n'a pas beaucoup d'importance.
Norme C99 sur les éléments de réseaux flexibles
ISO/IEC 9899:1999 (avant TC2) §6.7.2.1 ¶16 :
Dans un cas particulier, le dernier élément d'une structure comportant plus d'une membre nommé peut avoir un type de tableau incomplet ; on appelle cela un élément de réseau flexible . À deux exceptions près, l'élément de réseau flexible est ignoré. Tout d'abord, la taille de la structure doit être égale au décalage du dernier élément d'une structure par ailleurs identique qui remplace la structure membre de tableau flexible par un tableau de longueur non spécifiée. 106) Deuxièmement, lorsqu'un .
(ou ->
) a un opérande de gauche qui est (un pointeur vers) une structure avec un membre de tableau flexible et l'opérande de droite (un pointeur vers). structure avec un membre de tableau flexible et l'opérande droit nomme ce membre. nomme ce membre, il se comporte comme si ce membre était remplacé par le tableau le plus long (avec le même type d'élément) qui ne ferait pas l'affaire. par le plus long tableau (avec le même type d'élément) qui ne rendrait pas la structure structure plus grande que l'objet auquel on accède ; le décalage du tableau reste celui du tableau flexible. tableau reste celui du membre du tableau flexible, même s'il est différent de celui du tableau de remplacement. même s'il diffère de celui du tableau de remplacement. Si ce tableau n'a pas d'éléments, il se comporte comme s'il avait un élément, mais le comportement est différent. élément mais le comportement est indéfini si une tentative est faite pour accéder à cet élément ou de générer un pointeur sur cet élément.
126) La longueur n'est pas spécifiée pour tenir compte du fait que des que les implémentations peuvent donner aux membres du tableau des alignements différents selon en fonction de leur longueur.
(Cette note de bas de page est supprimée dans la réécriture). La norme C99 originale comprenait un exemple :
¶17 EXEMPLE En supposant que tous les membres du tableau sont alignés de la même façon, après les déclarations :
struct s { int n; double d[]; };
struct ss { int n; double d[1]; };
les trois expressions :
sizeof (struct s)
offsetof(struct s, d)
offsetof(struct ss, d)
ont la même valeur. La structure struct s possède un membre de tableau flexible d.
¶18 Si sizeof (double) est 8, alors après l'exécution du code suivant :
struct s *s1;
struct s *s2;
s1 = malloc(sizeof (struct s) + 64);
s2 = malloc(sizeof (struct s) + 46);
et en supposant que les appels à malloc réussissent, les objets pointés par s1 et s2 se comportent comme si les identificateurs avaient été déclarés comme :
struct { int n; double d[8]; } *s1;
struct { int n; double d[5]; } *s2;
¶19 Après d'autres affectations réussies :
s1 = malloc(sizeof (struct s) + 10);
s2 = malloc(sizeof (struct s) + 6);
ils se comportent alors comme si les déclarations l'étaient :
struct { int n; double d[1]; } *s1, *s2;
et :
double *dp;
dp = &(s1->d[0]); // valid
*dp = 42; // valid
dp = &(s2->d[0]); // valid
*dp = 42; // undefined behavior
¶20 L'affectation :
*s1 = *s2;
ne copie que le membre n et aucun des éléments du tableau. De même :
struct s t1 = { 0 }; // valid
struct s t2 = { 2 }; // valid
struct ss tt = { 1, { 4.2 }}; // valid
struct s t3 = { 1, { 4.2 }}; // invalid: there is nothing for the 4.2 to initialize
t1.n = 4; // valid
t1.d[0] = 4.2; // undefined behavior
Une partie de cet exemple a été supprimée en C11. Le changement n'a pas été noté (et n'avait pas besoin d'être noté) dans le CT2 parce que les exemples ne sont pas normatifs. exemples ne sont pas normatifs. Mais le matériel réécrit dans la C11 est informatif lorsqu'il est étudié.
N983 document identifiant un problème avec les membres d'un réseau flexible
N983 du WG14 Pré-Santa Cruz-2002 mailing est, je crois, la déclaration initiale d'un rapport de défaut. Il indique que certains compilateurs C (en citant trois) parviennent à mettre un FAM avant le padding à la fin d'une structure. Le rapport de défaut final était DR 282 .
D'après ce que j'ai compris, ce rapport a conduit au changement dans le CT2, bien que je n'ai pas retracé toutes les étapes du processus. Il semble que le DR ne soit plus disponible séparément.
Le CT2 a utilisé la formulation de la norme C11 dans le matériel normatif.
Norme C11 sur les éléments de réseaux flexibles
Alors, que dit la norme C11 à propos des membres de réseaux flexibles ?
§6.7.2.1 Spécificateurs de structure et d'union
¶3 Une structure ou une union ne doit pas contenir un membre dont le type est incomplet ou type de fonction (par conséquent, une structure ne doit pas contenir une instance de elle-même, mais peut contenir un pointeur vers une instance d'elle-même), sauf que que le dernier membre d'une structure comportant plus d'un membre nommé peut avoir un type tableau incomplet ; une telle structure (et toute union contenant, éventuellement de manière récursive, un membre qui est une telle structure) ne doit pas être un membre d'une structure ou un élément d'un tableau.
Cela positionne fermement le FAM à la fin de la structure - " le dernier ". membre " est par définition à la fin de la structure, et ceci est confirmé par :
¶15 Au sein d'un objet de structure, les membres du champ non-binaire et le les unités dans lesquelles les champs de bits résident ont des adresses qui augmentent dans ordre dans lequel ils sont déclarés.
¶17 Il peut y avoir un remplissage sans nom à la fin d'une structure ou d'une union.
¶18 A titre de cas particulier, le dernier élément d'une structure comportant plus de un membre nommé peut avoir un type de tableau incomplet. élément de réseau flexible . Dans la plupart des situations, l'élément de réseau flexible est ignoré. En particulier, la taille de la structure est la même que si le membre tableau flexible flexible était omis, sauf qu'il peut y avoir plus de remplissage en fin de ligne que que ce que l'omission impliquerait. Toutefois, lorsqu'un .
(ou ->
) a un opérande gauche qui est (un pointeur vers) une structure avec un tableau flexible. structure avec un membre de tableau flexible et l'opérande droit nomme ce membre. nomme ce membre, il se comporte comme si ce membre était remplacé par le tableau le plus long (avec les mêmes éléments). par le plus long tableau (avec le même type d'élément) qui ne rendrait pas ne rendrait pas la structure plus grande que l'objet auquel on accède ; le décalage du tableau reste celui du membre du tableau flexible, même si il serait différent de celui du tableau de remplacement. Si ce tableau n'a pas d'éléments, il se comporte comme s'il en avait un. élément mais le comportement est indéfini si une tentative est faite pour accéder à cet élément ou de générer un pointeur sur cet élément.
Ce paragraphe contient la modification du ¶20 de l'ISO/IEC 9899:1999/Cor.2:2004(E) - le TC2 pour C99 ;
Les données situées à la fin de la partie principale d'une structure contenant une membre de tableau flexible est un remplissage de fin régulier qui peut se produire avec tout type de structure. Un tel remplissage ne peut pas être accédé légitimement, mais peut être passé à des fonctions de bibliothèque, etc. fonctions de bibliothèque, etc., par le biais de pointeurs vers la structure, sans comportement indéfini.
La norme C11 contient trois exemples, mais le premier et le troisième sont sont liés à des structures anonymes et à des unions plutôt qu'aux mécanismes de membres de tableaux flexibles. N'oubliez pas que les exemples ne sont pas "normatifs", mais qu'ils sont illustratifs.
¶20 EXEMPLE 2 Après la déclaration :
struct s { int n; double d[]; };
la structure struct s
dispose d'un membre du réseau flexible d
. Une façon typique de l'utiliser est :
int m = /* some value */;
struct s *p = malloc(sizeof (struct s) + sizeof (double [m]));
et en supposant que l'appel à malloc
réussit, l'objet pointé par par p
se comporte, dans la plupart des cas, comme si p
avait été déclaré comme :
struct { int n; double d[m]; } *p;
(il existe des circonstances dans lesquelles cette équivalence est rompue ; en notamment, les décalages des membres d
pourraient ne pas être les mêmes).
¶21 Suite à la déclaration ci-dessus :
struct s t1 = { 0 }; // valid
struct s t2 = { 1, { 4.2 }}; // invalid
t1.n = 4; // valid
t1.d[0] = 4.2; // might be undefined behavior
L'initialisation de t2
est invalide (et viole une contrainte) car struct s
est traité comme s'il ne contenait pas de membre d
. L'affectation à t1.d[0]
est probablement un comportement non défini, mais il est possible que
sizeof (struct s) >= offsetof(struct s, d) + sizeof (double)
auquel cas l'affectation serait légitime. Néanmoins, elle ne peut apparaître dans un code strictement conforme.
¶22 Après la nouvelle déclaration :
struct ss { int n; };
les expressions :
sizeof (struct s) >= sizeof (struct ss)
sizeof (struct s) >= offsetof(struct s, d)
sont toujours égales à 1.
¶23 Si sizeof (double)
est 8, alors après l'exécution du code suivant :
struct s *s1;
struct s *s2;
s1 = malloc(sizeof (struct s) + 64);
s2 = malloc(sizeof (struct s) + 46);
et en supposant que les appels à malloc
réussir, les objets pointés par s1
y s2
se comportent, dans la plupart des cas, comme si les identifiants avaient été déclarés comme :
struct { int n; double d[8]; } *s1;
struct { int n; double d[5]; } *s2;
¶24 Après d'autres affectations réussies :
s1 = malloc(sizeof (struct s) + 10);
s2 = malloc(sizeof (struct s) + 6);
ils se comportent alors comme si les déclarations l'étaient :
struct { int n; double d[1]; } *s1, *s2;
et :
double *dp;
dp = &(s1->d[0]); // valid
*dp = 42; // valid
dp = &(s2->d[0]); // valid
*dp = 42; // undefined behavior
¶25 L'affectation :
*s1 = *s2;
ne fait que copier le membre n
si l'un des éléments du tableau se trouve dans le premier sizeof (struct s)
octets de la structure, ils peuvent être copiés ou simplement écrasés avec des valeurs indéterminées.
Notez que cela a changé entre C99 et C11.
Une autre partie de la norme décrit ce comportement de copie :
§6.2.6 Représentation des types §6.2.6.1 Généralités
¶6 Lorsqu'une valeur est stockée dans un objet de type structure ou union, y compris dans un objet membre, les octets de la représentation de l'objet qui correspondent à des octets de remplissage prennent des valeurs non spécifiées. valeurs non spécifiées. 51) La valeur d'une structure ou d'un objet d'union n'est n'est jamais une représentation de piège, même si la valeur d'un membre de l'objet structure ou de l'objet union peut être une représentation piège.
51) Ainsi, par exemple, l'affectation de structure ne doit pas copier aucun bit de remplissage.
Illustration de la structure problématique du GPA
Dans le C salon de discussion , I a écrit un peu de informations dont ceci est une paraphrase :
Pensez-y :
struct fam1 { double d; char c; char fam[]; };
En supposant que le double nécessite un alignement sur 8 octets (ou 4 octets ; cela n'a pas trop d'importance n'a pas trop d'importance, mais je m'en tiendrai à 8). struct non_fam1a { double d; char c; };
aurait 7 octets de remplissage après c
et une taille de 16. Plus loin, struct non_fam1b { double d; char c; char nonfam[4]; };
Le site a 3 octets de remplissage après le nonfam
et une taille de 16.
La suggestion est que le début de fam
en struct fam1
peut être à l'offset 9, même si sizeof(struct fam1)
est de 16. Ainsi, les octets après c
ne sont pas des rembourrages (nécessairement).
Donc, pour un FAM suffisamment petit, la taille de la structure plus le FAM pourrait toujours être inférieure à la taille de struct fam
.
L'allocation prototypique est :
struct fam1 *fam = malloc(sizeof(struct fam1) + array_size * sizeof(char));
lorsque le FAM est de type char
(dans le mot anglais struct fam1
). C'est une surestimation (brute) lorsque le décalage de fam est inférieur à sizeof(struct fam1)
.
Dror K. a pointé :
Il existe des macros pour calculer le stockage "précis" nécessaire. en se basant sur les offsets FAM qui sont inférieurs à la taille de la structure. Comme celle-ci : https://gustedt.wordpress.com/2011/03/14/flexible-array-member/
Répondre à la question
La question est posée :
- En utilisant des membres de tableaux flexibles (FAM) dans les types de structures, est-ce que nous exposons nos programmes à la possibilité d'un comportement non défini ? exposons nos programmes à la possibilité d'un comportement indéfini ?
- Est-il possible pour un programme d'utiliser les GPA tout en étant un programme strictement programme strictement conforme ?
- Le décalage du membre du tableau flexible doit-il être à la fin de la structure ? à la fin de la structure ?
Les questions s'appliquent à la fois au C99 (CT3) et au C11 (CT1).
Je pense que si vous codez correctement, les réponses sont "Non", "Oui", "Non et Oui, selon ".
Question 1
Je suppose que l'intention de la question 1 est "votre programme doit-il inévitablement être exposé à un comportement indéfini si vous utilisez n'importe quel FAM n'importe où ?" Pour énoncer ce qui me semble évident : il existe de nombreuses façons d'exposer un un programme à un comportement indéfini (et certaines de ces façons impliquent des structures avec des membres de tableaux flexibles).
Je ne pense pas que le simple fait d'utiliser un FAM signifie que le programme automatiquement (invoque, est exposé à) un comportement non défini.
Question 2
Section §4 Conformité définit :
¶5 A programme strictement conforme ne doit utiliser que les fonctionnalités de du langage et de la bibliothèque spécifiées dans la présente internationale. 3) Il ne doit pas produire de résultats dépendant d'un comportement non spécifié, non défini, ou défini par l'implémentation, et ne doit pas excéder un comportement minimum de l'implémentation. d'implémentation.
3) Un programme strictement conforme peut utiliser des (voir 6.10.8.3) à condition que l'utilisation soit protégée par un mécanisme de protection approprié. directive de prétraitement d'inclusion conditionnelle appropriée utilisant la macro correspondante.
¶7 A programme conforme est celle qui est acceptable pour une implémentation conforme. 5) .
5) Les programmes strictement conformes sont destinés à être portable au maximum parmi les implémentations conformes. Les programmes conformes peuvent dépendre de fonctionnalités non portables d'une implémentation conforme. d'une implémentation conforme.
Je ne pense pas qu'il y ait de caractéristiques du C standard qui, si elles sont utilisées de la manière prévue par le standard, rend le programme non strictement conforme. S'il en existe, elles sont liées à un comportement dépendant de la localisation. Le comportement du code FAM n'est pas intrinsèquement dépendant des conditions locales.
Je ne pense pas que l'utilisation d'un FAM signifie intrinsèquement que le programme n'est pas strictement conforme.
Question 3
Je pense que la question 3 est ambiguë entre :
- 3A : Est-ce que le décalage de l'élément de tableau flexible doit être égal à la taille de la structure contenant l'élément de tableau flexible ?
- 3B : Le décalage de l'élément du réseau flexible doit-il être supérieur à celui de tout élément antérieur de la structure ? que le décalage de tout élément antérieur de la structure ?
La réponse à la question 3A est "Non" (voir l'exemple du C11 au paragraphe 25, cité ci-dessus).
La réponse à 3B est "Oui" (témoin §6.7.2.1 ¶15, cité ci-dessus).
En désaccord avec la réponse de Dror
Je dois citer la norme C et la réponse de Dror. Je vais utiliser [DK]
pour indiquer le début d'une citation de la réponse de Dror, et les citations non marquées sont tirées de la norme C.
En date du 2017-07-01 18:00 -08:00, le réponse courte par Dror K a dit :
[DK]
- Oui. Les conventions courantes d'utilisation des GPA exposent nos programmes à la la possibilité d'un comportement indéfini. Cela dit, je ne connais pas aucune implémentation conforme existante qui se comporterait mal.
Je ne suis pas convaincu que la simple utilisation d'un FAM signifie que le programme automatiquement un comportement indéfini.
[DK]
- Possible, mais peu probable. Même si nous n'atteignons pas réellement un comportement indéfini. nous sommes toujours susceptibles d'échouer la conformité stricte.
Je ne suis pas convaincu que l'utilisation d'un FAM rende automatiquement un programme non strictement conforme.
[DK]
- Non. L'offset du FAM n'est pas obligé d'être à la fin de la structure. il peut recouvrir tout octet de remplissage de fin.
C'est la réponse à mon interprétation 3A, et je suis d'accord avec cela.
La réponse longue contient l'interprétation des réponses courtes ci-dessus.
[DK]
Le problème était que les implémentations courantes de C99, comme GCC, ne suivaient pas suivaient pas les exigences de la norme, et permettaient au FAM de superposer tout octet de remplissage de fin. Leur approche était considérée comme plus efficace, et puisque pour eux, suivre les exigences de la norme standard - aurait pour conséquence de rompre la compatibilité ascendante, le comité a choisi de modifier la spécification et, à partir de C99 TC2 (Nov. 2004), la norme n'exige plus l'utilisation de l'octet de remplissage. 2004) la norme n'exigeait plus que le décalage du FAM soit à à la fin de la structure.
Je suis d'accord avec cette analyse.
[DK]
La nouvelle spécification a supprimé la déclaration qui exigeait que l'offset du FAM soit à la fin de la structure. du FAM soit à la fin du struct, et cela a introduit une conséquence très malheureuse conséquence très malheureuse, car la norme donne à la mise en œuvre la liberté de ne pas garder les valeurs des octets de remplissage dans les structures structures ou unions dans un état cohérent.
Je suis d'accord que la nouvelle spécification a supprimé l'exigence que le FAM soit stocké à un décalage supérieur ou égal à la taille de la structure. structure.
Je ne pense pas qu'il y ait un problème avec les octets de remplissage.
La norme indique explicitement que l'affectation de structure pour une structure contenant un FAM ignore effectivement le FAM (§6.7.2.1 ¶18). Elle doit copier les membres non FAM. Il est explicitement indiqué que les octets de remplissage ne doivent pas être copiés du tout (§6.2.6.1 ¶6 et note de bas de page 51). Et l'exemple 2 indique explicitement (de manière non normative §6.7.2.1 ¶25) que si le GPA chevauche l'espace défini par la structure, les données de la partie du GPA qui chevauche l'espace défini par la structure ne sont pas prises en compte. de la partie du FAM qui chevauche la fin de la structure peuvent être peuvent ou non être copiées.
[DK]
Cela signifie que si l'un de nos éléments FAM correspond à (ou recouvre) des octets de remplissage de fin, lors du stockage dans un membre de la structure- ils (peuvent) prendre des valeurs non spécifiées. Nous n'avons même pas besoin de nous demander si cela s'applique à une valeur stockée dans le FAM lui-même. le FAM lui-même, même l'interprétation stricte que cela s'applique seulement aux membres autres que le FAM, est assez dommageable.
Je ne vois pas cela comme un problème. Si vous pensez que vous pouvez copier une structure contenant un FAM à l'aide de la fonction l'assignation de structure et que le tableau FAM soit copié est intrinsèquement la copie laisse les données FAM logiquement non copiées. Tout programme qui dépend des données FAM dans le cadre de la structure est cassé. structure est cassé ; c'est une propriété du programme (défectueux), pas de la norme. standard.
[DK]
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
int main(void) {
struct s {
size_t len;
char pad;
int array[];
};
struct s *s = malloc(sizeof *s + sizeof *s->array);
if (sizeof *s > offsetof(struct s, array)) {
s->array[0] = 123;
s->len = 1; /* any padding bytes take unspecified values */
printf("%d\n", s->array[0]); /* indeterminate value */
}
free(s);
return 0;
}
Idéalement, bien sûr, le code devrait définir le membre nommé pad
à une valeur déterminée, mais cela ne cause pas réellement de problème puisque l'on n'y accède jamais.
Je ne suis absolument pas d'accord pour dire que la valeur de s->array[0]
dans le printf()
est indéterminée ; sa valeur est 123
.
La citation de la norme antérieure est (il s'agit du même §6.2.6.1 ¶6 dans C99 et C11, bien que le numéro de la note de bas de page soit 42 dans C99 et 51 dans C11) :
Lorsqu'une valeur est stockée dans un objet de type structure ou union, y compris dans un objet membre, les octets de la représentation de l'objet qui correspondent à des octets de remplissage prennent des valeurs non spécifiées.
Notez que s->len
n'est pas une affectation à un objet de type structure ou union ; il s'agit d'une affectation à un objet de type size_t
. Je pense que c'est peut-être la principale source de confusion ici.
Si le code est inclus :
struct s *t = malloc(sizeof(*t) + sizeof(t->array[0]));
*t = *s;
printf("t->array[0] = %d\n", t->array[0]);
alors la valeur imprimée est effectivement indéterminée. Cependant, c'est parce que la copie d'une structure avec un FAM n'est pas garantie de copier le FAM. Un code plus correct serait (en supposant que vous ajoutez #include <string.h>
bien sûr) :
struct s *t = malloc(sizeof(*t) + sizeof(t->array[0]));
*t = *s;
memmmove(t->array, s->array, sizeof(t->array[0]));
printf("t->array[0] = %d\n", t->array[0]);
Maintenant, la valeur imprimée est déterminée (elle est 123
). Notez que la condition sur if (sizeof *s > offsetof(struct s, array))
est sans importance pour mon analyse.
Puisque le reste de la réponse longue (principalement la section identifiée par le l'intitulé "comportement non défini") est basé sur une déduction erronée concernant la possibilité que les octets de remplissage d'une structure changent lors de l'attribution à un membre entier d'une structure, le reste de la discussion ne nécessite pas d'analyse supplémentaire. n'a pas besoin d'une analyse plus approfondie.
[DK]
Une fois que nous stockons dans un membre de la structure, les octets de remplissage prennent octets non spécifiés et, par conséquent, toute hypothèse faite sur les valeurs des éléments FAM qui correspondent aux octets de remplissage de queue, est maintenant fausse. Ce qui signifie que toute hypothèse nous fait échouer à la stricte stricte.
Ceci est basé sur une fausse prémisse ; la conclusion est fausse.
0 votes
Le C11 TC1 n'était-il pas le document qui a changé une paire de
yyyymm
en valeurs201112
? Le C11 TC1 n'a donc aucun effet sur le support FAM, etc.0 votes
@JonathanLeffler Pour éviter la confusion, j'ai ajouté de la confusion. En spécifiant C99 TC0 je faisais référence à C99 avant tout TC, en spécifiant C99 TC2 je faisais référence à C99 après TC2, et en spécifiant C11 TC1 je faisais référence à C11 après TC1. Si vous avez une suggestion sur la façon de refléter cela d'une manière moins confuse, faites-le moi savoir. Pour l'instant, j'ajoute des parenthèses
1 votes
Q1 est une non-question. Si vous écrivez un bug dans votre programme, FAMs ou autre, il peut avoir un comportement non défini. Pouvez-vous expliquer ce que vous demandez dans Q1 (et en quoi cela diffère de votre Q2) ?
0 votes
@M.M La première question porte spécifiquement sur l'utilisation par l'UB de structs avec des FAM (sommes-nous à tout La seconde question est de savoir s'il est possible pour un programme d'utiliser les GPA tout en étant un programme strictement conforme (sommes-nous en train de nous demander si les GPA ne sont pas un danger pour la santé publique ? toujours à défaut d'une stricte conformité ?). Mes réponses ci-dessous répondent aux deux préoccupations liées à l'UB et à la stricte conformité. Je suppose qu'il serait utile d'expliquer ce que signifient l'UB et la stricte conformité et en quoi les critères diffèrent, mais il semble que plus le texte est verbeux, moins les lecteurs ont la patience de le lire.
1 votes
Je ne comprends pas vraiment, par exemple, vous pourriez écrire en dehors des limites de l'espace alloué avec un FAM et ce serait un UB, donc il y a un risque d'UB ici.
0 votes
@M.M Je pense que mon exemple de code est simple et que mes réponses sont suffisamment explicites. Je ne pense pas qu'en rendant les questions trop explicites, cela changerait les choses pour le mieux. Mais si vous avez une suggestion pour améliorer les questions, n'hésitez pas à m'en faire part.