除了编译器使用C 语言,它完全知道给定值的类型,没有系统知道给定值的类型。
请注意,C 本身并没有带来任何运行时类型的信息系统。
看看下面的例子:
int i_var;
double d_var;
int main () {
i_var = -23;
d_var = 0.1;
return 0;
}
在代码中,涉及两种不同类型的值,一种存储为整数,另一种存储为双精度值。
分析代码的编译器非常了解它们的确切类型。这里是 gcc 在生成通过传递-fdump-tree-all
给 gcc 生成的代码时保存的类型信息的短片段的转储:
@1 type_decl name: @2 type: @3 srcp: <built-in>:0
chan: @4
@2 identifier_node strg: int lngt: 3
@3 integer_type name: @1 size: @5 algn: 32
prec: 32 sign: signed min : @6
max : @7
...
@5 integer_cst type: @11 low : 32
@6 integer_cst type: @3 high: -1 low : -2147483648
@7 integer_cst type: @3 low : 2147483647
...
@3805 var_decl name: @3810 type: @3 srcp: main.c:3
chan: @3811 size: @5 algn: 32
used: 1
...
@3810 identifier_node strg: i_var lngt: 5
寻找@links,您应该清楚地看到,确实存储了很多关于内存大小、对齐约束以及存储在节点@1-3 和@ 中的“int”类型的允许最小值和最大值的信息5-7。(我省略了@4 节点,因为提到的“ chan ”条目仅用于链接生成树中的任何类型定义)
关于在 main.c 第 3 行声明的变量,众所周知,它持有一个 int 类型的值,正如对节点 @3 的类型引用所见。
如果您不相信我,您肯定可以自己在自己的实验中找到双重条目和 d_var 的条目,他们也会在那里。
看看-S
列出的生成的汇编代码(使用 gcc 传递开关),我们可以看看编译器在代码生成中使用这些信息的方式:
.file "main.c"
.comm i_var,4,4
.comm d_var,8,8
.text
.globl main
.type main, @function
main:
pushl %ebp
movl %esp, %ebp
movl $-23, i_var
fldl .LC0
fstpl d_var
movl $0, %eax
popl %ebp
ret
.size main, .-main
.section .rodata
.align 8
.LC0:
.long -1717986918
.long 1069128089
.ident "GCC: (Debian 4.4.5-8) 4.4.5"
.section .note.GNU-stack,"",@progbits
查看赋值指令,您会发现编译器找到了正确的指令“mov”来分配我们的 int 值和“fstp”来分配我们的“double”值。
然而,除了在机器级别选择的指令之外,没有指示这些值的类型。查看存储在 .LC0 中的值,值 0.1 的类型“double”甚至在两个连续的存储位置中分解为 long,以满足汇编器的已知“类型”。
事实上,以这种方式分解值只是其他可能性的一种选择,使用 8 个连续的 "type" .byte 值也会做得同样好。