Pour montrer le sujet que je vais utiliser le mot C, mais la même macro peut également être utilisé en C++ (avec ou sans struct
), ce qui soulève la même question.
Je suis venu avec cette macro
#define STR_MEMBER(S,X) (((struct S*)NULL)->X, #X)
Son but est d'avoir des chaînes de caractères (const char*
) d'un membre d'un struct
, de sorte que, si le membre n'existe pas, la compilation échoue. Un minimum d'exemple d'utilisation:
#include <stdio.h>
struct a
{
int value;
};
int main(void)
{
printf("a.%s member really exists\n", STR_MEMBER(a, value));
return 0;
}
Si value
n'étaient pas membre de l' struct a
, le code ne compile pas, et c'est ce que je voulais.
L'opérateur virgule doit évaluer l'opérande de gauche, puis jeter le résultat de l'expression (si il y en a un), de sorte que ma compréhension est que souvent cet opérateur est utilisé lors de l'évaluation de l'opérande de gauche a des effets secondaires.
Dans ce cas, cependant, il n'y a pas (prévue) des effets secondaires, mais bien sûr qu'il fonctionne le forum le compilateur n'a pas fait de produire du code qui évalue l'expression, car, sinon, il aurait accès à un struct
situé à l' NULL
et une erreur de segmentation se produirait.
Gcc/g++ 6.3 et 4.9.2 n'a jamais produit que de code dangereux, même avec -O0
,, comme si elles l'ont toujours été en mesure de "voir" que l'évaluation n'a pas d'effets secondaires et peut donc être ignoré.
L'ajout d' volatile
de la macro (par exemple, parce que l'accès, l'adresse mémoire est le côté souhaité effet) était jusqu'à présent le seul moyen de déclencher l'erreur de segmentation.
Donc, la question: est-il rien dans les langages C et C++ standard qui garantit que les compilateurs évitera toujours de réelle évaluation de l'opérande gauche de l'opérateur virgule lorsque le compilateur peut être sûr que l'évaluation n'a pas d'effets secondaires?
Notes et fixation
Je ne demande pas un jugement sur la macro que c'est et à l'occasion de l'utiliser ou de le rendre meilleur. Pour les fins de cette question, la macro est mauvais si et seulement si elle évoque un comportement indéterminé - c'est à dire, si et seulement si il est risqué parce que les compilateurs sont autorisées à produire de l ' "évaluation" du code, même lorsque cela n'a pas d'effets secondaires.
J'ai déjà deux évident correctifs à l'esprit: "chosification" de l' struct
et à l'aide de offsetof
. L'ancien a besoin d'une mémoire accessible zone aussi grande que le plus grand struct
- nous utiliser comme premier argument de STR_MEMBER
(par exemple, peut-être un statique de l'union pourrait faire...). Ce dernier devrait fonctionner parfaitement: il donne un décalage qui ne nous intéresse pas, et évite le problème de l'accès - en effet, je suis en supposant que gcc, parce que c'est le compilateur que j'utilise (d'où l'étiquette), et que son offsetof
intégré se comporte.
Avec l' offsetof
corrigé de la macro devient
#define STR_MEMBER(S,X) (offsetof(struct S,X), #X)
La rédaction volatile struct S
au lieu de struct S
n'entraîne pas l'erreur de segmentation.
Des Suggestions sur d'autres possibles "correctifs" sont aussi les bienvenus.
Ajout de la note
En réalité, la véritable utilisation de cas était en C++ dans une mémoire statique struct
. Cela semble aller pour le mieux dans le C++, mais dès que j'ai essayé C avec un code plus proche de l'original au lieu d'une bouillie pour cette question, j'ai réalisé que C n'est pas du tout satisfait de cette:
error: initializer element is not constant
C veut la struct être initializable au moment de la compilation, au lieu de C++ c'est bien aussi.