В разделе 6.5.9 раздела C в отношении операторов == и != указано следующее:
2 Должно быть выполнено одно из следующих условий:
- оба операнда имеют арифметический тип;
- оба операнда являются указателями на квалифицированные или неквалифицированные версии совместимых типов;
- один операнд является указателем на тип объекта, а другой - указателем на квалифицированную или неквалифицированную версию void; или
- один операнд является указателем, а другой - константой нулевого указателя.
...
6 Два указателя сравнивают одинаковые, если и только если оба являются нулевыми указателями, оба являются указателями на один и тот же объект (включая указатель на объект и подобъектом в начале) или функцией, оба являются указателями на один за последним элементом одного и того же объекта массива, или один - это указатель на один конец конца одного объекта массива, а другой - указатель на начало другого объекта массива, который происходит с немедленно следуйте за первым объектом массива в адресном пространстве. 109)
7 Для целей этих операторов указатель на объект, который не элемент массива ведет себя так же, как указатель на первый элемент массива длиной один с типом объекта как его тип элемента.
Сноска 109:
109) Два объекта могут быть смежными в памяти, поскольку они смежны элементы большего массива или смежные элементы структуры без между ними, или , потому что реализация выбрала место их так, хотя они не связаны. Если предыдущий неверный указатель операции (такие как доступ к границам внешнего массива), созданный undefinedповедение, последующие сравнения также приводят к поведению undefined.
Это, похоже, указывает, что вы можете сделать следующее:
int a;
int b;
printf("a precedes b: %d\n", (&a + 1) == &b);
printf("b precedes a: %d\n", (&b + 1) == &a);
Это должно быть законным, так как мы используем адрес один элемент за концом массива (который в этом случае является единственным объектом, обработанным как массив размером 1), без его разыменования. Что еще более важно, один из этих двух операторов потребуется для вывода 1, если одна переменная сразу же следует за другой в памяти.
Однако тестирование, похоже, не повлияло на это. Учитывая следующую тестовую программу:
#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;
}
Компилятор gcc 4.8.5, система CentOS 7.2 x64.
С -O0, я получаю следующий вывод:
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
Мы можем видеть здесь, что int составляет 4 байта и что адрес a равен 4 байтам за адресом b, а x содержит адрес a, а y имеет адрес b. Однако сравнение &a == ((&b)+1) оценивается как false, а сравнение (x+1) == y равно true. Я ожидал бы, что оба будут истинными, поскольку сравниваемые адреса выглядят идентичными.
С -O1 я получаю следующее:
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
Теперь оба сравнения оцениваются как false, хотя (как и раньше) сравниваемый адрес выглядит одинаковым.
Это похоже на undefined поведение, но в зависимости от того, как я прочитал вышеприведенный отрывок, кажется, это должно быть разрешено.
Обратите внимание, что сравнение адресов соседних объектов одного и того же типа в struct выводит ожидаемый результат во всех случаях.
Я что-то неправильно понимаю о том, что разрешено (что означает UB), или это версия gcc-несоответствия в этом случае?