В разделе 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-несоответствия в этом случае?