例如
float f = 2.4;
int n = f + 1;
n = 3
变量f在内存中为0x4019999a,所以f + 1 = 0x4019999a + 1但是电脑没有。如何知道f是"浮点数"?即使f在内存中只是0x4019999a。变量的类型是否存储在某个地方?
类型不是显式存储的(在优化的生产可执行文件中;调试输出中有各种额外的信息)。
"如果类型没有被存储,它如何知道如何处理f = f + 1
和n = n + 1
的不同?"我听到你问了。答案是编译器在编译时知道类型,并且为这些操作输出不同的CPU指令。在f
的情况下,它输出的指令是处理浮点值的,而在n
的情况下,它输出的指令是处理双补整数的。
不存储变量的类型。编译器根据你写的代码发出机器码指令。
在你的例子中:
float f = 2.4;
int n = f + 1;
float
value2.4
正在转换为int
,其值为2
。在整型值2
上增加1
。结果是3
。f
在内存中的物理表示并不重要。
如果您想将1
添加到作为无符号整数的f
的物理内存表示中,您需要:
float f = 2.4f;
unsigned n,m;
memcpy(&n, &f, sizeof(n));
m = n + 1;
类型仅在编译器分析源代码并生成等效机器码时在翻译过程中起作用。在这种情况下,像"一元*
的操作数"这样的规则必须具有指针类型";+
的操作数必须是算术类型"函数调用中的形参类型和数量必须与函数声明匹配;等等都是强制执行的
类型信息没有显式存储在生成的机器码中。根据源代码中使用的类型,生成的机器码将根据对象的大小和是否为浮点数使用不同的操作码和寄存器。例如,在x86上,当两个单精度浮点数相加时,编译器会在机器码中添加addss
指令;当两个32位的int
加在一起时,它会加addl
指令。
所以我认为f + 1 = 0x4019999a + 1,但计算机不。
int
不能存储小数值——C中的规则是,当您将浮点值赋给整数目标时,小数部分将被截断。n
不能存储3.4
,只能存储3
。
我把上面的代码片段包装成一个完整的程序,编译它1,然后查看生成的机器码。行
float f = 2.4;
int n = f + 1;
翻译成以下内容:
movss LCPI0_0(%rip), %xmm0 ## xmm0 = mem[0],zero,zero,zero
movss LCPI0_1(%rip), %xmm1 ## xmm1 = mem[0],zero,zero,zero
...
movss %xmm1, -8(%rbp)
addss -8(%rbp), %xmm0
cvttss2si %xmm0, %eax
movl %eax, -12(%rbp)
movss
指令将单精度浮点值从一个位置复制到另一个位置——在这种情况下,它将浮点值2.4
和1.0
分别复制到浮点寄存器%xmm0
和%xmm1
。将"%xmm1
(2.4
)"中的值复制到位置"-8(%rpb)
",即变量"f
"的空间。该值添加到%xmm0
(1.0
)中的值。此时,%xmm0
的值为3.4
。然后通过cvttss2si
指令将该值转换为其等效的整数(3
),并将结果存储在32位通用(即非浮点)寄存器%eax
中。然后将结果复制到n
(-12(%rbp)
)。
请记住,浮点值和整数值具有完全不同的表示。32位整型值1
的二进制表示为0x00000001
;单精度浮点值1.0
的二进制表示为0x3f800000
。
- Apple LLVM version 10.0.1 (clang1001.0.46.4)
目标:x86_64-apple-darwin18.6.0