目录

作用域

所谓作用域(Scope),就是变量的有效范围。C 语言中所有的变量都有自己的作用域,决定变量作用域的是变量的定义位置。

  • 局部变量(Local Variable):定义在函数内部的变量称为局部变量,包括函数形参变量。实参给形参传值的过程也就是给局部变量赋值的过程。
  • 全局变量(Global Variable):它的作用域默认是整个程序,也就是所有的源文件,包括 .c 和 .h 文件。

注:局部变量和全局变量的名称可以相同,但是在函数内,如果两个名字相同,会优先使用局部变量值,全局变量不会被使用。

全局变量与局部变量在内存中的区别:

  • 全局变量保存在内存的全局存储区中,占用静态的存储单元;
  • 局部变量保存在栈中,只有在所在函数被调用时才动态地为变量分配存储单元。
  • 当局部变量被定义时,系统不会对其初始化,您必须自行对其初始化。定义全局变量时,系统会自动对其初始化。

C 语言编程 — 作用域和存储器_C语言

注:正确地初始化变量是一个良好的编程习惯。

如果需要在一个源文件中引用另外一个源文件中定义的变量,我们只需在引用的源文件中将变量加上 extern 关键字的声明即可。如下例:

  • addtwonum.c
#include <stdio.h>

extern int x;
extern int y;

int addtwonum(){
    return x + y;
}
  • main.c
#include <stdio.h>

int x=1;
int y=1;

int addtwonum();

int main(){
    int result;
    result = addtwonum();
    printf("result : %d", result);

    return 0;
}

运行

$ gcc -Wall main.c addtwonum.c -o main

$ ./main
result : 2

注意,因为 C 语言代码是从前往后依次执行的,所以全局变量通常位于源文件的顶部。

int a, b;  //全局变量
void func1(){
    //TODO:
}
float x,y;  //全局变量
int func2(){
    //TODO:
}
int main(){
    //TODO:
    return 0;
}

a、b、x、y 都是在函数外部定义的全局变量。由于 x、y 定义在函数 func1() 之后,所以在 func1() 内无效;而 a、b 定义在源程序的开头,所以在 func1()、func2() 和 main() 内都有效。

存储类

存储类定义了 C 程序中的变量/函数的作用域和生命周期,通过特殊的修饰符(关键字)来进行修饰不同的变量/函数,使得他们具有灵活的作用域。下面列出 C 程序中可用的存储类:

  • auto
  • register
  • static
  • extern

auto 修饰符

auto 存储类是所有局部变量默认的存储类。auto 只能用在函数内,即 auto 只能用于修饰局部变量。

下述实例定义了两个具有相同存储类的变量:

{
   int mount;
   auto int month;
}

register 修饰符

register 存储类用于定义存储在寄存器中而不是内存中的局部变量。这意味着变量的最大 Size 等于寄存器的 Size,且不能对它应用一元运算符 ‘&’ (因为它没有内存空间)。

寄存器只用于需要快速访问的变量,比如:计数器。还应注意的是,使用了 register 修饰符进行修复并不意味着变量将被存储在寄存器中,它意味着变量可能存储在寄存器中,这取决于硬件和实现的限制。

{
   register int  miles;
}

static 修饰符

被 static 修饰的变量和常量被称为 静态变量静态常量

  • 修饰变量,修改变量的存储区域和生命周期,使变量存储在静态区,在 main 函数运行前就分配了空间,如果有初始值就用初始值初始化它,如果没有初始值系统用默认值初始化它。

    • 用于修饰局部变量时:编译器在程序的生命周期内保持局部变量的存在,不会在每次进入和离开函数的时候对局部变量进行重新的创建和销毁(重新赋值)。因此,使用 static 修饰局部变量可以在函数调用之间保持住局部变量的值不被重置。

    • 用于修饰全局变量时:会使变量的作用域限制在声明它的源文件内,而不再是整个程序。

  • 修饰函数,表明函数的作用范围,仅在定义该函数的文件内才能使用。在多人开发项目时,为了防止与他人命名空间里的函数重名,可以将函数定位为 static。

#include <stdio.h>
 
/* 函数声明 */
void func1(void);
 
static int count=10;        /* 全局变量 - static 是默认的 */
 
int main()
{
  while (count--) {
      func1();
  }
  return 0;
}
 
void func1(void)
{
/* 'thingy' 是 'func1' 的局部变量 - 只初始化一次
 * 每次调用函数 'func1' 'thingy' 值不会被重置。
 */                
  static int thingy=5;
  thingy++;
  printf(" thingy 为 %d , count 为 %d\n", thingy, count);
}

运行:

$ ./main
thingy 6, count 9
thingy 7, count 8
thingy 8, count 7
thingy 9, count 6
thingy 10, count 5
thingy 11, count 4
thingy 12, count 3
thingy 13, count 2
thingy 14, count 1
thingy 15, count 0

可见,静态变量 thingy 不会在每次函数调用时重置。

extern 修饰符

extern 存储类用于提供一个全局变量/函数的引用。全局变量/函数的作用域是整个程序,当你在一个源文件中想引用另外一些源文件中定义的全局变量/函数时,你可以在该源文件中使用 extern 修饰符,修饰一个全局变量/函数,而且不需要进行初始化(因为该全局变量实际上在另一个源文件中已经完成了初始化)。

简而言之,extern 修饰符就是用与在一个文件中,对另外一个完全中的全局变量或函数进行声明(但不定义),向编译器保证该变量/函数的存在(即便变量/函数的定义并不在本文件中)。

  • main.c
#include <stdio.h>
 
int count ;
extern void write_extern();
 
int main()
{
   count = 5;
   write_extern();
}
  • support.c
#include <stdio.h>
 
extern int count;
 
void write_extern(void)
{
   printf("count is %d\n", count);
}