Типизация с unsigned и присвоение переменной разного байта - PullRequest
0 голосов
/ 20 сентября 2019

Целое число со знаком присваивается целому числу без знака.Поведение выглядит немного странно при использовании целого числа без знака с разным размером байта.

Код 1:

    #include <stdio.h>
    typedef unsigned long   uint32;
    typedef unsigned short  uint16;
    typedef signed short    sint16;

    sint16 rawTCU = -100;

    int main()
    {
        uint32 _tmpSig = 0;
        _tmpSig = (uint32)rawTCU;
        printf("_tmpSig = %d",_tmpSig );
        return 0;
    }

Код 2:

    #include <stdio.h>
    typedef unsigned long   uint32;
    typedef unsigned short  uint16;
    typedef signed short    sint16;

    sint16 rawTCU = -100;

    int main()
    {
        uint16 _tmpSig = 0;
        _tmpSig = (uint16)rawTCU;
        printf("_tmpSig = %d",_tmpSig );
        return 0;
    }

Код 1 печатает

    _tmpSig = -100

Код 2 печатает

    _tmpSig = 65436

Я не понимаю, как это ведет себя по-разному, почему разные типы приводят к выводу разных значений.Почему при использовании uint16 для присвоения создается другое значение (которое равно 65536 - 100 = 65436).Как это оптимизируется, но не во время использования uint32.Пожалуйста, дайте ваши предложения, как это работает.Спасибо!

Ответы [ 2 ]

4 голосов
/ 20 сентября 2019

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

В этом выражении

_tmpSig = (uint32)rawTCU;

знаковый бит отрицательного значения rawTCU распространяется на все 32 бита.

В этом выражении

_tmpSig = (uint16)rawTCU;

происходит усечение до 16 бит.Результатом приведения считается значение без знака.Таким образом, распространение не происходит.

Обратите внимание, что этот вызов

printf("_tmpSig = %d",_tmpSig );

имеет неопределенное поведение.Вы должны написать

printf("_tmpSig = %lu",_tmpSig );
                  ^^^
0 голосов
/ 20 сентября 2019

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

Возможно, вы заметили, что вы можете напечатать char, short intили long int с использованием той же строки формата "% d" .Это стало возможным благодаря тому, что все необязательные аргументы printf подвергаются продвижению по умолчанию.Фактически, так обстоит дело с аргументами всех функций с переменными числами !

Упрощенная сумма правил продвижения выглядит так:

  • Плавающие становятсяdouble
  • Целочисленные типы, меньшие чем int (то есть, символы и короткие целые числа), становятся int
  • Без знака int остается как беззнаковое int

Это означает, что существуетздесь скрытое преобразование, которое не очень очевидно, если вы его не ищете.

Случай 1:

_tmpSig = (uint32)rawTCU;

Здесь, поскольку мы конвертируем из типа со знаком ,расширение знака выполняется так, что _tmpSig становится очень большим числом без знака, которое является представлением -100.

Теперь, при вызове printf, никакого продвижения не происходит, так как это уже unsigned int,При печати, поскольку вы использовали форматирование "% d", оно интерпретируется как signed int и дает -100.

Случай 2:

_tmpSig = (uint16)rawTCU;

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

Однако, когда оно передается в printf(), оно превращается в signed int.Так что здесь есть скрытое преобразование из unsigned short в signed long.Поскольку мы конвертируем из типа без знака , здесь нет расширения знака!Это где значение на самом деле становится 65436 .Теперь при печати как «% d» печатается то же значение.

РЕДАКТИРОВАТЬ: Исправлена ​​фактическая неточность, указанная @ EricPostpischil

...