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
被转换为int
1 类型 before ~
(按位补码)运算符被应用。这是因为 整数提升,它被调用到 ~
的操作数。在这种情况下,unsigned char
类型的对象被提升为(有符号)int
,然后(在 ~
运算符评估之后)由printf
函数使用,并匹配%d
格式说明符。
请注意,默认参数提升(因为printf
是一个可变参数函数)在这里没有任何作用,因为对象已经是int
类型。
另一方面,在这段代码中:
unsigned char c = 4, d;
d = ~c;
printf("%d", d);
发生以下步骤:
c
是 integer Promotions 的主题,因为 ~
(以同样的方式,如上所述)
~c
右值被评估为(有符号)int
值(例如-5
)
d=~c
进行从 int
到 unsigned char
的隐式转换,因为 d
具有这种类型。您可能认为它与d = (unsigned char) ~c
相同。请注意,d
不能为负数(这是所有无符号类型的一般规则)。
printf("%d", d);
调用默认参数提升,因此d
被转换为int
并保留(非负)值(即int
类型可以表示unsigned char
类型的所有值) .
1) 假设int
可以表示unsigned char
的所有值(请参阅下面的TC 的comment),但是非常可能以这种方式发生.更具体地说,我们假设INT_MAX >= UCHAR_MAX
成立。通常sizeof(int) > 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 的补码形式的十进制。
注意char
c
到int
的默认提升也是在
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
一些运算符(一元运算符
~
,和二元运算符<<
、>>
、&
、^
和|
, 统称为按位运算符)必须有操作数 整数类型。这些运算符产生的值取决于 整数,并且有符号类型的实现定义和未定义方面。
编译器足够聪明,可以分析表达式、检查表达式的语义、执行类型检查和算术转换(如果需要)。这就是在 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
类型,所以为了适应 ~c
到 b
的结果值,结果值 (~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 <<, >>, &, ^, and |, collectively described as bitwise operators) are required to have operands that have integer type.
今天让我很开心。
@vaxquis 谢谢,-当我来到这篇文章时,我注意到大多数答案都在解释“结果如何不同”,所以我决定添加我的答案并强调“为什么这样”的原因, -- 在您阅读我的回答时,我在草稿中遗漏了另一个相关内容,现已添加。以上是关于c语言中两句相同的printf为啥输出结果不同的主要内容,如果未能解决你的问题,请参考以下文章