C语言中的显式类型转换

arknldoa  于 2023-01-08  发布在  其他
关注(0)|答案(6)|浏览(161)

以下代码片段之间有什么区别:
1.

double a = 10.154430;
int b = a;
printf("%d", b); //prints 10 (WHY?)
double a = 10.154430;
printf("%d", a);  //prints garbage value as expected`

现在,在这两种情况下,我都没有做显式类型转换,但如果我正确地工作...为什么?(int存储双精度值,这不应该隐式工作)

0sgqnhkj

0sgqnhkj1#

请注意,第一种情况相当于printf("%d", (int)a);
doubleint的强制转换是一种特殊情况,它们将浮点数 * 转换 * 为整数。
(In实际上,大多数其他强制转换,例如在诸如unsigned longshort的各种整数类型之间的强制转换,“保留”内部表示的大多数位;在这方面,从整型到浮点型或从浮点型到整型的强制转换非常特殊,因为这涉及到一些“附加”处理)
第二种情况printf("%d", a)实际上是undefined behavior(UB)。您使用的printf的参数类型与其控制格式字符串不兼容。UB实际上可以是worse
另请参见n1570的§6.5.4

50pmv0ei

50pmv0ei2#

代码片段1和代码片段2存在差异,即:

  • 〉在代码片段1中,格式说明符**%d需要Integer类型,而您引用的B类型为int**。
  • 〉但是,在代码片段2中,您引用的是d类型,而格式说明符**%d需要的是int**类型
igsr9ssn

igsr9ssn3#

在第一种情况下,有一个隐式类型转换。

int b = a;       // Implicit conversion. OK

相当于

int b = (int)a;  // Explicit conversion. OK

(The只要类型转换值在int类型范围内,上述类型转换即可。)
而在第二种情况下,你的程序没有使用正确的double数据类型的格式规范,从而调用了未定义的行为。你得到的垃圾值是未定义行为的一个可能结果。

2w3rbyxf

2w3rbyxf4#

printf("%d", a)被调用时,下面的代码被压入到调用栈中。

"%d" // address of string literal 
a    // 8 bytes pushed onto stack assuming sizeof(double) is 8
return address of caller

然后调用到printf函数本身的跳转。
当printf启动时,它有第一个参数,格式字符串("%d"),在一个已知的堆栈偏移量,但它不知道有多少参数也被推到堆栈上,它依赖于格式代码告诉它如何解释堆栈上的后续字节。
它解析格式字符串并读取%d,然后当它看到%d时,它假设下一个被压入堆栈的值是一个4字节的整数,然后它从堆栈中读取4个字节,并做任何需要的处理将内存打印成整数,但实际上,它真正打印的是组成浮点值的一半字节。
仅看一个Intel示例,假设以下代码。

double d = 3.14;
printf("%x\n", d); // push 8-byte double, but print as integer hex
return 0;

打印输出:51eb851f
英特尔处理器存储在内存中的3.14的IEEE浮点表示如下:双精度浮点数的前4个字节与打印的相同(除了字节顺序相反,因为Intel是“little endian”)。

uz75evzq

uz75evzq5#

现在,在这两种情况下,我都没有做显式类型转换,但如果我正确地工作...为什么?(int存储双精度值,这不应该隐式工作)
案例1涉及到使用简单的赋值运算符。操作数满足此运算符的约束,特别是使用案例
左操作数具有原子、限定或非限定算术类型,右操作数具有算术类型
请注意,这两个参数的类型不必相同。它们都是算术类型就足够了。在这种情况下,指定行为的主要方面是
右操作数的值被转换为赋值表达式的类型,并替换存储在由左操作数指定的对象中的值。
因此,即使没有显式强制转换,也可以获得转换。
案例2是不同的,请注意printf的原型:

int printf(const char * restrict format, ...);

这是一个可变参数函数,你在可变参数中传递double,当你把一个参数传递给一个原型函数参数时,你会得到和简单赋值中完全一样的转换,但是对于没有作用域内原型的函数的参数,以及可变参数的函数,你会得到"默认参数提升"。2这些只包括将小于int的整型提升为intunsigned int,以及将float提升为double
printf()的特定情况下,当实际参数与相应的字段指令不匹配时,行为是未定义的。这就是您的情况。更一般地说,当任何变元函数试图将其变量参数之一解释为与该参数的(默认提升的)实际类型不兼容时,就会发生UB。

qvsjd97n

qvsjd97n6#

任何变量(int、char、float或double)都以位的形式存储在分配给它的内存位置。
printf("%d", a);这行代码的意思是...将内存地址a处的位打印为%d(int)。
例如,请考虑以下行:

char ch = '0';
printf("%d", ch);

第一行char ch = 0;存储**'0'的二进制值,即在名为ch的地址处的0b110000**。第二行printf("%d", ch);将地址ch处的值打印为%d。基本上,它将值'0b110000'打印为int。转换为十进制的48,因此输出为48
“double”也是一样,存储在“double”变量地址的位将被打印为“int”,因此是垃圾值。“double”的小数部分也存储为位,但“int”类型不会将这些位视为小数部分。您可以阅读更多关于“float”和“doubles”如何存储的信息。

相关问题