Сегодня я обнаружил тревожное поведение при экспериментировании с битовыми полями. Ради обсуждения и простоты, вот пример программы:
#include <stdio.h>
struct Node
{
int a:16 __attribute__ ((packed));
int b:16 __attribute__ ((packed));
unsigned int c:27 __attribute__ ((packed));
unsigned int d:3 __attribute__ ((packed));
unsigned int e:2 __attribute__ ((packed));
};
int main (int argc, char *argv[])
{
Node n;
n.a = 12345;
n.b = -23456;
n.c = 0x7ffffff;
n.d = 0x7;
n.e = 0x3;
printf("3-bit field cast to int: %d\n",(int)n.d);
n.d++;
printf("3-bit field cast to int: %d\n",(int)n.d);
}
Программа намеренно вызывает переполнение 3-битного битового поля. Вот (правильный) вывод при компиляции с использованием "g ++ -O0":
3-битное поле приведено к int: 7
3-битное поле приведено к int: 0
Вот вывод при компиляции с использованием "g ++ -O2" (и -O3):
3-битное поле приведено к int: 7
3-битное поле приведено к int: 8
Проверяя сборку последнего примера, я нашел это:
movl $7, %esi
movl $.LC1, %edi
xorl %eax, %eax
call printf
movl $8, %esi
movl $.LC1, %edi
xorl %eax, %eax
call printf
xorl %eax, %eax
addq $8, %rsp
Оптимизации только что вставили "8", предполагая 7 + 1 = 8, когда на самом деле число переполняется и равно нулю.
К счастью, код, который меня волнует, насколько я знаю, не переполняется, но эта ситуация пугает меня - это известная ошибка, особенность или это ожидаемое поведение? Когда я могу ожидать, что gcc будет прав по этому поводу?
Редактировать (повторно: подписано / не подписано):
Он рассматривается как неподписанный, потому что он объявлен как неподписанный. Объявив его как int, вы получите вывод (с O0):
3-битное поле приведено к int: -1
3-битное поле приведено к int: 0
Еще более забавная вещь случается с -O2 в этом случае:
3-битное поле приведено к int: 7
3-битное поле приведено к int: 8
Я признаю, что атрибут - это подозрительная вещь; в этом случае меня беспокоит разница в настройках оптимизации.