Альтернативный способ вычисления размера типа с использованием арифметики указателей - PullRequest
8 голосов
/ 02 августа 2010

Является ли следующий код переносимым на 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() и т. Д.

Ответы [ 9 ]

13 голосов
/ 02 августа 2010

Из 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);

Тогда это выглядит хорошо. Положения о требованиях к выравниванию соответствуют сноске, если требования по выравниванию всегда делятся на размер объекта.

ОБНОВЛЕНИЕ: Интересно. Как многие из вас, вероятно, знают, 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.

4 голосов
/ 02 августа 2010

&a+1 приведет к неопределенному поведению в соответствии со стандартом C ++ 5.7 / 5:

Когда выражение, имеющее целочисленный тип, добавляется или вычитается из указателя, результатимеет тип операнда указателя.<...> Если и операнд-указатель, и результат указывают на элементы одного и того же объекта массива или один после последнего элемента объекта массива, вычисление не должно приводить к переполнению; в противном случае поведение не определено .

&a+1 в порядке в соответствии с 5.7 / 4:

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

Это означает, что 5.7 / 5 может применяться без UB.И, наконец, замечание 75 из 5.7 / 6, отмеченное в его ответе @Luther Blissett, говорит о том, что код в вопросе действителен.


В рабочем коде вы должны использовать sizeof вместо этого.Но стандарт C ++ не гарантирует, что sizeof(int) приведет к 4 на каждой 32-битной платформе.

2 голосов
/ 02 августа 2010

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

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

Отказ от ответственности:

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

2 голосов
/ 02 августа 2010

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

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

1 голос
/ 02 августа 2010

Это, вероятно, определяется реализацией.

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

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

1 голос
/ 02 августа 2010

Почему бы просто:

size_t size_of_int = sizeof(int);
0 голосов
/ 02 августа 2010

Был спор по подобному вопросу .

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

0 голосов
/ 02 августа 2010

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

0 голосов
/ 02 августа 2010

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

...