【问题标题】:Variable name precedence in cc中的变量名优先级
【发布时间】:2016-02-10 10:02:56
【问题描述】:

这是a follow up question

考虑以下示例代码:

#include <stdio.h>
int asdf = 5;
int main(void) {
    sub(10);
    return 0;
}
int sub (int asdf)
{
    printf("%d\n",asdf);
}

在这种情况下,全局变量和局部参数都使用相同的名称,asdf。代码输出10,所以使用了局部变量。不会引发编译错误或警告。

全局变量仍然可以从sub() 中访问,当我将sub 的声明更改为

int sub (int asd)

代码输出 5。所以我知道sub() 可以访问全局变量,但它不能。

我无法在 c 标准中找到关于范围优先级的明确定义,所以我的问题是:

考虑到上述情况,命名局部变量和全局变量是否只是一种不好的做法,还是会引发错误/UB?

【问题讨论】:

  • 使用全局变量是不好的做法,除非它们是常量。
  • 它不是 UB,但可以肯定的是,您迟早会在代码中遇到错误。
  • 另外值得注意的是,一些具有大量遗留代码和全局变量的大型项目,使用特殊前缀命名这些全局变量,这些前缀与其他变量不同
  • @MsYvette 当我将sub 的声明更改为int sub (int asd) 时,基本上会更改局部变量名称。

标签: c


【解决方案1】:

来自 C11 草案规范

6.2.1 标识符的范围

4 [...] 如果声明符或类型说明符声明 标识符出现在块内或参数列表内 函数定义中的声明,标识符具有块范围, 它在关联块的末尾终止。

[...] 如果一个标识符在同一个标​​识符中指定了两个不同的实体 名称空间,范围可能重叠。如果是这样,一个实体的范围 (内部范围)将严格在另一个范围之前结束 实体(外部范围)。在内部范围内,标识符 指定在内部范围内声明的实体;声明的实体 在外部范围内隐藏(不可见)在内部范围内。

因此,声明一个名为asdf 的函数参数会在函数中隐藏全局变量asdf。为局部变量和全局变量提供相同的标识符是不好的做法,但规范允许。

【讨论】:

    【解决方案2】:

    考虑到上述情况,命名局部变量和全局变量是否只是一种不好的做法,还是会引发错误/UB?

    这是一种不好的做法,并不是未定义的行为。

    考虑一个包含许多作者的大型程序。在这种情况下,很难跟踪正在使用的变量是函数中的变量还是全局变量。考虑一下如果有任何变化,如果原作者离开了工作并且一个新人试图理解代码会发生什么。程序变得更难维护。

    这也是你应该在代码中避免使用全局变量的原因之一。

    【讨论】:

      【解决方案3】:

      这称为掩蔽。当您有 2 个相同的变量全局是可见的,但在具有与全局变量同名的参数的函数中,函数参数具有更高的优先级并屏蔽全局变量。不会有任何调用错误,但这不是一个好习惯。

      【讨论】:

      • @undur_gongor 隐藏。
      【解决方案4】:

      是的,显然用同名命名两个变量是一种不好的做法。

      在这些情况下,您可以借助关键字extern 进行区分。考虑下面的例子。

      #include <stdio.h>      
      
      int a = 12;             
      
      int main(void)          
      {           
          int a = 15;             
          printf("Inside a's main local a = : %d\n", a);
      
          {
              extern int a;
              printf("In a global a = %d\n", a);
          }
      
          return 0; 
      } 
      

      它会给你op

      Inside a's main local a = : 15
      In a global a = 12
      

      编译器不会抛出任何错误。

      【讨论】:

        【解决方案5】:

        Formal parameters,被视为函数中的局部变量,它们优先于全局变量。

        当您使用如下方法时,您使用的是作为参数传递给方法的变量的值,这取决于方法调用:

        int sub (int asdf)
        {
            printf("%d\n",asdf);
        }
        

        例如:

        sub(10); // asdf is being assigned the value of 10 and asdf is a different variable.
        

        实际上与以下内容相同:

        int sub (int adf)
        {
            printf("%d\n",adf);
        }   
        

        这没有使用传入方法的int参数,所以使用了全局变量值。

        int sub (int adf)
        {
            printf("%d\n",asdf);
        }
        

        或者你可以像这样调用原始方法:

        sub(asdf);
        

        无论哪种方式,它都没有真正的价值,将其命名为全局变量。因为它可以使用int anything 来实现,然后无论如何都可以访问全局变量。它确实可能会造成混淆。

        任何含糊不清的命名都被认为是不好的做法。

        然后是使用全局变量的问题,以一种微不足道的方式,这可能会导致程序走下坡路,这已在此处的其他答案中提到。

        【讨论】:

          【解决方案6】:

          您可以称其为不好的做法,因为它会使您的代码难以阅读,但这不会导致错误。本地名称总是覆盖全局名称。

          想象一下如果本地名称没有覆盖全局名称会发生​​什么:当您与外部代码交互时,您通常不知道已经选择的变量名称。在这种情况下,外部代码可能会突然更改变量的值,而您不会注意到它,因为您将全局变量命名为与其局部变量相同。

          【讨论】:

            【解决方案7】:

            全局变量当然是不好的做法。除此之外,我在这里没有看到关于变量名优先级的任何意外情况。

            很明显,编译器总是首先在最窄的范围内查找变量名,比如x

            • 如果它确实在本地范围内找到x,它将停在那里,并且永远不会检查任何其他范围。当您有两个变量asdf,一个是本地变量,一个是全局变量时,就是这种情况。因为编译器在sub() 中找到了asdf,所以它永远不会寻找全局的。这种行为也称为名称隐藏
            • 只有在本地范围内没有找到x时,才会将搜索范围扩大到更广泛的范围,扩大到全局范围。

            这个名字隐藏的东西也适用于不同的范围,例如:

            int i = 10;
            for( int i = 5; i < 9; i++ )
            {
                printf( "i = %d (inside for loop)\n", i );   // <-- i = 10 is hidden in this loop
            }
            printf( "i = %d (outside for loop)\n", i );
            

            顺便说一句,你的原型不正确,返回类型应该是void,而不是int

            void sub (int asdf);
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2016-11-02
              • 2020-12-20
              • 2016-05-23
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2017-07-10
              相关资源
              最近更新 更多