Преобразование unsigned int в signed int C

Я пытаюсь преобразовать 65529 С unsigned int подписанного int. Я попытался сделать такой бросок:

unsigned int x = 65529;
int y = (int) x;

но y все еще возвращает 65529, когда он должен вернуть -7. Почему так?

7 ответов


похоже, вы ожидаете int и unsigned int быть 16-разрядным целым числом. Очевидно, это не так. Скорее всего, это 32-разрядное целое число, которое достаточно велико, чтобы избежать обертывания, которое вы ожидаете.

обратите внимание, что нет полностью c-совместимого способа сделать это, потому что приведение между подписанным/неподписанным для значений вне диапазона определяется реализацией. Но это все равно будет работать в большинстве случаев:

unsigned int x = 65529;
int y = (short) x;      //  If short is a 16-bit integer.

или же:

unsigned int x = 65529;
int y = (int16_t) x;    //  This is defined in <stdint.h>

Я знаю, что это старый вопрос, но он хороший, так как насчет этого?

unsigned short int x = 65529U;
short int y = *(short int*)&x;

printf("%d\n", y);

@Mysticial понял. Короткий обычно 16-битный и проиллюстрирует ответ:

int main()  
{
    unsigned int x = 65529;
    int y = (int) x;
    printf("%d\n", y);

    unsigned short z = 65529;
    short zz = (short)z;
    printf("%d\n", zz);
}

65529
-7
Press any key to continue . . .


Чуть больше деталей. Это все о том, как подписанные числа хранятся в памяти. Сделайте поиск двоек-дополните нотацию для более подробной информации, но вот основы.

Итак, давайте посмотрим на 65529 decimal. Его можно представить как FFF9h в шестнадцатеричном виде. Мы также можем представить это в двоичном формате как:

11111111 11111001

когда мы объявляем short zz = 65529; компилятор интерпретирует 65529 как знаковое значение. В нотации двойки-дополнения верхний бит означает, является ли знаковое значение положительным или отрицательным. В этом случае вы можете увидеть, что верхний бит-это 1, поэтому он рассматривается как отрицательное число. Вот почему он печатает -7.

на unsigned short, мы не заботимся о знаке, так как это unsigned. Поэтому, когда мы распечатываем его с помощью %d, мы используем все 16 бит, поэтому он интерпретируется как 65529.


чтобы понять, почему, вам нужно знать, что CPU представляет подписанные числа, используя дополнение двух (возможно, не все, но многие).

    byte n = 1; //0000 0001 =  1
    n = ~n + 1; //1111 1110 + 0000 0001 = 1111 1111 = -1

а также, что тип int и unsigned int может быть разного размера в зависимости от вашего процессора. При выполнении конкретных вещей, как это:

   #include <stdint.h>
   int8_t ibyte;
   uint8_t ubyte;
   int16_t iword;
   //......

представление значений 65529u и -7 идентичны для 16-битных ints. Только интерпретация битов различна.

для больших ints и этих значений вам нужно подписать extend; один из способов-с логическими операциями

int y = (int )(x | 0xffff0000u); // assumes 16 to 32 extension, x is > 32767

Если скорость не является проблемой, или разделить быстро на вашем процессоре,

int y = ((int ) (x * 65536u)) / 65536;

умножение сдвигает влево 16 бит (опять же, предполагая расширение 16 до 32), а деление сдвигает вправо, сохраняя знак.


вы ожидаете, что ваш int тип имеет ширину 16 бит, и в этом случае вы действительно получите отрицательное значение. Но, скорее всего, это 32 бита в ширину, поэтому подписано int может представлять 65529 просто отлично. Вы можете проверить это, напечатав sizeof(int).


чтобы ответить на вопрос, написал в комментарии выше - попробуйте что-то вроде этого:

unsigned short int x = 65529U;
short int y = (short int)x;

printf("%d\n", y);

или

unsigned short int x = 65529U;
short int y = 0;

memcpy(&y, &x, sizeof(short int);
printf("%d\n", y);