在开发中,经常会碰到有符号数与无符号数之间的转换,有时是隐性,有时是显性。如果不清楚其中规则,则可能带来非常难以排查的bug。
下图表示几个常用的数据类型之间的转换:
注:
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
#include
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,只是解析方式发生改变。