求高人指点:C语言中char型与int型强制转换的内部过程

xiaozhang112889 2014-10-29 10:31:52
本人最近笔试,碰到不少C语言强制转换问题,赶脚越转越糊涂了~不多说,先看一段代码:
#include <stdio.h>
int main()
{
char a = 254;
printf("10进制形式:a=%d,254=%d\n",a,254);
printf("16进制形式:a=%x,254=%x\n",a,254);
int b = (int)a;
int c = (int)(a&0xFF);
printf("10进制形式:b=%d,c=%d\n",b,c);
printf("16进制形式:b=%x,c=%x\n",b,c);
return 0;
}
本人在VS2008(32位编译器)编译运行后结果如下:
10进制形式:a=-2,254=254
16进制形式:a=fffffffe,254=fe
10进制形式:b=-2,c=254
16进制形式:b=fffffffe,c=fe
请按任意键继续. . .
关于此题,本人有如下疑问,望高手解答一下:
(1)char型变量a,在内存中占一个字节,按照低位截断,感觉16进制形式的a应该为0xfe,为何是0xfffffffe?
(2)b的值还能勉强理解,但是c的值是怎么来的?
...全文
2522 10 打赏 收藏 转发到动态 举报
写回复
用AI写文章
10 条回复
切换为时间正序
请发表友善的回复…
发表回复
ypzhong 2014-10-30
  • 打赏
  • 举报
回复
引用 9 楼 xiaozhang112889 的回复:
@mymtom(三楼) 关于printf的问题,个人觉得没问题,查阅相关资料,%x表示输出16进制形式的无符号的整数,所以printf的时候,编译器隐式将char转换为unsingned int; 关于c的问题,个人觉得不太对;经过调试,你会发现a在内存中的确是以0xfe的形式存储,进行( a&0xff )运算时,两操作数位数相同,没道理将a转换为0xfffffffe,运算结果为0xfe,此时好编译器是不是默认为无符号数据?? 实验可以证明:int c = (unsigned char)0xfe 、int c = 0xfe、int c = (int)(a&0xFF)结果是一样的,都为0x000000fe; 若按位与两操作数都为char,结果自然依旧为char 实验可以证明 int c = (char)0xfe 、int c = (int)(a&(char)0xFF)结果一样,都为0xfffffffe;
楼上讲了好多,主要是符号扩展,其实理解这件事除了符号扩展还有一点,然后才能整件事情串起来。 在C99标准中section 6.5.2.2 "Function calls" 的Paragraphs 6, 7: 1、默认参数提升:如果一个函数的形参类型未知,那么调用函数时要对相应的实参做“整数提升(integer promotions)”,除此以外,float类型的参数会被提升为double。 printf符合1,因为它是形参类型未知 int printf(const char *,...)。所以char or unsigned char会做类型提升,提升为int,提升这个操作才涉及到符号扩展的原则。
xiaozhang112889 2014-10-30
  • 打赏
  • 举报
回复
@mymtom(三楼)

关于printf的问题,个人觉得没问题,查阅相关资料,%x表示输出16进制形式的无符号的整数,所以printf的时候,编译器隐式将char转换为unsingned int;
关于c的问题,个人觉得不太对;经过调试,你会发现a在内存中的确是以0xfe的形式存储,进行( a&0xff )运算时,两操作数位数相同,没道理将a转换为0xfffffffe,运算结果为0xfe,此时好编译器是不是默认为无符号数据??
实验可以证明:int c = (unsigned char)0xfe 、int c = 0xfe、int c = (int)(a&0xFF)结果是一样的,都为0x000000fe;
若按位与两操作数都为char,结果自然依旧为char
实验可以证明 int c = (char)0xfe 、int c = (int)(a&(char)0xFF)结果一样,都为0xfffffffe;
dbzhang800 2014-10-29
  • 打赏
  • 举报
回复
引用 6 楼 wangzuxi 的回复:
我说的是默认情况,大多数编译器默认情况char就是signed char,要是强制指定-funsigned-char,当然是按无符号的来,无符号char转到int高位被置0,gcc下用movzbl指令,零扩展;有符号char转int用movsbl指令,符号扩展。 lz的问题只能用符号扩展来解释,不管char是signed还是unsigned一样能解释得通。 ps:@lz 遇到这种情况,linux下用gdb或者objdump看一下反汇编代码就知道了,windows下也可以用IDE的反汇编工具看或者od之类的也可以。
??
zuxi 2014-10-29
  • 打赏
  • 举报
回复
引用 4 楼 dbzhang800 的回复:
[quote=引用 3 楼 wangzuxi 的回复:] (1)a的最高位是1,转换成b时符号位扩展了,所以b=0xfffffffe (2)0xFF虽然看似一个字节,但等于0x000000FF,和a进行与运算之后符号位为0,转换成c时自然就为正数,即0xFE,如果像这样:int c = (int)(a&(char)0xFF);那c和b是一样的值。
只强调最高位,不管unsigned还是signed是不恰当的的。 对楼主的程序来说,使用MSVC编译时,/J 参数用于控制 char 是否带符号 cl test.c cl /J test.c 结果是不一样的 同样,对GCC gcc -fsigned-char test.c gcc -funsigned-char test.c 结果也是不一样的[/quote] 我说的是默认情况,大多数编译器默认情况char就是signed char,要是强制指定-funsigned-char,当然是按无符号的来,无符号char转到int高位被置0,gcc下用movzbl指令,零扩展;有符号char转int用movsbl指令,符号扩展。 lz的问题只能用符号扩展来解释,不管char是signed还是unsigned一样能解释得通。 ps:@lz 遇到这种情况,linux下用gdb或者objdump看一下反汇编代码就知道了,windows下也可以用IDE的反汇编工具看或者od之类的也可以。
赵4老师 2014-10-29
  • 打赏
  • 举报
回复
计算机组成原理→DOS命令→汇编语言→C语言(不包括C++)、代码书写规范→数据结构、编译原理、操作系统→计算机网络、数据库原理、正则表达式→其它语言(包括C++)、架构…… 对学习编程者的忠告: 多用小脑和手,少用大脑、眼睛和嘴,会更快地学会编程! 眼过千遍不如手过一遍! 书看千行不如手敲一行! 手敲千行不如单步一行! 单步源代码千行不如单步Debug版对应汇编一行! 单步Debug版对应汇编千行不如单步Release版对应汇编一行! VC调试时按Alt+8、Alt+7、Alt+6和Alt+5,打开汇编窗口、堆栈窗口、内存窗口和寄存器窗口看每句C对应的汇编、单步执行并观察相应堆栈、内存和寄存器变化,这样过一遍不就啥都明白了吗。 对VC来说,所谓‘调试时’就是编译连接通过以后,按F10或F11键单步执行一步以后的时候,或者在某行按F9设了断点后按F5执行停在该断点处的时候。 (Turbo C或Borland C用Turbo Debugger调试,Linux或Unix下用GDB调试时,看每句C对应的汇编并单步执行观察相应内存和寄存器变化。) 电脑内存或文件内容只是一个一维二进制字节数组及其对应的二进制地址; 人脑才将电脑内存或文件内容中的这个一维二进制字节数组及其对应的二进制地址的某些部分看成是整数、有符号数/无符号数、浮点数、复数、英文字母、阿拉伯数字、中文/韩文/法文……字符/字符串、汇编指令、函数、函数参数、堆、栈、数组、指针、数组指针、指针数组、数组的数组、指针的指针、二维数组、字符点阵、字符笔画的坐标、黑白二值图片、灰度图片、彩色图片、录音、视频、指纹信息、身份证信息…… 推荐使用WinHex软件查看硬盘或文件或内存中的原始字节内容。
dbzhang800 2014-10-29
  • 打赏
  • 举报
回复
引用 3 楼 wangzuxi 的回复:
(1)a的最高位是1,转换成b时符号位扩展了,所以b=0xfffffffe (2)0xFF虽然看似一个字节,但等于0x000000FF,和a进行与运算之后符号位为0,转换成c时自然就为正数,即0xFE,如果像这样:int c = (int)(a&(char)0xFF);那c和b是一样的值。
只强调最高位,不管unsigned还是signed是不恰当的的。 对楼主的程序来说,使用MSVC编译时,/J 参数用于控制 char 是否带符号 cl test.c cl /J test.c 结果是不一样的 同样,对GCC gcc -fsigned-char test.c gcc -funsigned-char test.c 结果也是不一样的
zuxi 2014-10-29
  • 打赏
  • 举报
回复
引用 楼主 xiaozhang112889 的回复:
本人最近笔试,碰到不少C语言强制转换问题,赶脚越转越糊涂了~不多说,先看一段代码: #include <stdio.h> int main() { char a = 254; printf("10进制形式:a=%d,254=%d\n",a,254); printf("16进制形式:a=%x,254=%x\n",a,254); int b = (int)a; int c = (int)(a&0xFF); printf("10进制形式:b=%d,c=%d\n",b,c); printf("16进制形式:b=%x,c=%x\n",b,c); return 0; } 本人在VS2008(32位编译器)编译运行后结果如下: 10进制形式:a=-2,254=254 16进制形式:a=fffffffe,254=fe 10进制形式:b=-2,c=254 16进制形式:b=fffffffe,c=fe 请按任意键继续. . . 关于此题,本人有如下疑问,望高手解答一下: (1)char型变量a,在内存中占一个字节,按照低位截断,感觉16进制形式的a应该为0xfe,为何是0xfffffffe? (2)b的值还能勉强理解,但是c的值是怎么来的?
(1)a的最高位是1,转换成b时符号位扩展了,所以b=0xfffffffe (2)0xFF虽然看似一个字节,但等于0x000000FF,和a进行与运算之后符号位为0,转换成c时自然就为正数,即0xFE,如果像这样:int c = (int)(a&(char)0xFF);那c和b是一样的值。
mymtom 2014-10-29
  • 打赏
  • 举报
回复
楼上说的对 printf("16进制形式:a=%x,254=%x\n",a,254); 由于VS里char是signed的 a = 254; 的实际结果是 这里的a = -2, printf会把 a 转成 int 类型 相当于 printf("16进制形式:a=%x,254=%x\n",(int)a,254); 输出就是fffffffe 一般来说如果char a; 要用%x输出的话,建议使用(unsigned char)类型转换 printf("16进制形式:a=%x,254=%x\n",(unsigned char)a,254); c 的值 就是 0xfffffffe & 0xff = 0xfe啊
dbzhang800 2014-10-29
  • 打赏
  • 举报
回复
char 是 unsigned char 还是 signed char 是由编译器自主决定的。 很明显,VS2008中的 char 是 signed char。也就是 char a = 254; 其实就是 char a = -2;
zuxi 2014-10-29
  • 打赏
  • 举报
回复
引用 7 楼 dbzhang800 的回复:
[quote=引用 6 楼 wangzuxi 的回复:] 我说的是默认情况,大多数编译器默认情况char就是signed char,要是强制指定-funsigned-char,当然是按无符号的来,无符号char转到int高位被置0,gcc下用movzbl指令,零扩展;有符号char转int用movsbl指令,符号扩展。 lz的问题只能用符号扩展来解释,不管char是signed还是unsigned一样能解释得通。 ps:@lz 遇到这种情况,linux下用gdb或者objdump看一下反汇编代码就知道了,windows下也可以用IDE的反汇编工具看或者od之类的也可以。
??[/quote] signed char转int:符号扩展,unsigned char转int:零扩展

69,759

社区成员

发帖
与我相关
我的任务
社区描述
C语言相关问题讨论
社区管理员
  • C语言
  • 花神庙码农
  • 架构师李肯
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧