Вычитание недискретных указательных адресов
Вычитание непересекающихся адресов указателей, определенных в C? В С++?
Вот пример:
void* p = malloc(64);
int* one = (int*)((char*)p);
int* two = (int*)((char*)p + 7);
printf("%x %x %d %d\n", one, two, sizeof(int), two - one);
Идеальная ссылка.
Я получаю вывод 8a94008 8a9400f 4 1
, поэтому кажется, что он разделяет и обрезает остаток. Определено ли поведение?
Ответы
Ответ 1
Это поведение undefined в соответствии с 5.7.6:
Когда два указателя на элементы одного и того же объекта массива вычитаются, результатом является разность индексов двух элементов массива. [...] Если оба указателя не указывают на элементы одного и того же объекта массива или один за последним элементом объекта массива, поведение undefined.
В вашем коде указатель two
не указывает на элемент того же массива int
как указатель one
. Фактически, он не указывает на какой-либо элемент массива из p
, поскольку он указывает на "середину" одного из элементов (который сам по себе является undefined).
Ответ 2
При некоторых предположениях 1 в C третья строка:
int* two = (int*)((char*)p + 7);
уже вызывает поведение undefined, потому что указатель p неправильно выровнен для типа, на который он ссылается 2.
1 Предполагается, что требования к выравниванию для типа int будут выше, чем для типа char. Это справедливо для большинства современных архитектур. Так как все выравнивания должны иметь силу двух 3 а значение 7 - нет, добавление этого значения в указатель p не может создать указатель с выравниванием, которое является настолько строгим, как требование выравнивания для тип int.
2 (Цитируется по: ISO/IEC 9899: 201x 6.3.2.3 Указатели 7.)
Указатель на тип объекта может быть преобразован в указатель на другой тип объекта. Если
полученный указатель неправильно выровнен для ссылочного типа, поведение
undefined.
3 (Цитируется по: ISO/IEC 9899: 201x 6.2.8 Выравнивание объектов 4.)
Все действующие
значение выравнивания должно быть неотрицательной интегральной степенью двух.