C++中关于类型转换的问题讨论
这里以signed/unsigned char, signed/unsigned short, signed/unsigned int类型为例,
讨论一下基本类型转换的基本原理,这样我们在编程中碰到由类型错误转换而引发的越界问题时,也可以比较容易诊断,不至于把BUG怀疑到机器或编译器身上:)。
本文属于个人原创,任何个人都可以转载,但请务必提供转载地址。
一,3种基本类型表示范围如下 (圆形示例图),其中阴影部分的弧边界包含的是没有越界的数值范围:
2,类型转换原则小结:
我们把类型转换分成两类:
1)同类型符号之间的转换:
*同长度类型,不做转换,如int->int,
*向更长度类型转换,如char->short->int
原则:向高字节对齐
因为有足够位数容纳数值,所以数值会保持不变;
若为负数时高位会被1,若为正数时高位会补0.
*向短长度类型转换:如int ->short->char
原则:高字节截断,低字节保留。
2)不同类型符号之间的转换:
*同长度类型转换:如int<->unsigned int
原则:重新解释至目标类型,
这里是重新解释,换名话说,在等长字节时,有符号类型和无符号类型在内存中的十六进制表示仍然是一样的。
当数值范围溢出时,重新解释会造成前后两者数值的会发生巨大跳变,详见示例。
*向更长类型转换:如char->unsigned int
原则:先字节对齐,再类型统一。
如:char->unsigned int ===>char -> int ->unsigned int
unsigned char -> int ===>unsigned char ->unsigned int ->int
这个原则会造成将负数执行”跨类型跨字节“转换时,前后两数值之间很大差距,
如果想让差距变得”相对“合理些(等价于重新解释后的数值),你需要手动做强制转换:先类型统一,再做字节对齐。
*向更短字节对齐:如int->unsigned char
原则:高字节截断,低字节保留
提示:
1)如果想要转换无错,还是规规矩矩的保证数值:不要溢出,不要截断,不要跨类型跨字节转换。
2)在”有符号类型“向”无符号类型“转换时,数值不要为负数,否则打印出的数值并非你想要的结果。
尤其是“有符号短字节类型“向”无符号长字节类型转换“时,负数会按照高字节补1的原则,你将会得到一个无敌错错值。
3,类型转换源代码
#include <stdio.h> #include <stdlib.h> #include <limits.h> typedef char s8; typedef unsigned u8; typedef short s16; typedef unsigned short u16; typedef int s32; typedef unsigned int u32; #define NONE "\033[0m" #define BLUE "\033[0;32m" #define RED "\033[0;31m" void func_s8(s8 num) { s8 s8Num = num; u8 u8Num = num; s16 s16Num = num; u16 u16Num = num; s32 s32Num = num; u32 u32Num = num; //signed->signed printf(BLUE " s8[%hhd#0x%02hhx]:" NONE "\ns16(%hd#0x%04hx) \ns32(%d#0x%08x)\n", s8Num, s8Num, s16Num, s16Num, s32Num, s32Num); //signed->unsigned printf(" u8(%hhu#0x%02hhx) \nu16(%hu#0x%04hx) \nu32(%u#0x%08x)\n", u8Num, u8Num, u16Num, u16Num, u32Num, u32Num); } void func_u8(u8 num) { s8 s8Num = num; u8 u8Num = num; s16 s16Num = num; u16 u16Num = num; s32 s32Num = num; u32 u32Num = num; //unsigned->unsigned printf(BLUE " u8[%hhu#0x%02hhx)]" NONE "\nu16(%hu#0x%04hx) \nu32(%u#0x%08x)\n", u8Num, u8Num, u16Num, u16Num, u32Num, u32Num); //unsigned->signed printf(" s8(%hhd#0x%02hhx): \ns16(%hd#0x%04hx) \ns32(%d#0x%08x)\n", s8Num, s8Num, s16Num, s16Num, s32Num, s32Num); } void func_s16(s16 num) { s8 s8Num = num; u8 u8Num = num; s16 s16Num = num; u16 u16Num = num; s32 s32Num = num; u32 u32Num = num; //signed->signed printf(BLUE "s16[%hd#0x%04hx]: " NONE "\ns32(%d#0x%08x) \n s8(%hhd#0x%02hhx)\n", s16Num, s16Num, s32Num, s32Num, s8Num, s8Num); //signed->unsigned printf("u16(%hu#0x%04hx) \nu32(%u#0x%08x) \n u8(%hhu#0x%02hhx)\n", u16Num, u16Num, u32Num, u32Num, u8Num, u8Num); } void func_u16(u16 num) { s8 s8Num = num; u8 u8Num = num; s16 s16Num = num; u16 u16Num = num; s32 s32Num = num; u32 u32Num = num; //unsigned->unsigned printf(BLUE "u16[%hu#0x%04hx]:" NONE "\nu32(%u#0x%08x) \n u8(%hhu#0x%02hhx)\n", u16Num, u16Num, u32Num, u32Num, u8Num, u8Num); //unsigned->signed printf("s16(%hd#0x%04hx) \ns32(%d#0x%08x) \n s8(%hhd#0x%02hhx)\n", s16Num, s16Num, s32Num, s32Num, s8Num, s8Num); } void func_s32(s32 num) { s8 s8Num = num; u8 u8Num = num; s16 s16Num = num; u16 u16Num = num; s32 s32Num = num; u32 u32Num = num; //signed->signed printf(BLUE "s32[%d#0x%08x]:" NONE "\ns16(%hd#0x%04hx) \n s8(%hhd#0x%02hhx)\n", s32Num, s32Num, s16Num, s16Num, s8Num, s8Num); //signed->unsigned printf("u32(%u#0x%08x) \nu16(%hu#0x%04hx) \n u8(%hhu#0x%02hhx)\n", u32Num, u32Num, u16Num, u16Num, u8Num, u8Num); } void func_u32(u32 num) { s8 s8Num = num; u8 u8Num = num; s16 s16Num = num; u16 u16Num = num; s32 s32Num = num; u32 u32Num = num; //unsigned->unsigned printf(BLUE "u32[%u#0x%08x]" NONE "\nu16(%hu#0x%04hx) \n u8(%hhu#0x%02hhx)\n", u32Num, u32Num, u16Num, u16Num, u8Num, u8Num); //unsigned->signed printf("s32(%d#0x%08x): \ns16(%hd#0x%04hx) \n s8(%hhd#0x%02hhx)\n", s32Num, s32Num, s16Num, s16Num, s8Num, s8Num); } int main() { printf(RED "######s8Num testing######\n" NONE); s8 s8Num = 0x7F; func_s8(s8Num); s8Num = 0x80; func_s8(s8Num); s8Num = 0xFF; func_s8(s8Num); printf(RED "######u8Num testing######\n"NONE); u8 u8Num = 0x7F; func_u8(u8Num); u8Num = 0x80; func_u8(u8Num); u8Num = 0xFF; func_u8(u8Num); printf(RED "######s32Num testing######\n"NONE); s32 s32Num = 0x7FFFFFFF; func_s32(s32Num); s32Num = 0x80000000; func_s32(s32Num); s32Num = 0xFFFFFFFF; func_s32(s32Num); printf(RED "######u32Num testing######\n"NONE); u32 u32Num = 0x7FFFFFFF; func_u32(u32Num); u32Num = 0x80000000; func_u32(u32Num); u32Num = 0xFFFFFFFF; func_u32(u32Num); printf(RED "######s16Num testing######\n"NONE); s16 s16Num = 0x7FFF; func_s16(s16Num); s16Num = 0x8000; func_s16(s16Num); s16Num = 0xFFFF; func_s16(s16Num); printf(RED "######u16Num testing######\n"NONE); u16 u16Num = 0x7FFF; func_u16(u16Num); u16Num = 0x8000; func_u16(u16Num); u16Num = 0xFFFF; func_u16(u16Num); return 0; }
4,类型转换运行结果
######s8Num testing###### s8[127#0x7f]: s16(127#0x007f) s32(127#0x0000007f) u8(127#0x7f) u16(127#0x007f) u32(127#0x0000007f) s8[-128#0x80]: s16(-128#0xff80) s32(-128#0xffffff80) u8(128#0x80) u16(65408#0xff80) u32(4294967168#0xffffff80) s8[-1#0xff]: s16(-1#0xffff) s32(-1#0xffffffff) u8(255#0xff) u16(65535#0xffff) u32(4294967295#0xffffffff) ######u8Num testing###### u8[127#0x7f)] u16(127#0x007f) u32(127#0x0000007f) s8(127#0x7f): s16(127#0x007f) s32(127#0x0000007f) u8[128#0x80)] u16(128#0x0080) u32(128#0x00000080) s8(-128#0x80): s16(128#0x0080) s32(128#0x00000080) u8[255#0xff)] u16(255#0x00ff) u32(255#0x000000ff) s8(-1#0xff): s16(255#0x00ff) s32(255#0x000000ff) ######s32Num testing###### s32[2147483647#0x7fffffff]: s16(-1#0xffff) s8(-1#0xff) u32(2147483647#0x7fffffff) u16(65535#0xffff) u8(255#0xff) s32[-2147483648#0x80000000]: s16(0#0x0000) s8(0#0x00) u32(2147483648#0x80000000) u16(0#0x0000) u8(0#0x00) s32[-1#0xffffffff]: s16(-1#0xffff) s8(-1#0xff) u32(4294967295#0xffffffff) u16(65535#0xffff) u8(255#0xff) ######u32Num testing###### u32[2147483647#0x7fffffff] u16(65535#0xffff) u8(255#0xff) s32(2147483647#0x7fffffff): s16(-1#0xffff) s8(-1#0xff) u32[2147483648#0x80000000] u16(0#0x0000) u8(0#0x00) s32(-2147483648#0x80000000): s16(0#0x0000) s8(0#0x00) u32[4294967295#0xffffffff] u16(65535#0xffff) u8(255#0xff) s32(-1#0xffffffff): s16(-1#0xffff) s8(-1#0xff) ######s16Num testing###### s16[32767#0x7fff]: s32(32767#0x00007fff) s8(-1#0xff) u16(32767#0x7fff) u32(32767#0x00007fff) u8(255#0xff) s16[-32768#0x8000]: s32(-32768#0xffff8000) s8(0#0x00) u16(32768#0x8000) u32(4294934528#0xffff8000) u8(0#0x00) s16[-1#0xffff]: s32(-1#0xffffffff) s8(-1#0xff) u16(65535#0xffff) u32(4294967295#0xffffffff) u8(255#0xff) ######u16Num testing###### u16[32767#0x7fff]: u32(32767#0x00007fff) u8(255#0xff) s16(32767#0x7fff) s32(32767#0x00007fff) s8(-1#0xff) u16[32768#0x8000]: u32(32768#0x00008000) u8(0#0x00) s16(-32768#0x8000) s32(32768#0x00008000) s8(0#0x00) u16[65535#0xffff]: u32(65535#0x0000ffff) u8(255#0xff) s16(-1#0xffff) s32(65535#0x0000ffff) s8(-1#0xff)