c 如何在不同作用域处理相同的变量名?

问题描述 投票:0回答:6

我有以下代码

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

o/p-8

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

请澄清我的疑问。我通过谷歌但是它是如何完成的,我想要什么C编程。编译器如何识别这两个变量以及它如何存储在内存中。

T

c scope shadowing
6个回答
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;
}

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 来自定义其行为)。 另请阅读

λ-演算

中的α-转换 在实践中,避免不同嵌套作用域的同义变量。它们使您的代码非常难以被人类阅读(即使编译器为您的代码给出了非常精确且明确的含义)。

-Wall

选项要求所有警告,您将收到针对此类情况的警告。

    


1
投票

但是在同一函数范围内单独很重要。把 printf 放在大括号里你就会明白了

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



1
投票

使用时

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

这会创建一个新的范围,并且 
x

与在大括号外部定义的

x
不同。

范围可以用大括号、函数、文件创建

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

但还要记住,局部作用域变量优先于同名的全局变量。


1
投票

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

静态 int main.x = 8;

静态 int main.scope1.x = 9;

这两者都是静态的,但它们(对于编译器和程序逻辑)具有不同的名称,并且充当不同的变量。只是它们的缩写名称相同,“x”,这让您感到困惑,但变量实际上是不同的。他们尊重你所说的“静态”,但并不冲突。

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

请记住,这种使用范围名称的“重命名”只是一个类比,而不是真实的事情。尽管如此,我希望它有助于理解这个问题。


1
投票
堆栈

。当解析'{'时,编译器可以将此分隔符放入堆栈中,当遇到'}'符号时,它将删除最后一个'{'...'}'区域之间的所有符号。 当编译器解析某些符号时,它可以简单地深入到符号堆栈中以找到匹配项或宣布错误。

在解析该文件的每个关键点,该编译器都会有一个符号堆栈:

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

这种方法实际上与 C 编译器的遗留实现非常接近——x-es 在现实中也将被放置到堆栈中。

© www.soinside.com 2019 - 2024. All rights reserved.