Действительно ли следующий код 100% является портативным?
int a=10;
size_t size_of_int = (char *)(&a+1)-(char*)(&a); // No problem here?
std::cout<<size_of_int;// or printf("%zu",size_of_int);
P.S.: вопрос только для изучения цели. Поэтому не давайте ответы как Use sizeof()
и т.д.
Из ANSI-ISO-IEC 14882-2003, стр.87 (c++03):
"75) Другой способ подхода к указателю арифметика заключается в том, чтобы сначала преобразовать указатель(и) в символьный указатель(и): В этой схеме интегральное значение выражение, прибавляемое или вычитаемое из преобразованного указателя, сначала умножается на размер объекта на который первоначально указывал указатель, и полученный указатель преобразуется обратно к исходному типу. Для указателя вычитание, результат разность между символьными указателями аналогичным образом делится на размер объекта, на который первоначально указывали на который первоначально указывал."
Это, кажется, предполагает, что разница указателей равна размеру объекта.
Если мы уберем UB'ness из инкремента указателя на скаляр a и превратим a в массив:
int a[1];
size_t size_of_int = (char*)(a+1) - (char*)(a);
std::cout<<size_of_int;// or printf("%zu",size_of_int);
Тогда это выглядит нормально. Положения о требованиях к выравниванию согласуются со сноской, если требования к выравниванию всегда кратны размеру объекта.
UPDATE: Interesting. Как многие из вас, вероятно, знают, GCC позволяет задавать явное выравнивание типов в качестве расширения. Но я не могу сломать им метод OP "sizeof", потому что GCC отказывается его компилировать:
#include <stdio.h>
typedef int a8_int __attribute__((aligned(8)));
int main()
{
a8_int v[2];
printf("=>%d\n",((char*)&v[1]-(char*)&v[0]));
}
Сообщение: error: alignment of array elements is greater than element size
.
Да, он дает вам эквивалент sizeof (a)
, но с использованием ptrdiff_t
вместо типа size_t
.
&a+1
приведет к неопределенному поведению согласно стандарту C++ 5.7/5:
Когда выражение, имеющее интегральный тип, складывается с указателем или вычитается из него, результат имеет тип операнда указателя. <...> Если и операнд указателя, и результат указывают на элементы одного и того же объекта массива или на один элемент после последнего элемента объекта массива, оценка не должна приводить к переполнению; в противном случае поведение не определено.
&a+1
нормально согласно 5.7/4:
Для целей этих операторов указатель на объект, не являющийся массивом, ведет себя так же, как указатель на первый элемент массива длины один с одним последним элементом. элемент массива длины один с типом объекта как типом элемента.
Это означает, что 5.7/5 можно применять без UB. И, наконец, замечание 75 из 5.7/6, как @Luther Blissett
отметил в своем ответе, говорит, что код в вопросе валиден.
В производственном коде вы должны использовать sizeof
вместо этого. Но стандарт Си++ не гарантирует, что sizeof(int)
будет давать 4 на каждой 32-битной платформе.
Нет. Этот код не будет работать так, как вы ожидаете, на каждой платформе. По крайней мере, теоретически может существовать платформа, например, с 24-битные целые числа (= 3 байта), но 32-битное выравнивание. Такое выравнивание не является нетипичным для (старых или более простых) платформ. Тогда ваш код вернет 4, а sizeof (int) вернет 3.
Но я не знаю реального оборудования, которое ведет себя подобным образом. На практике ваш код будет работать на большинстве или на всех платформах.
Это не переносится на 100% по следующим причинам:
int a[1];
и тогда a+1
станет окончательно валидным. int
, size_of_int
не будет содержать правильного ответа. Я не уверен, что вышесказанное справедливо для C++.
Здесь были дебаты по аналогичному вопросу.
См. комментарии к моему ответу на этот вопрос для некоторых указаний на то, почему это не только не переносимо, но и является неопределенным поведением в стандарте.
Вероятно, это определено реализацией.
Я могу представить (гипотетическую) систему, в которой sizeof (int) меньше, чем выравнивание по умолчанию.
Можно с уверенностью сказать, что size_of_int> = sizeof (int)
Приведенный выше код будет переносимо вычислять sizeof (int)
на целевой платформе, но последнее определяется реализацией - вы получите разные результаты на разных платформах.