c如何处理不同范围内的相同变量名?

时间:2014-02-27 11:46:17

标签: c linux compiler-construction

我有以下代码

int main()
{
        static int x = 8;
        {
                static int x = 9;
        }
        printf("%d",x);
}

O / P-8

我怀疑,根据规则,静态变量只创建一次并保留在内存中。如果名称为x的变量在内存中保留,那么我怎样才能创建一个新变量。

请清楚我的怀疑。我经历了谷歌,但它是如何完成的,我想要的是C编程。如何编译器识别变量以及它如何存储在内存中。

Ť

6 个答案:

答案 0 :(得分:2)

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;
}

答案 1 :(得分:2)

在内部,编译器重命名具有不同范围的同义变量。 考虑一下你的文件:

// 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来自定义其行为)。

另请阅读λ-calculus中的α转换

在实践中,避免使用不同嵌套范围的同义变量。它们使您的代码对人类来说非常难以理解(即使编译器为您的代码提供了非常精确和明确的含义)。 -Wall选项会询问所有警告,并且您会收到有关此类情况的警告。

答案 2 :(得分:1)

在函数调用期间,静态值将保持不变。

但在同一功能范围内单独关注。将printf放在你会理解的支架内

int main()
{
        static int x = 8;
        {
                static int x = 9;
                **printf("%d",x);**
        }
        printf("%d",x);
}

答案 3 :(得分:1)

静态变量只创建一次,并在内存中保留变量的范围。

使用时

{
     static int x = 9;
     printf("%d",x) // would print 9 only
}

这会创建一个新范围,x与在大括号外定义的x不同。

可以使用大括号,函数,文件

创建

范围

如果在全局空间中声明了静态变量,那么它在整个文件中都是持久的。

但是还要记住局部范围变量优先于具有相同名称的全局变量。

答案 4 :(得分:1)

想象一下,每个开口都会创建一个范围{并且每次关闭都会关闭}。并且,对于每个新范围,变量的名称都以范围的名称(可以与函数的名称同化,或者对于匿名构造,如示例中的匿名构造,具有通用名称)加上前缀(精神上) “scope1”,“scope2”,“scope3”序列。

所以,基本上,你定义了两个变量,而不是一个:

static int main.x = 8;

static int main.scope1.x = 9;

这两个都是静态的,但它们(对于编译器和程序的逻辑)有不同的名称,并且表现得像不同的变量。只有他们缩短的名字是相同的,“x”,这是让你困惑的,但变量实际上是不同的。他们尊重你对“静态”的看法,但没有冲突。

一句警告:在“scope1”中,变量“main.scope1.x”隐藏变量“main.x”。

请记住,使用范围名称进行“重命名”仅仅是一个类比,而不是真实的。不过,我希望它有助于理解这个问题。

答案 5 :(得分:1)

理论编译器可以保留遇到符号的堆栈。解析'{'时,编译器可以将此分隔符放入堆栈中,当遇到'}'符号时,它将删除最后一个'{'...'}'区域之间的所有符号。

当编译器解析某个符号时,它可以直接深入到符号堆栈以找到匹配或宣布错误。

在解析此文件的每个关键点,此编译器将具有以下符号堆栈:

main  main   main    main    main     main    main
      {      {       {       {        {
             x       x       x        x
                     {       {
                             x

这种方法实际上非常接近于c-compilers的遗留实现 - x-es也将被放置到现实中。