Сегодня я обнаружил тревожное поведение при экспериментировании с битовыми полями. Для обсуждения и простоты здесь приведен пример программы:
#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 будет прав по этому поводу?
Изменить (re: signed/unsigned):
Он обрабатывается как unsigned, потому что он объявлен как unsigned. Объявляя его как int, вы получаете результат (с O0):
3-битное поле, переданное в int: -1
3-битное поле, отличное от int: 0
В этом случае случается еще более смешная ситуация с -O2:
3-битное поле, отличное от int: 7
3-битное поле, переданное в int: 8
Я признаю, что атрибут - это опасная вещь; в этом случае это разница в настройках оптимизации, о которых я беспокоюсь.