在開發中,經常會碰到有符号數與無符号數之間的轉換,有時是隐性,有時是顯性。如果不清楚其中規則,則可能帶來非常難以排查的bug。
下圖表示幾個常用的資料類型之間的轉換:
注:
- 上表中使用大端格式表示資料
- 上表中的資料為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位):
總結:
有符号的占用空間小的資料類型(比如char)轉換到無符号或有符号的占用空間更大的資料類型(比如int,unsigned int)會進行符号擴充,也就是在多出來的高位全部用符号位填充。示例中的char(0xff),符号位為1,為負數,故轉換為int、uint的結果都是0xff ff ff ff。char(0x5f)為正數,符号位為0,轉換後值不變。占用空間大的資料類型轉為占用空間小的資料類型時,隻取低位,高位會被截斷。其餘情況下,記憶體單元中的值不會發生變化,比如char轉為uchar,隻是解析方式發生改變。