天天看點

c/c++ 有符号數與無符号數的轉換

在開發中,經常會碰到有符号數與無符号數之間的轉換,有時是隐性,有時是顯性。如果不清楚其中規則,則可能帶來非常難以排查的bug。

下圖表示幾個常用的資料類型之間的轉換:

c/c++ 有符号數與無符号數的轉換

注:

  1. 上表中使用大端格式表示資料
  1. 上表中的資料為16進制

x:表示轉換前後的變量的值本身不發生變化,而解析方式改變(解析為有符号數或無符号數)

e:表示将發生零擴充(在開頭填0)

E:表示将發生符号擴充(填充最高有效位的值)

c:表示将發生截斷,保留低位,多餘的高位舍去

廢話少說,show the code:

/*
 * HOST: Linux 3.2.0-4-686-pae #1 SMP Debian 3.2.60-1+deb7u1 i686 GNU/Linux
 * GCC: Thread model: posix, gcc version 4.7.2 (Debian 4.7.2-5)
 * INFO: The basic data type conversion between signed and unsigned.
 *
 * */

#include <stdio.h>
#include <stdlib.h>

typedef unsigned char uchar;
typedef unsigned int uint;
typedef uchar * byte_pointer;

void show_bytes(byte_pointer addr, int size)
{
        int i = 0;

        for (i = 0; i < size; i++) {
                printf("%02x", addr[i]);
                if (i != size - 1)
                        printf(" ");
        }
        printf("\n");
}

int item_counter;
struct test_item {
        char *name;
        char buf[8];
        int len;
        byte_pointer p;
};

struct test_item *menu[100];

#define ITEM_ADD(f, t, d)                                                       \
        do {                                                                    \
                struct test_item *p;                                            \
                p       = (struct test_item*)malloc(sizeof(struct test_item));  \
                p->name         = #f"("#d") to "#t;                             \
                p->len          = sizeof(t);                                    \
                *(t*)p->buf     = (f)d;                                         \
                menu[item_counter++]    = p;                                    \
        }while(0)


int main(void)
{
        ITEM_ADD(char,  int,    0x5f);
        ITEM_ADD(char,  int,    0xff);
        ITEM_ADD(char,  uint,   0x5f);
        ITEM_ADD(char,  uint,   0xff);
        ITEM_ADD(uchar, int,    0x5f);
        ITEM_ADD(uchar, int,    0xff);
        ITEM_ADD(uchar, uint,   0x5f);
        ITEM_ADD(uchar, uint,   0xff);
        ITEM_ADD(int,   char,   0x5f);
        ITEM_ADD(int,   char,   0xffffffffu);
        ITEM_ADD(int,   uchar,  0x5f);
        ITEM_ADD(int,   uchar,  0xfffffffu);
        ITEM_ADD(uint,  char,   0x5f);
        ITEM_ADD(uint,  char,   0xfffffffu);
        ITEM_ADD(uint,  uchar,  0x5f);
        ITEM_ADD(uint,  uchar,  0xffffffu);

        int j;
        for (j = 0; j < item_counter; j++) {
                printf("%-28s", menu[j]->name);
                show_bytes((byte_pointer)menu[j]->buf, menu[j]->len);
                free(menu[j]);
        }

        return 0;
}

           

輸出結果(主機環境為小端格式,32位):

c/c++ 有符号數與無符号數的轉換

總結:

有符号的占用空間小的資料類型(比如char)轉換到無符号或有符号的占用空間更大的資料類型(比如int,unsigned int)會進行符号擴充,也就是在多出來的高位全部用符号位填充。示例中的char(0xff),符号位為1,為負數,故轉換為int、uint的結果都是0xff ff ff ff。char(0x5f)為正數,符号位為0,轉換後值不變。占用空間大的資料類型轉為占用空間小的資料類型時,隻取低位,高位會被截斷。其餘情況下,記憶體單元中的值不會發生變化,比如char轉為uchar,隻是解析方式發生改變。