c语言中两句相同的printf为啥输出结果不同

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了c语言中两句相同的printf为啥输出结果不同相关的知识,希望对你有一定的参考价值。

float类型数据只有前6位是精确的,从第七位开始都是由“四舍五入”获得的。
两个printf()函数的输出结果的差异发生在第十位,所以说,这两组数据都是“对”的。
下面把float改为double(当然输出格式也做了相应的修改),两组输出结果是相同的,原因在于,double类型数据的前16位是精确的。
/*
a
=
12345678900.000000
b
=
12345678920.000000
a
=
12345678900.000000
b
=
12345678920.000000
Press
any
key
to
continue
*/
#include <stdio.h>
int main(int argc, char* argv[])
double a = 123456.789e5, b;
b = a + 20;
printf("a = %lf b = %lf\n",a,b);
printf("a = %lf b = %lf\n",a,b);
return 0;
参考技术A int
a=7,b=2,m=0,n=0,k;
k=(n=b>a)||(m=a);
/*
执行上面这一行的时候,先运算
n=b>a,b>a
为假,值为
0,赋值给
n,表达式值为
0。
接着,因为逻辑或
||
的左边是
0,不足以判断数值,所以还要运算
m=a,
赋值
7

m
后,表达式值为
7,代表真,逻辑或的结果为真,
所以整体
(n=b>a)||(m=a)
的值是
1,赋值给
k。
*/
printf("%d,%d\n",k,m);
/*

k

m
打印出来,结果就是:
1,7
*/
参考技术B 回答

假设在你选择的系统下,short型占2个字节,int型占4个字节,又假设有符号整数采用补码格式,则short型取值范围是[-32768, 32767],含有65536个整数;而int型取值范围是[-2147483648, 2147483647],含有4294967296个整数。1、当字节数少于int型的两个变量(此例是short型的x和y)在执行算术运算(此例是加法)时,它俩的值将被复制出来并被隐式类型转换为int型,然后执行算术运算。这种转换过程被称为“提升转换”。因此,表达式x+y等价于(int)x+(int)y,结果将是int型的32770。2、当x+y赋值给z时,发生隐式类型转换,由int型隐式类型转换为short型,这种转换过程被称为“窄化转换”。这次等待转换的int型值32770,已经超出short型取值范围,目标类型是short型属于有符号整数类型,发生“有符号整数溢出”,其结果将是32770-65536得到-32766。

提问

???

啥啊

为啥补码通过 printf 表现不同?

【中文标题】为啥补码通过 printf 表现不同?【英文标题】:Why does the complement behave differently through printf?为什么补码通过 printf 表现不同? 【发布时间】:2015-04-18 01:47:37 【问题描述】:

我正在阅读有关位运算符的一章,我遇到了 1 的补码运算符程序并决定在 Visual C++ 上运行它。

int main ()

   unsigned char c = 4, d;
   d = ~c;
   printf("%d\n", d);

它给出了有效的输出:251

然后我决定不使用d作为变量来保存~c的值,而是直接打印~c的值。

int main ()

   unsigned char c=4;
   printf("%d\n", ~c);

它给出了输出-5

为什么没用?

【问题讨论】:

提示:4 的 1 的补码等价于 -5 的二进制补码表示 是complement,不是compliment @LưuVĩnhPhúc 你的补充值得称赞。 【参考方案1】:

在此声明中:

printf("%d",~c);

c 被转换为int1 类型 before ~(按位补码)运算符被应用。这是因为 整数提升,它被调用到 ~ 的操作数。在这种情况下,unsigned char 类型的对象被提升为(有符号)int,然后(在 ~ 运算符评估之后)由printf 函数使用,并匹配%d 格式说明符。

请注意,默认参数提升(因为printf 是一个可变参数函数)在这里没有任何作用,因为对象已经是int 类型。

另一方面,在这段代码中:

unsigned char c = 4, d;
d = ~c;
printf("%d", d);

发生以下步骤:

cinteger Promotions 的主题,因为 ~ (以同样的方式,如上所述) ~c 右值被评估为(有符号)int 值(例如-5d=~c 进行从 intunsigned char 的隐式转换,因为 d 具有这种类型。您可能认为它与d = (unsigned char) ~c 相同。请注意,d 不能为负数(这是所有无符号类型的一般规则)。 printf("%d", d); 调用默认参数提升,因此d 被转换为int 并保留(非负)值(即int 类型可以表示unsigned char 类型的所有值) .

1) 假设int 可以表示unsigned char 的所有值(请参阅下面的TC 的comment),但是非常可能以这种方式发生.更具体地说,我们假设INT_MAX &gt;= UCHAR_MAX 成立。通常sizeof(int) &gt; sizeof(unsigned char) 持有并且字节由八位组成。否则,c 将转换为 unsigned int(如 C11 子条款 §6.3.1.1/p2),格式说明符也应相应更改为 %u 以避免获得 UB(C11 §7.21. 6.1/p9)。

【讨论】:

但这也发生在d=~c。真正的区别是在d=~c 中发生的(返回)到unsigend char 的转换,而不是在printf 调用中。 @MarcvanLeeuwen:d 的类型为 unsigned char,因此它不能为负数(即使在转换为 int 之后,因为 printf默认参数提升我>)。事实上,你是对的,在d=~c 中也发生了整数提升(以同样的方式,正如我在上面的答案中描述的那样),但是赋值又将int 转换为unsigned char。另一方面,在第二种情况下,printf 函数采用“原样”int 参数(即%d 格式说明符是正确的),所以 default arguments Promotions 在那里什么都不做。 对于更多读者:我更新了我的答案以反映 Marc 的观察。您可以在修订历史记录中找到原始(较短的)答案。希望现在一切都清楚了。 请注意,在某些系统中,unsigned char 可以通过整数提升(例如,如果 sizeof(int) == 1)提升为 unsigned int,在这种情况下,printf 将调用未定义的行为,因为它使用错误的说明符。 @T.C.:你是对的,但让我们假设(为简单起见)我们生活在一个字节由八位组成的世界。我知道标准没有强制执行它,但例如 POSIX 执行。【参考方案2】:

~ 运算符应用于c 时,它会提升为int,结果也是int

然后

在第一个示例中,结果被转换为unsigned char,然后提升为signed int 并打印出来。 在第二个示例中,结果打印为signed int

【讨论】:

【参考方案3】:

整数提升,来自标准:

如果带符号整数类型的操作数的类型可以表示所有 无符号整数类型的操作数类型的值, 无符号整数类型的操作数应转换为类型 带符号整数类型的操作数。

【讨论】:

【参考方案4】:

char 在第二个 sn-p 中的操作 ~ 之前在 printf 语句中提升为 int。所以c,就是

0000 0100 (2's complement)  

二进制提升为(假设是32位机器)

0000 0000 0000 0000 0000 0000 0000 0100 // Say it is x  

其按位补码等于该值的二进制补码减一 (~x = −x − 1)

1111 1111 1111 1111 1111 1111 1111 1011  

这是-5 2 的补码形式的十进制。

注意charcint的默认提升也是在

d = ~c;

在补码运算之前,但结果被转换回unsigned char,因为d 的类型为unsigned char

C11:6.5.16.1 简单赋值(p2):

在简单赋值(=)中,右操作数的值被转换为赋值表达式的类型,并替换存储在左操作数指定的对象中的值。

6.5.16 (p3):

赋值表达式的类型是左操作数的类型 左值转换后。

【讨论】:

【参考方案5】:

它给出了 op -5。为什么它不起作用?

代替:

printf("%d",~c);

使用:

printf("%d", (unsigned char) ~c);

获得与第一个示例相同的结果。

~ 操作数经过整数提升,默认参数提升应用于可变参数函数的参数。

【讨论】:

【参考方案6】:

要了解代码的行为,您需要了解名为 'Integer Promotions' 的概念(在您的代码中隐式发生在对 unsigned char 操作数进行位非操作之前)如 N1570 委员会草案中所述:

§ 6.5.3.3 Unary arithmetic operators

    ~ 运算符的结果是它的按位补码 (提升)操作数(即,结果中的每一位都被设置当且仅 如果未设置转换后的操作数中的相应位)。 该 对操作数执行整数提升,结果有 提升的类型。如果提升的类型是“‘无符号类型’,则 表达式~E 等价于可表示的最大值 输入减号E"。

因为unsigned char 类型比int 类型更窄(因为它需要更少的字节), - 由抽象机(编译器)执行的隐式类型提升和变量c 的值在当时被提升为int编译(在应用补码操作~ 之前)。它是正确执行程序所必需的,因为~ 需要一个整数操作数。

§ 6.5 Expressions

    一些运算符(一元运算符~和二元运算符&lt;&lt;&gt;&gt;&amp;^|, 统称为按位运算符)必须有操作数 整数类型。这些运算符产生的值取决于 整数,并且有符号类型的实现定义和未定义方面。

编译器足够聪明,可以分析表达式、检查表达式的语义、执行类型检查和算术转换(如果需要)。这就是在 char 类型上应用 ~ 的原因,我们不需要显式编写 ~(int)c — 称为显式类型转换(并且要避免错误)。

注意:

    c 的值在表达式 ~c 中提升为 int,但 c 的类型仍然是 unsigned char - 它的类型不是。不要混淆。

    重要提示:~ 操作的结果是int 类型!,查看以下代码(我没有 vs-compiler,我使用的是 gcc):

    #include<stdio.h>
    #include<stdlib.h>
    int main(void)
       unsigned char c = 4;
       printf(" sizeof(int) = %zu,\n sizeof(unsigned char) = %zu",
                sizeof(int),
                sizeof(unsigned char));
       printf("\n sizeof(~c) = %zu", sizeof(~c));        
       printf("\n");
       return EXIT_SUCCESS;
    
    

    编译并运行:

    $ gcc -std=gnu99 -Wall -pedantic x.c -o x
    $ ./x
    sizeof(int) = 4,
    sizeof(unsigned char) = 1
    sizeof(~c) = 4
    

    注意~c 的结果大小与int 相同,但不等于unsigned char — 此表达式中~ 运算符的结果为int!正如提到的6.5.3.3 Unary arithmetic operators

      一元- 运算符的结果是其(提升的)操作数的负数。整数 对操作数执行提升,并且结果具有提升的类型。

现在,正如@hacks 在他的answer 中所解释的那样,~c 在 32 位机器上的结果和c = 4 的值是:

1111 1111 1111 1111 1111 1111 1111 1011

十进制是-5——这是你的第二个代码的输出!

在您的第一个代码中,还有一行很有趣,可以理解b = ~c;,因为b 是一个unsigned char 变量,~c 的结果是int 类型,所以为了适应 ~cb 的结果值,结果值 (~c) 被截断以适合 unsigned char 类型,如下所示:

    1111 1111 1111 1111 1111 1111 1111 1011  // -5 & 0xFF
 &  0000 0000 0000 0000 0000 0000 1111 1111  // - one byte      
    -------------------------------------------          
                                  1111 1011  

1111 1011 的十进制等效值为 251。您可以使用以下方法获得相同的效果:

printf("\n ~c = %d", ~c  & 0xFF); 

或如@ouah 在他的answer 中建议的那样使用显式转换。

【讨论】:

@hacks 谢谢,是的,我来这里主要是为了学习而不是参与,目前我主要从事 python、django、web 工作。 +int(PI/3) 用于实际引用相关规范部分......没有其他答案实际上解释了为什么促销发生在这里! this->Some operators (the unary operator ~, and the binary operators &lt;&lt;, &gt;&gt;, &amp;, ^, and |, collectively described as bitwise operators) are required to have operands that have integer type. 今天让我很开心。 @vaxquis 谢谢,-当我来到这篇文章时,我注意到大多数答案都在解释“结果如何不同”,所以我决定添加我的答案并强调“为什么这样”的原因, -- 在您阅读我的回答时,我在草稿中遗漏了另一个相关内容,现已添加。

以上是关于c语言中两句相同的printf为啥输出结果不同的主要内容,如果未能解决你的问题,请参考以下文章

c语言float类型输出

VS2010 编写c语言变量为啥要初始化?

C语言如何控制文字输出速度?

为啥在C语言中,这个printf的输出是错误的?

c语言计算为啥结果不对

在C语言中能否直接给指针指向的数据赋值?为啥?