(转) char c=128;printf("%d",c);问题

本文解析了在C语言中,char类型变量存储128时,在不同输出格式下的显示结果及其背后的原理。通过反汇编过程展示了数值如何从char扩展到int,并解释了符号扩展的概念。
(转) 

char c=128;printf("%d",c);问题

一、文章来由

同属计算机储存,补码问题的延生~~

二、解密

答案输出是-128,为什么呢?

char型变量c中存储的是128的补码:10000000。上一篇文章已经说到,对于计算机来说,存储的都是数据的补码。

赋值给char型,debug模式下反汇编如下:

char c = 128;
00C6446E  mov         byte ptr [c],80h  

    char cc = c;
00C64472  mov         al,byte ptr [c]  
00C64475  mov         byte ptr [cc],al  
    unsigned int ui = c;
00C64478  movsx       eax,byte ptr [c]  
00C6447C  mov         dword ptr [ui],eax  
    int i = c;
00C6447F  movsx       eax,byte ptr [c]  
00C64483  mov         dword ptr [i],eax  
    double d = c;
00C64486  movsx       eax,byte ptr [c]  
00C6448A  cvtsi2sd    xmm0,eax  
00C6448E  movsd       mmword ptr [d],xmm0
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
00C64478  movsx       eax,byte ptr [c]
   
  • 1

使eax寄存器扩展成为 0xFFFFFF80,扩展在这一步~~

这里写图片描述

而且

00C64475  mov         byte ptr [cc],al   //char cc = c;
00C6447C  mov         dword ptr [ui],eax //unsigned int ui = c;
00C64483  mov         dword ptr [i],eax  //int i = c;
00C6448E  movsd       mmword ptr [d],xmm0 //double d = c;
   
  • 1
  • 2
  • 3
  • 4

进一步将寄存器存到内存中

对于计算机来说

char c = -128; 
signed char c = 0x80; 
以上两句是一回事。

如下几种情况:

char c=128; 
printf(“%u\n”, c); //按%u输出,结果为 4294967168 
printf(“%d\n”, c); //按%d输出,结果为 -128 
printf(“%c\n”, c);//按%c输出,结果为空

原因:

c 是一个字符的长度,但当它被传送到printf函数的参数时,是将c按照int来扩张传给printf的。 
128 即为二进制的 1000 0000,16进制的0x80, 当它扩展为 int时,由于int可能是4个字节,所以会进行符号扩展。由于 128最高为是1,为负数,扩展为int的-128为11111111 111111 111111 1000000,即0xffffff80

而0xffffff80按照有符号的int来说,就是-128。 
而按照无符号的int来说,就是十进制的 4294967168。

    unsigned char uc=128;
    char c=128;
    printf("%d\n",uc);      //128
    printf("%d\n",c);       //-128
    printf("%u\n",uc);      //128
    printf("%u\n",c);       //4294967168
    printf("%08x\n",uc);    //0x00000080
    printf("%x\n",c);       //0xffffff80
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

三、数据扩展

1、短数据类型扩展为长数据类型

(1)要扩展的短数据类型为有符号数

进行符号扩展,即短数据类型的符号位填充到长数据类型的高字节位(即比短数据类型多出的那一部分),保证扩展后的数值大小不变

例1:char x=10001001b; short y=x; 则y的值应为11111111 10001001b;

例2:char x=00001001b; short y=x; 则y的值应为00000000 00001001b;

(2)要扩展的短数据类型为无符号数*

进行零扩展,即用零来填充长数据类型的高字节位

例1:unsigned char x=10001001b; short y=x; 则y的值应为00000000 10001001b;

例2:unsigned char x=00001001b; short y=x; 则y的值应为00000000 00001001b;

2、长数据类型缩减为短数据类型

如果长数据类型的高字节全为1或全为0,则会直接截取低字节赋给短数据类型;如果长数据类型的高字节不全为1或不全为0,则转换就会发生错误。(这个还不确定)

事实上下面代码会输出 ‘0’

    int a = 0xAA30;
    char c = a;
    printf("%c",c);
   
  • 1
  • 2
  • 3

3、同一长度的数据类型中有符号数与无符号数的相互转化

直接将内存中的数据赋给要转化的类型,数值大小则会发生变化,因为以不同类型解释同一段内存数据会得到不同的数值。比如一个字节中存放的数据是11111111,以unsigned char来解释就是255,以char来解释就是-1.

根据以上规则,可以得出当char c 是一个有符号的字符变量,其内存中存储的是1000 0000,但当它被传送到printf函数的参数时,是将c按照int来进行宽度扩展后再传给printf的。

128的补码是 1000 0000,16进制是0x80, 当它扩展为 int时,由于int是4个字节,需要进行短数据类型扩展到长数据类型。由于内存中存放的是10000000,以char型来解释的话第一位为符号位,表示负数,进行符号扩展为int后,int型变量中存储的数据是:11111111 11111111 11111111 1000000,即0xffffff80。以int来解释的这四个字节的数据,其值就是-128,以unsigned int来解释的话,就是232−1−127=4294967168。

四、为什么会在前面补1?

开始的char c 是 1000 0000b,后来变成 11111111 11111111 11111111 1000000b,为什么要在前面补1呢?

或者举例,char c 是 1000 0001b,后来变成 11111111 11111111 11111111 1000001b

因为要使 -2^31+2^30+…+2^7+0+1 = -2^7+0+1 
也就是 -2^31+2^30+…+2^8 = -2*2^7 
也就是 -2^31+2^30+…+2^8+2*2^7 = 0

-2^31+2^30+…+2^8+2*2^7 
= -2^31+(2^31-1)-(2^6+2^5+…+1)+2^7 = 0

—END—

<think>好的,我现在需要分析用户提供的C语言代码,并解释其运行结果。首先,我会仔细阅读代码,看看有没有语法错误或者潜在的问题。 用户给出的代码是: #include <stdio.h> int main() { char price = 200; printf(%d\n,price); char price = -200; printf(%d\n,price); return 0; } 首先,我注意到代码中有两个`char price`的声明。在同一个作用域内重复定义变量,这会导致编译错误。这是第一个需要指出的问题。 接下来,关于`char`类型的范围。通常,`char`在C语言中默认是否有符号取决于编译器和平台,但一般来说,`char`可以是有符号(-128到127)或无符号(0到255)。这里用户给price赋值200和-200,这可能会溢出。 对于第一个赋值`char price = 200;`,如果`char`是有符号的,200超过了127,会导致溢出。这时候实际存储的值会是200 - 256 = -56。所以用%d打印时,会输出-56。 第二个赋值`char price = -200;`同样有问题。如果`char`是有符号的,范围是-128到127,-200低于最小值,会溢出。计算方式是用补码表示,可能需要加上256,得到56,但由于符号位的问题,实际存储的值可能为56,但作为有符号的char,这会被解释为负数吗?或者要看具体的溢出处理方式。这里可能需要进一步分析。 不过,由于变量price被重复定义,代码无法编译通过。所以用户需要先解决这个问题,将第二个声明改为另一个变量名,或者重新赋值而不是重新声明。 然后,考虑`printf`的格式字符串是否正确,用户代码中的printf漏掉了双引号,应该是`printf("%d\n", price);`。这也是一个语法错误,会导致编译失败。 总结问题点: 1. 重复定义变量price。 2. printf语句缺少双引号。 3. char类型的溢出问题。 修改后的代码应该是: #include <stdio.h> int main() { char price1 = 200; printf("%d\n", price1); char price2 = -200; printf("%d\n", price2); return 0; } 然后根据char是否为有符号,输出结果可能不同。假设char是有符号的,那么: 200在char中溢出,变成-56。 -200同样溢出,可能变成56,但需要验证。或者根据补码的计算,-200的二进制表示为超出8位的情况,截断后可能得到某个值。 例如,对于8位有符号char,范围是-128到127。-200表示为二进制,假设是32位int,取补码后,截断最后8位。计算-200的补码:200的二进制是00000000 00000000 00000000 11001000,取反加一得到补码是11111111 11111111 11111111 00111000。截断后8位是00111000,即十进制的56?或者可能是不同的结果? 或者,正确的计算方式是:对于有符号的8位,-200 + 256 = 56,所以存储的是56。但因为是有符号类型,当用%d打印时,会进行符号扩展,变成56,而不是负数?这可能吗? 这时候可能需要实际测试或者更仔细的分析。 可能我在这里的思考有误,需要确认正确的溢出处理方式。例如,对于有符号char,赋值超过范围的值,结果是由实现定义的,但通常采用二进制截断的方式。例如,200作为char(8位),二进制是11001000,最高位是1,所以对于有符号char,这是-56。而-200的二进制补码需要计算:200的二进制是11001000,补码是00111000(取反加一),但可能我的计算有误。或者,正确的做法是:对于-200,在8位有符号中,无法表示,所以计算其模256的值。即-200 mod 256 = 56。所以存储的是56,但因为是有符号类型,所以会被视为正数56?或者,如果char是无符号的,那么-200会被换为56(256-200=56),而用%d打印时会显示56? 这可能取决于编译器对char的处理方式,是否有符号。例如,在x86的GCC中,char默认是有符号的。这种情况下,第一个例子中的200会被视为-56,第二个例子中的-200会被换为56?或者可能第二个例子中的-200在换为char时,由于超出范围,结果可能不是预期的。 这部分可能需要进一步查证,或者运行代码来观察结果。但根据C标准,当将超出范围的值赋给有符号整数类型时,结果是实现定义的,或者引发未定义行为。不过通常编译器会进行二进制截断。 所以,假设char是有符号的: price1 = 200 → 二进制11001000 → 符号位为1,数值部分取反加一得到:0011000 → 56 → 所以十进制是-56。 price2 = -200 → 二进制补码为... 假设int是32位,-200的补码是0xFFFFFF38。截断为8位是0x38(即56)。所以存储的是56。当用%d打印时,会进行符号扩展,但由于char是有符号的,0x38的最高位是0,所以符号扩展后是00000000 00000000 00000000 00111000 → 56。因此,输出结果为-56和56? 或者,可能我在这里搞错了。比如,当char是有符号类型时,赋值为-200,但无法表示,所以实际存储的是-200 + 256 = 56。当用%d打印时,会将char提升为int,此时56保持正值,所以输出56。 因此,修改后的代码可能会输出: -56 56 但如果编译器将char视为无符号的,那么第一个赋值200是合法的,输出200。第二个赋值-200会被换为56,输出56。 所以最终结果取决于char的符号性,而这是实现定义的。 因此,在解释时需要说明这些情况,指出可能的不同结果,并建议使用明确的类型(如signed char或unsigned char)来避免歧义。</think>让我们分析这段C代码的运行逻辑及潜在问题: ```c #include <stdio.h> int main() { char price = 200; // 行A printf(%d\n,price); // 行B char price = -200; // 行C printf(%d\n,price); // 行D return 0; } ``` ### 🚨 代码问题 1. **重复定义变量**:行A和行C重复定义了`price`变量(违反C语言作用域规则) 2. **printf格式错误**:行B和行D缺少双引号(`printf("%d\n", price);`) 3. **char溢出问题**:`char`类型取值范围有限,直接赋值200/-200会导致溢出 ### 🔧 修改建议 ```c #include <stdio.h> int main() { signed char price1 = 200; // 明确使用有符号char printf("%d\n", price1); // 输出-56(溢出结果) signed char price2 = -200; // 明确使用有符号char printf("%d\n", price2); // 输出56(溢出结果) return 0; } ``` ### 💡 原理分析(假设char是有符号类型) | 操作 | 二进制存储 | 十进制解释 | |-------------------|------------------|-----------| | `signed char = 200` | `11001000` | -56 | | `signed char = -200`| `00111000` | 56 | #### 计算过程: 1. **200的存储**: - 200超出`signed char`范围(-128~127) - 实际存储值 = 200 - 256 = **-56**(二进制`11001000`) 2. **-200的存储**: - -200超出`signed char`范围 - 实际存储值 = -200 + 256 = **56**(二进制`00111000`) ### 🌟 关键结论 1. **char类型的符号性**: ```c printf("char范围:%d ~ %d", CHAR_MIN, CHAR_MAX); // 查看具体实现 ``` - 默认可能有符号(常见于x86架构) - 可通过编译选项`-funsigned-char`改变 2. **整数溢出处理**: ```c signed char a = 200; // 实际值 = (200 + 128) % 256 - 128 unsigned char b = -1; // 实际值 = 255 ``` 3. **类型选择建议**: ```c // 明确需求选择类型 unsigned char u_price = 200; // 合法 signed char s_price = -200; // 仍会溢出 ``` 建议:需要存储超过127的数值时,优先使用`unsigned char`或更大类型(如`int`)。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值