float f = 2.4;
int n = f + 1;
n = 3
变量f在内存中的值为0x4019999a, 因此我认为f + 1 = 0x4019999a + 1, 但计算机并不这样认为。 如何知道f是“float”类型?即使f仅是内存中的0x4019999a。 变量类型是否存储在某个地方?
float f = 2.4;
int n = f + 1;
n = 3
变量f在内存中的值为0x4019999a, 因此我认为f + 1 = 0x4019999a + 1, 但计算机并不这样认为。 如何知道f是“float”类型?即使f仅是内存中的0x4019999a。 变量类型是否存储在某个地方?
在优化后的生产可执行文件中,类型并不会被显式地存储(调试输出包含各种额外信息)。
"那么如果类型没有被存储,它怎么知道如何处理f = f + 1
和n = n + 1
这两个操作呢?" 我听到你在问。 :-) 答案是编译器在编译时知道了类型,并针对这些操作输出了不同的CPU指令。在f
的情况下,它输出能够处理浮点数值的指令,但在n
的情况下,它输出能够处理二进制补码整数的指令。
变量的类型不会被存储。编译器会根据你编写的代码发出相应的机器码指令。
以你的示例为例:
float f = 2.4;
int n = f + 1;
float
类型的数值2.4
被转换为int
类型,其值为2
。整数值2
加上1
,结果为3
。在内存中,f
的物理表示并不重要。
如果您想将f
作为无符号整数的物理内存表示加上1
,您需要:
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
。
gcc
,您可以尝试向二进制文件的生成添加调试选项。 - pmg#define define(type, name) const char* name ## Type = #type; type name
并像这样使用:define(int, foo) = 5; printf("%s foo = %d", fooType, foo);
- WENDYN