c中的变量名优先级

时间:2016-02-10 10:02:56

标签: c

这是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?

7 个答案:

答案 0 :(得分:6)

来自C11草案规范

  

6.2.1 标识符范围

     

4 [...]如果是声明的声明符或类型说明符   标识符出现在块内或参数列表中   函数定义中的声明,标识符有块作用域,   它终止于相关块的末尾。

     

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

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

答案 1 :(得分:2)

  

考虑到上面的场景,将本地和全局变量命名为仅仅是不好的做法,还是调用错误/ UB?

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

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

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

答案 2 :(得分:1)

它被称为掩蔽。当你有2个相同的变量时,全局是可见的,但是在函数中具有与全局变量同名的参数,函数参数具有更高的优先级并且掩盖全局变量。不会有任何被调用的错误,但这不是好的做法。

答案 3 :(得分:1)

  

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实现,然后可以访问全局变量。它确实可能造成混淆。

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

然后出现了使用全局变量的问题,这是一个微不足道的问题以及这可能会导致程序跟踪的问题,这在其他答案中已经提到过了。

答案 4 :(得分:1)

  

是的,显然,命名两个具有相同名称的变量是一种不好的做法。

您可以在这些方案中使用关键字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 :(得分:0)

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

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

答案 6 :(得分:0)

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

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

  • 如果它确实在本地范围内找到x,它将停在那里,并且永远不会检查任何其他范围。当您有两个变量asdf,一个本地变量和一个全局变量时就是这种情况。因为编译器在asdf中找到了sub(),所以它永远不会寻找全局的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);

const char* global = "Example";

void foo() {
  printf("%s\n",  global );
}