C/C++语言中变量作用域:局部变量,全局变量,文件级变量


C/C++语言中的变量分为全局变量和局部变量。这种划分方式的依据是变量的可见范围或者叫做作用域。

1 局部变量

局部变量指的是定义在{}中的变量,其作用域也在这个范围内。虽然常见的局部变量都是定义在函数体内的,也完全可以人为的增加一对大括号来限定变量作用域。

如下所示:

void f()
{
    float x = 0;
    {
        int a;
    }
}

别小看这个作用域问题,这对于C++的影响远比纯C要大。C语言中局部变量离开作用域时,编译器会插入一个POP 指令来清理变量占用的栈空间。而在C++中,除了POP指令,还要调用析构函数。

class MyClass
{
    MyClass(){}
    ~MyClass(){}
};

void f()
{
    {
        MyClass a;
    } // 此处会C++编译器被插入调用~MyClass()的代码
    do_someting();
}

C++编译器在对象a的作用域结束之前,自动插入调用~MyClass()的汇编代码。

局部变量作用域是由编译器强制实施的,这样一旦出现作用域外访问,编译时就会报错,从而帮助程序员排除错误。

2 全局变量

全局变量的作用域是整个工程,也就是在所有参与链接的文件中都是可见的。这就会导致一个问题-名称冲突。例如下面工程中有3个源文件main.c, 1.c, 2.c。

main.c

#include <stdio.h>
int main(int argc, char** argv)
{
    return 0;
}

1.c

int a = 1;

2.c

int a = 2;

编译每个文件都是可以通过的,但是链接时会报错,因为1.c和2.c使用了同一个名称的全局变量。为此,C语言的全局变量被给予了极坏的形象。甚至不使用全局变量的教条在很大范围内盛行。

然而全局变量在很多时候还是必须的,至少是使用它会让问题变得方便。例如当一个变量是很多函数的参数时。

void f1(int a);
void f2(int a);
void f3(int a);

这样每次调用函数都需要传递这个变量a,当这样的参数个数增多时,会让人变得发狂。如

void f1(int a, int b, int c, int d, int e);
void f2(int a, int b, int c, float g);
void f3(int a, int b, int c, int d);

这种情况在需要保存状态的程序中很常见,如GDI库,OpenGL库等。此时采用全局变量来维护状态数据是非常好的选择。C++看到了这种需要,所以索性把这些状态数据和算法函数绑定到了一起,形成了类的概念,从而简化了代码设计。

3 文件级变量

很多时候,其实程序员需要变量的可见范围既不是整个工程,也不是函数内部,而是在当前文件中可见。C语言为此提供了静态全局变量。static global variable。这个名称完全没有能够反映出变量作用域的范围,是一个非常糟糕的名字。而且起关键字static更是让人摸不着头脑。

static int a = 100;

C语言的设计者或许是为了节省关键字的使用,很多关键字用在不同的地方都有完全不同的含义。这种设计应该是仁者见仁的事情,我个人觉得如果此处使用其他的关键字如internal来标识,会更容易让人理解。

internal int a = 100;

好像在C#中确实存在类似的关键字来表示作用域。

言归正传,static 修饰的全局变量只在定义它的文件内部有效,其他文件内无法引用它。上面的例子改为:
main.c

#include <stdio.h>
int main(int argc, char** argv)
{
    return 0;
}

1.c

static int a = 1;

2.c

int a = 2;

此时,项目会链接成功。因为全局范围内只有一个名为a值为2的全局变量,值为1的那个a只在1.c内有效。

4 C和C++编译器对const常量的一点不同

C++编译器对const常量会自动增加static关键字,使其作用域为文件级别。而C语言编译器则不会。如下代码:

main.c

#include <stdio.h>
int main(int argc, char** argv)
{
    return 0;
}
  • 1

1.c

const int a = 1;
  • 1

2.c

int a = 2;

使用C++编译器可以顺利编译链接成功,但是使用C编译器则在连接时报错。为了代码的可移植性,最好还是手动把 static const都写上。

main.c

#include <stdio.h>
int main(int argc, char** argv)
{
    return 0;
}

1.c

static const int a = 1;

2.c

int a = 2;

上述代码则在C和C++编译器下均可编译链接成功。

本文永久更新链接地址

相关内容