我有以下代码
int main()
{
static int x = 8;
{
static int x = 9;
}
printf("%d",x);
}
o/p-8
我的疑问,根据规则,静态变量仅创建一次并保留在内存中。因此,如果名称为 x 的变量保留在内存中,那么我如何才能创建一个新变量。
请澄清我的疑问。我通过谷歌但是它是如何完成的,我想要什么C编程。编译器如何识别这两个变量以及它如何存储在内存中。
T
objdump
的反汇编输出可能会给您一些有关编译器(在本例中为gcc
)如何处理这种情况的提示:
$ objdump -d a.out
...
000000000040050c <main>:
40050c: 55 push %rbp
40050d: 48 89 e5 mov %rsp,%rbp
400510: 8b 05 fa 03 20 00 mov 0x2003fa(%rip),%eax # 600910 <x.2163>
400516: 89 c6 mov %eax,%esi
400518: bf fc 05 40 00 mov $0x4005fc,%edi
40051d: b8 00 00 00 00 mov $0x0,%eax
400522: e8 b9 fe ff ff callq 4003e0 <printf@plt>
400527: 8b 05 e7 03 20 00 mov 0x2003e7(%rip),%eax # 600914 <x.2162>
40052d: 89 c6 mov %eax,%esi
40052f: bf fc 05 40 00 mov $0x4005fc,%edi
400534: b8 00 00 00 00 mov $0x0,%eax
400539: e8 a2 fe ff ff callq 4003e0 <printf@plt>
40053e: b8 00 00 00 00 mov $0x0,%eax
400543: 5d pop %rbp
400544: c3 retq
使用
readelf
,我们可以发现每个x
在最终的可执行文件中都有自己的符号:
$ readelf -s a.out
...
45: 0000000000600910 4 OBJECT LOCAL DEFAULT 25 x.2163
46: 0000000000600914 4 OBJECT LOCAL DEFAULT 25 x.2162
这是C代码:
int main()
{
static int x = 8;
{
static int x = 9;
printf("%d",x);
}
printf("%d",x);
return 0;
}
在内部,编译器会重命名具有不同作用域的同义变量。 考虑您的文件:
// file pradipta.c
#include <stdio.h>
int main () {
static int x = 8;
{
static int x = 9;
}
printf ("%d\n", x);
}
然后用
编译它(在 Linux 上使用 GCC) gcc -fdump-tree-all -O -Wall pradipta.c -o pradipta.bin
然后你会得到很多
pradipta.c.[0-9]*t.*
文件。他们向您展示了 GCC 表示的“部分”(因此不完整)转储。一些内部变量可能不同但具有相同的名称。在编译器内部,变量在内部由一些复杂的数据结构表示(GCC 术语中的 tree
节点,在 gimple
指令内使用),并且您可以拥有两个具有相同“可打印”名称的不同此类结构。您还可以使用 MELT
来探索 GCC 内部结构(或通过使用 MELT 扩展来扩展 GCC 来自定义其行为)。 另请阅读
λ-演算中的α-转换。 在实践中,避免不同嵌套作用域的同义变量。它们使您的代码非常难以被人类阅读(即使编译器为您的代码给出了非常精确且明确的含义)。
-Wall
选项要求所有警告,您将收到针对此类情况的警告。
但是在同一函数范围内单独很重要。把 printf 放在大括号里你就会明白了
int main()
{
static int x = 8;
{
static int x = 9;
**printf("%d",x);**
}
printf("%d",x);
}
使用时
{
static int x = 9;
printf("%d",x) // would print 9 only
}
这会创建一个新的范围,并且
x
与在大括号外部定义的
x
不同。 范围可以用大括号、函数、文件创建
如果静态变量在全局空间中声明,那么它在整个文件中是持久的。
但还要记住,局部作用域变量优先于同名的全局变量。
所以,基本上,你定义了两个变量,而不是一个:
静态 int main.x = 8;
和
静态 int main.scope1.x = 9;
这两者都是静态的,但它们(对于编译器和程序逻辑)具有不同的名称,并且充当不同的变量。只是它们的缩写名称相同,“x”,这让您感到困惑,但变量实际上是不同的。他们尊重你所说的“静态”,但并不冲突。
警告一句:在“scope1”内,变量“main.scope1.x”隐藏了变量“main.x”。
请记住,这种使用范围名称的“重命名”只是一个类比,而不是真实的事情。尽管如此,我希望它有助于理解这个问题。
。当解析'{'时,编译器可以将此分隔符放入堆栈中,当遇到'}'符号时,它将删除最后一个'{'...'}'区域之间的所有符号。 当编译器解析某些符号时,它可以简单地深入到符号堆栈中以找到匹配项或宣布错误。
在解析该文件的每个关键点,该编译器都会有一个符号堆栈:
main main main main main main main
{ { { { {
x x x x
{ {
x
这种方法实际上与 C 编译器的遗留实现非常接近——x-es 在现实中也将被放置到堆栈中。