Альтернативный способ вычислить размер типа с помощью адресной арифметики с указателями

Действительно ли следующий код 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() и т.д.

8
задан Prasoon Saurav 3 August 2010 в 00:29
поделиться

9 ответов

Из 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.

13
ответ дан 5 December 2019 в 07:10
поделиться

Почему не просто:

size_t size_of_int = sizeof(int);
1
ответ дан 5 December 2019 в 07:10
поделиться

Да, он дает вам эквивалент sizeof (a) , но с использованием ptrdiff_t вместо типа size_t .

0
ответ дан 5 December 2019 в 07:10
поделиться

&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-битной платформе.

4
ответ дан 5 December 2019 в 07:10
поделиться

Нет. Этот код не будет работать так, как вы ожидаете, на каждой платформе. По крайней мере, теоретически может существовать платформа, например, с 24-битные целые числа (= 3 байта), но 32-битное выравнивание. Такое выравнивание не является нетипичным для (старых или более простых) платформ. Тогда ваш код вернет 4, а sizeof (int) вернет 3.

Но я не знаю реального оборудования, которое ведет себя подобным образом. На практике ваш код будет работать на большинстве или на всех платформах.

2
ответ дан 5 December 2019 в 07:10
поделиться

Это не переносится на 100% по следующим причинам:

  1. Edit: Лучше использовать int a[1]; и тогда a+1 станет окончательно валидным.
  2. &a вызывает неопределенное поведение для объектов класса хранения регистров.
  3. В случае ограничений выравнивания, которые больше или равны размеру типа int, size_of_int не будет содержать правильного ответа.

Оговорка:

Я не уверен, что вышесказанное справедливо для C++.

2
ответ дан 5 December 2019 в 07:10
поделиться

Здесь были дебаты по аналогичному вопросу.

См. комментарии к моему ответу на этот вопрос для некоторых указаний на то, почему это не только не переносимо, но и является неопределенным поведением в стандарте.

0
ответ дан 5 December 2019 в 07:10
поделиться

Вероятно, это определено реализацией.

Я могу представить (гипотетическую) систему, в которой sizeof (int) меньше, чем выравнивание по умолчанию.

Можно с уверенностью сказать, что size_of_int> = sizeof (int)

1
ответ дан 5 December 2019 в 07:10
поделиться

Приведенный выше код будет переносимо вычислять sizeof (int) на целевой платформе, но последнее определяется реализацией - вы получите разные результаты на разных платформах.

0
ответ дан 5 December 2019 в 07:10
поделиться
Другие вопросы по тегам:

Похожие вопросы: