La section 6.5.9 de la C standard concernant le ==
y !=
les opérateurs déclarent ce qui suit :
2 L'une des situations suivantes s'applique :
- les deux opérandes sont de type arithmétique ;
- les deux opérandes sont des pointeurs vers des versions qualifiées ou non qualifiées de types compatibles ;
- un opérande est un pointeur vers un type d'objet et l'autre est un pointeur vers une version qualifiée ou non qualifiée de void ; ou
- un opérande est un pointeur et l'autre est une constante de type pointeur nul.
...
6 Deux pointeurs sont égaux si et seulement si les deux sont des pointeurs nuls, les deux sont des pointeurs vers le même objet (y compris un pointeur vers un objet et un sous-objet à son début) ou une fonction, les deux sont des pointeurs vers l'avant-dernier élément du même objet de type tableau, ou l'un est un l'un est un pointeur vers la fin d'un objet du tableau et l'autre est un l'autre est un pointeur vers le début d'un autre objet tableau qui se trouve être immédiatement après le premier objet de tableau dans l'espace d'adressage. 109)
7 Aux fins de ces opérateurs, un pointeur sur un objet qui n'est pas qui n'est pas un élément d'un tableau se comporte de la même manière qu'un pointeur vers le premier élément du tableau. élément d'un tableau de longueur un avec le type de l'objet comme type d'élément. type d'élément.
Note de bas de page 109 :
109) Deux objets peuvent être adjacents en mémoire parce qu'il s'agit d'éléments adjacents éléments adjacents d'un tableau plus grand ou des membres adjacents d'une structure sans aucun de remplissage entre eux, ou parce que l'implémentation a choisi de les placer ainsi, même s'ils ne sont pas liés . Si des opérations antérieures de pointeurs invalides invalides (comme les accès en dehors des limites d'un tableau) ont donné lieu à des comportement indéfini, les comparaisons suivantes produisent également un comportement indéfini.
Cela semble indiquer que vous pourriez faire ce qui suit :
int a;
int b;
printf("a precedes b: %d\n", (&a + 1) == &b);
printf("b precedes a: %d\n", (&b + 1) == &a);
Cela devrait être légal puisque nous utilisons une adresse un élément après la fin d'un tableau (qui dans ce cas est un objet unique traité comme un tableau de taille 1) sans le déréférencer. Plus important encore, l'une de ces deux instructions serait nécessaire pour afficher 1
si une variable suit immédiatement l'autre en mémoire.
Cependant, les tests n'ont pas semblé le confirmer. Étant donné le programme de test suivant :
#include <stdio.h>
struct s {
int a;
int b;
};
int main()
{
int a;
int b;
int *x = &a;
int *y = &b;
printf("sizeof(int)=%zu\n", sizeof(int));
printf("&a=%p\n", (void *)&a);
printf("&b=%p\n", (void *)&b);
printf("x=%p\n", (void *)x);
printf("y=%p\n", (void *)y);
printf("addr: a precedes b: %d\n", ((&a)+1) == &b);
printf("addr: b precedes a: %d\n", &a == ((&b)+1));
printf("pntr: a precedes b: %d\n", (x+1) == y);
printf("pntr: b precedes a: %d\n", x == (y+1));
printf(" x=%p, &a=%p\n", (void *)(x), (void *)(&a));
printf("y+1=%p, &b+1=%p\n", (void *)(y+1), (void *)(&b+1));
struct s s1;
x=&s1.a;
y=&s1.b;
printf("addr: s.a precedes s.b: %d\n", ((&s1.a)+1) == &s1.b);
printf("pntr: s.a precedes s.b: %d\n", (x+1) == y);
return 0;
}
Le compilateur est gcc 4.8.5, le système est CentOS 7.2 x64.
Avec -O0
j'obtiens le résultat suivant :
sizeof(int)=4
&a=0x7ffe9498183c
&b=0x7ffe94981838
x=0x7ffe9498183c
y=0x7ffe94981838
addr: a precedes b: 0
addr: b precedes a: 0
pntr: a precedes b: 0
pntr: b precedes a: 1
x=0x7ffe9498183c, &a=0x7ffe9498183c
y+1=0x7ffe9498183c, &b+1=0x7ffe9498183c
addr: s.a precedes s.b: 1
Nous pouvons voir ici qu'un int
est de 4 octets et que l'adresse de a
est 4 octets après l'adresse de b
et que x
contient l'adresse de a
tandis que y
contient l'adresse de b
. Cependant, la comparaison &a == ((&b)+1)
évalue à false alors que la comparaison (x+1) == y
est évalué à true. Je m'attendrais à ce que les deux soient vrais puisque les adresses comparées semblent identiques.
Avec -O1
je reçois ça :
sizeof(int)=4
&a=0x7ffca96e30ec
&b=0x7ffca96e30e8
x=0x7ffca96e30ec
y=0x7ffca96e30e8
addr: a precedes b: 0
addr: b precedes a: 0
pntr: a precedes b: 0
pntr: b precedes a: 0
x=0x7ffca96e30ec, &a=0x7ffca96e30ec
y+1=0x7ffca96e30ec, &b+1=0x7ffca96e30ec
addr: s.a precedes s.b: 1
pntr: s.a precedes s.b: 1
Maintenant, les deux comparaisons donnent un résultat faux, même si (comme avant) l'adresse comparée semble être la même.
Cela semble indiquer que comportement indéfini mais d'après la façon dont j'ai lu le passage ci-dessus, il semble que cela devrait être autorisé.
Notez également que la comparaison des adresses d'objets adjacents de même type dans un fichier de type struct
imprime le résultat attendu dans tous les cas.
Est-ce que je lis mal quelque chose ici concernant ce qui est autorisé (ce qui signifie que c'est UB), ou est-ce que cette version de gcc est non-conforme dans ce cas ?