Приведение из uint16_t в int32_t требует промежуточного преобразования в int16_t - PullRequest
1 голос
/ 06 мая 2020

См. Мой код ниже. Я не понимаю, почему мне нужно преобразовать переменную var_u16 в int16_t , прежде чем преобразовывать в int32_t , чтобы правильно интерпретировать как подписанный int в MyFun c ().

Я бы подумал, что, поскольку var_u16 имеет самый старший бит как единицу, при простом преобразовании в int32_t компилятор поймет, что это отрицательное значение.

Вместо этого оно все еще интерпретируется как беззнаковое значение. Зачем нужно промежуточное приведение к int16_t сначала?

Я использую g cc 8.3.0

#include <stdio.h>

void MyFunc(int32_t s32Var)
{
    printf("%d\n", s32Var);
}

int main()
{
    uint16_t var_u16 = 0x8000;
    MyFunc((int32_t)var_u16);          // prints 32768  
    MyFunc((int32_t)(int16_t)var_u16); // prints -32768 

    return 0;
}

Ответы [ 3 ]

4 голосов
/ 06 мая 2020

Здесь происходит то, что значение преобразуется из 16-битного числа без знака в 32-битное число со знаком. Базовое представление не имеет значения.

Поскольку любое значение, которое может быть сохранено в uint16_t, также может быть сохранено в int32_t, значение не изменяется при преобразовании в Новый тип. Таким образом, uint16_t со значением 32768 становится int32_t со значением 32768.

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

2 голосов
/ 06 мая 2020

В первом случае значение не меняется. Это происходит потому, что int32_t может представлять все значения uint16_t. Когда вы его приводите, вы сохраняете значение 0x8000, оно просто дополняется нулями.

компилятор поймет, что это отрицательное значение

Это неверно . 0x8000 - вполне допустимое положительное число, если оба представления.

int16_t не может содержать все значения uint16_t, и 0x8000 является одним из этих значений. Вместо этого 32768 в этом случае становится -32768. При преобразовании в int32_t вы подписываете и расширяете int16_t, сохраняя -32768.

0 голосов
/ 06 мая 2020

Объект типа int32_t может хранить значение типа uint16_t.

Из стандарта C (6.2.6.2 Целочисленные типы)

5 Значения любых битов заполнения не указаны.54) Допустимо (без ловушки) объектное представление целочисленного типа со знаком, где бит знака равен нулю, является допустимым представлением объекта соответствующего беззнакового типа и должно представлять то же значение .

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

Так, например, если у вас есть следующее объявление

signed char c = -1;

, тогда оно имеет внутреннее представление в двух системах дополнений, например

11111111

Если вы конвертируете его в int16_t или uint16_t, то как

int16_t i = c;
uint17_t ui = c;

, тогда в обоих случаях внутренние представления будут выглядеть как

11111111 11111111

Однако, если у вас есть объект беззнакового символьного типа, например

unsigned char c = 255;

с внутренним представлением, например

11111111

, тогда в этом случае переменные i и ui, объявленные как

int16_t i = c;
uint17_t ui = c;

, будут иметь следующее внутреннее представление

00000000 11111111

, потому что значение 255 является допустимым значением для объектов этих типов.

...