Разница между uint16_t и uint32_t - PullRequest
1 голос
/ 04 июля 2019

В моем коде нет разницы между uint16_t и uint32_t. Почему?

Я использую Raspbian на RasPi с ARMv7 (32 бита).

root@zentrale:/src# uname -a
Linux zentrale 4.19.42-v7+ #1219 SMP Tue May 14 21:20:58 BST 2019 armv7l GNU/Linux

Это код:

void main()
{

        uint16_t wert1;
        uint32_t wert2;

        int i;

        wert1=2;
        wert2=2;
        for (i=0; i<33;i++)
        {
                printf("i: %2d\tLShifted wert1: %10u\t",i,wert1 << i);
                printf("RShifted wert1: %10u\t",wert1 >> i);
                printf("LShifted wert2: %10u\t",wert2 << i);
                printf("RShifted wert2: %10u\n",wert2 >> i);
        }
exit(0);
}

Это разделённый вывод:

i:  0   LShifted wert1:          2      RShifted wert1:          2      LShifted wert2:          2      RShifted wert2:          2
i:  1   LShifted wert1:          4      RShifted wert1:          1      LShifted wert2:          4      RShifted wert2:          1
[...]
i: 14   LShifted wert1:      32768      RShifted wert1:          0      LShifted wert2:      32768      RShifted wert2:          0
i: 15   LShifted wert1:      65536      RShifted wert1:          0      LShifted wert2:      65536      RShifted wert2:          0
i: 16   LShifted wert1:     131072      RShifted wert1:          0      LShifted wert2:     131072      RShifted wert2:          0
[...]

Я бы ожидал, что wert1 будет 16 битами и достигнет нуля со значением i = 15, как следует из названия, это 16 битов.

Вместо этого нет никакой разницы в этих двух переменных.

Я нашел ссылку на максимальное значение uint16_t в Raspian (см. https://raspberry -projects.com / pi / program-in-c / memory / variable )

Так почему нет разницы?

Большое спасибо!

Ответы [ 2 ]

2 голосов
/ 04 июля 2019

Оба операнда << будут подвергаться целочисленным преобразованиям , т.е. C11 6.3.11p2 :

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

  • Объект или выражение с целочисленным типом (кроме int или unsigned int), чей целочисленный ранг преобразования меньше или равен рангу int и unsigned int.
  • Битовое поле типа _Bool, int, sign int или unsigned int.

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

Поскольку int на вашей платформе имеет ширину 32 бита, все значения uint16_t представляются int. uint32_t преобразуется в unsigned int!

Теперь поведение их обоих кажется равным , потому что GCC гарантирует большую часть этого! Вся подписанная арифметика на всех архитектурах, поддерживаемых GCC, использует дополнение 2; и дополнительно GCC не считает поведение << для чисел со знаком неопределенным в случаях, когда бит знака изменяется .

Тем не менее, все еще не определено (даже в GCC), что происходит, если ширина сдвига больше или равна ширине операнда (в данном случае 32 бита), поэтому << 32 и << 33 будет иметь неопределенное поведение.

Кроме этого, как правило, стандарт C говорит, что поведение не определено, если целое положительное число со знаком смещено влево, так что бит знака изменяется! Это происходит, когда вы сдвигаете uint16_t влево на столько бит, что он изменит бит сдвига int. Следовательно,

(uint16_t)0xFFFF << 16

имеет неопределенное поведение на 32-битной платформе, поскольку самый верхний бит смещен в знаковый бит int, тогда как

(uint32_t)0xFFFF << 16

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

1 голос
/ 04 июля 2019

Нет разницы из-за Целочисленных рекламных акций

Вы действительно должны пройти этот превосходный пост по теме Неявные правила продвижения типа

Предполагая 4 байта int, перед любой арифметической операцией uint16_t преобразуется в int со знаком, а затем выполняется операция.

Сдвиг влево на отрицательном числе не определен. Но в этом случае это число не может быть отрицательным. Таким образом, вы получите тот же вывод, что и uint32_t

Для правильной работы вы должны типизировать выходные данные левого сдвига.

Кроме того, вы запускаете цикл до i<33. На i==32 вы будете иметь неопределенное поведение для uint32_t, а на i==31 вы будете иметь неопределенное поведение для целого числа со знаком для uint16_t

 printf("i: %2d\tLShifted wert1: %10u\t",i,  (uint16_t)(wert1 << i);
...