sizeof (bitfield_type), допустимый в ANSI C? - PullRequest
4 голосов
/ 17 сентября 2008
struct foo { unsigned x:1; } f;
printf("%d\n", (int)sizeof(f.x = 1));

Каков ожидаемый результат и почему? Взятие размера битового поля напрямую не допускается. Но, используя оператор присваивания, кажется, что мы все еще можем взять размер типа битового поля.

Что такое "размер битового поля в байтах"? Это размер блока хранения, содержащего битовое поле? Это число бит, занятое bf, округленное до ближайшего числа байтов?

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

Ответы [ 13 ]

4 голосов
/ 17 сентября 2008

Вы правы, целочисленные продвижения не применяются к операнду sizeof:

Целочисленные преобразования применяются только: как часть обычных арифметических преобразований, к определенным выражениям аргументов, к операндам унарных операторов +, - и ~ и к обоим операндам операторов сдвига, как указано соответствующие подпункты.

Реальный вопрос в том, имеют ли битовые поля свои собственные типы.

Джозеф Майерс сказал мне:

Заключение от DR C90 было то, что битовые поля имеют свои собственные типы, и от DR C99 должен был оставить, есть ли у них свои собственные типы, определенные реализацией, и GCC следует за DR C90 и поэтому назначение имеет тип int: 1 и не является продвигается как операнд sizeof.

Это обсуждалось в Отчете о дефектах # 315 .

Подводя итог: ваш код легален, но определяется реализацией.

3 голосов
/ 17 сентября 2008

Стандарт C99 ( PDF последней версии ) говорит в разделе 6.5.3.4 о sizeof ограничениях:

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

Это означает, что применение sizeof к выражению присваивания разрешено.

6.5.16.3 говорит:

Типом выражения присваивания является тип левого операнда ...

6.3.1.1.2 говорит о целочисленных акциях:

Следующее может использоваться в выражении везде, где могут использоваться int или unsigned int:

  • ...
  • Битовое поле типа _Bool, int, signed int или unsigned int.

Если int может представлять все значения исходного типа, значение преобразуется в int; в противном случае он преобразуется в unsigned int.

Итак, ваша тестовая программа должна вывести размер int, т.е. sizeof(int).

Есть ли компилятор, который не подходит к этому?

1 голос
/ 17 сентября 2008

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

sizeof(f.x = 1) вернет размер типа выражения. Поскольку C не имеет реального «типа битового поля», выражение (здесь: выражение присваивания) обычно получает тип базового типа битового поля, в вашем примере unsigned int, но компилятор может использовать внутренний тип поменьше (в данном случае, вероятно, unsigned char, потому что он достаточно велик для одного бита).

0 голосов
/ 17 сентября 2008

CL, я уже видел ваши цитаты и согласен, что они полностью актуальны, но даже после прочтения я не был уверен, определен ли код.

6.3.1.1.2 говорит о целочисленных акциях:

Да, но целочисленные правила продвижения применяются только в том случае, если продвижение действительно проводится. Я не думаю, что мой пример требует продвижения по службе. Точно так же, если вы делаете

char ch;
sizeof ch;

... тогда ch также не повышен.

Я думаю, что здесь мы имеем дело непосредственно с типом битового поля.

Я также видел вывод 1 gcc, в то время как многие другие компиляторы (и даже другие версии gcc) этого не делают. Это не убеждает меня в том, что код является недопустимым , потому что размер также может быть достаточно определен реализацией, чтобы сделать результат несовместимым для нескольких компиляторов.

Однако меня смущает вопрос о том, может ли код быть неопределенным, потому что ничто в стандарте, похоже, не указывает, как обрабатывается случай sizeof bitfield.

0 голосов
/ 17 сентября 2008

Слушай, я прекрасно понимаю, что я делаю с трюком с заданиями.

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

В частности, я пробовал различные компиляторы, которые дают мне sizeof (int) вместо sizeof (char) EVEN, если я применяю это к битовому полю, имеющему только один бит.

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

Тем не менее, я действительно хочу знать, является ли конструкция ГАРАНТИРОВАННОЙ для работы и дает НЕКОТОРЫЕ значение.

0 голосов
/ 17 сентября 2008

во втором примере, если вы определите свою структуру как

struct foo { unsigned x:12} f;

, а затем записать значение типа 1 в f.x - оно использует 2 байта из-за выравнивания. Если вы делаете назначение, как

f.x = 1;

и это возвращает назначенное значение. Это очень похоже на

int a, b, c;
a = b = c = 1;

где присвоение оценивается справа налево. c = 1 присваивает 1 переменной c, и это присвоение возвращает присвоенное значение и присваивает его b (и т. д.), пока 1 не будет присвоено a

равно

a = ( b = ( c = 1 ) )

в вашем случае sizeof получает размер вашего присвоения, который NOT битовое поле, но назначенная ему переменная.

sizeof ( f.x = 1)

не возвращает размер битовых полей, но присвоение переменной, которая является 12-битным представлением 1 (в моем случае), и поэтому sizeof () возвращает 2 байта (из-за 8-битного aligment)

0 голосов
/ 17 сентября 2008

не является выражением, это присваивание и, следовательно, возвращает назначенное значение. В этом случае размер этого значения зависит от переменной, которой оно было присвоено.

Во-первых, это IS выражение, содержащее оператор присваивания.

Во-вторых, я хорошо знаю, что происходит в моем примере:)

тогда sizeof (f.x = 1) вернет 2 байта (опять же из-за 8-битного выравнивания)

Где ты это взял? Это то, что происходит на конкретном компиляторе, который вы пробовали, или эта семантика прописана в стандарте? Потому что я не нашел таких заявлений. Я хочу знать, гарантированно ли работает конструкция вообще.

0 голосов
/ 17 сентября 2008

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

Итак, вы заявляете, что поведение не определено, то есть имеет ту же степень легальности, что и "* (int *) 0 = 0;", и компиляторы могут решить не обрабатывать это разумно?

Вот что я пытаюсь выяснить. Вы предполагаете, что это не определено пропуском, или есть что-то, что явно объявляет это незаконным?

0 голосов
/ 17 сентября 2008

sizeof (1) - это, вероятно, размер целого числа на платформе, на которой вы компилируете, вероятно, 4 или 8 байтов.

Обратите внимание, что я НЕ беру sizeof (1), который по сути является sizeof (int). Посмотрите внимательно, я беру sizeof (f.x = 1), который должен быть sizeof (bitfield_type).

Я хотел бы видеть ссылку на что-то, что говорит мне, является ли конструкция допустимой. В качестве дополнительного бонуса было бы неплохо, если бы он сказал мне, какой результат ожидается.

gcc определенно не согласен с утверждением, что sizeof (bitfield_type) должен совпадать с sizeof (int), но только на некоторых платформах.

0 голосов
/ 17 сентября 2008

(f.x = 1)

не является выражением, это присваивание и, следовательно, возвращает присвоенное значение. В этом случае размер этого значения зависит от переменной, которой оно было присвоено.

unsigned x:1

имеет 1 бит и его размер возвращает 1 байт (8-битное выравнивание)

Если бы вы использовали

unsigned x:12

тогда sizeof (f.x = 1) вернет 2 байта (опять же из-за 8-битного выравнивания)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...