Стандарт C99 гласит:Разница указателей между членами структуры?
Когда два указателя вычитаются, то оба указывают на элементы одного и того же объекта массива, или за последним элементом массива объектов
Рассмотрим следующее код:
struct test {
int x[5];
char something;
short y[5];
};
...
struct test s = { ... };
char *p = (char *) s.x;
char *q = (char *) s.y;
printf("%td\n", q - p);
Это явно нарушает вышеуказанное правило, поскольку p
и q
указатели указывают на разные «объекты массива», и, согласно правилу, разница q - p
не определена.
Но на практике почему такое поведение всегда приводит к неопределенному поведению? В конце концов, элементы структуры выложены последовательно (так же, как элементы массива), с любым возможным дополнением между членами. Правда, количество отступов будет различаться в разных реализациях, что повлияет на результат вычислений, но почему этот результат должен быть «неопределенным»?
Вопрос в том, можем ли мы предположить, что стандарт просто «неосведомлен» по этой проблеме или есть веская причина не расширять это правило? Не удалось ли перевести вышеуказанное правило в «, оба должны указывать на элементы одного и того же объекта массива или на элементы той же структуры»?
Мое единственное подозрение - это сегментированные архитектуры памяти, в которых члены могут оказаться в разных сегментах. Это так?
Я также подозреваю, что именно по этой причине GCC определяет свой собственный __builtin_offsetof
, чтобы иметь «соответствие стандартам» определение макроса offsetof
.
EDIT:
Как уже отмечалось, арифметика недействительных указателей не допускается стандартом. Это расширение GNU, которое выдает предупреждение только тогда, когда GCC передается -std=c99 -pedantic
. Я заменяю указатели void *
указателями char *
.
и в вашем примере арифметика указателя на void запрещена в любом случае. если типы arent одинаковы, как вы можете их вычесть? – camelccc
Вы правы, что арифметика на указателях пустот не допускается стандартом, и это расширение GNU. Представьте, что оба указателя являются «char *». –
gcc разрешает арифметику указателя на 'void *' путем обработки 'sizeof (void)' как 1 (так же, как 'char *'). Так что для целей вашего вопроса это не имеет значения. –