本文主要介绍C语言的变量作用域、以及不同位置声明定义的变量的存储类型,extern关键字在声明函数和变量时的不同含义。

变量作用域

  • 声明和定义的区别:

    • 声明:可以多次,不分配存储空间,声明的位置决定作用域,主要用来向程序表明变量的类型和名称,给编译器提供引用标识

    • 定义:只能一次,给变量分配存储空间,还可以初始化变量,无声明的情况下决定作用域。

    • 定义是一种特殊的声明,最重要的区别在于是否分配内存。在C语言中,所有变量在使用之前必须先声明,可以使用extern关键字来声明已经定义的变量,extern关键字会扩大变量的作用域。

      // main.c
      #include <stdio.h>
      #include "test.h"
      extern int d; //声明变量d,变量d在test.c中定义,必须先声明才能使用,可以在main.c或test.h中进行声明,变量声明不会分配内存
      int main(){
          int a=100;//定义并声明变量a,并对变量a进行初始化,已分配内存
          int b; //定义并声明变量b,不对变量b初始化,已分配内存
          b=20;
          printf("a=%d,b=%d\n",a,b);
          fun_test();
          printf("c=%d,d=%d\n",c,d);
      }
      
      //test.h
      extern int c; //声明变量c,不分配内存
      extern void fun_test(); //声明函数fun_test
      
      //test.c
      #include <stdio.h>
      #include <unistd.h>
      
      int c=30; //定义并声明变量c,分配内存并初始化
      int d; //定义并声明变量d,分配内存但未初始化
      
      void fun_test(){
          d=50;
      }
      
  • 编译器可以确认4中不同类型的作用域:文件作用域、函数作用域、代码块作用域和原型作用域。标识符声明的位置决定它的作用域。

  • 所有代码块之外声明的标识符都是具有文件作用域,它表示该变量从它的声明处到文件结尾都是可以访问的。

  • 函数原型中声明的参数具有原型作用域。

  • 在代码块开始位置声明的标识符具有代码块作用域。

  • goto语句的标签具有函数作用域

链接属性

  • 标识符的链接属性决定如何处理不同文件中出现的标识符。标识符的作用域与它的链接属性有关。链接属性可以改变标识符的作用域

  • 链接属性一共有3个:

    • external:多个文件中声明的同名标识符表示同一个实体。
    • internal:单个文件中声明的同名标识符表示同一个实体。
    • none: 声明的同名标识符代表独立不同实体。
  • 只有具备文件作用域的标识符才能拥有external或internal链接属性,其他标识符都是none属性

  • 默认情况下,具备文件作用域的标识符拥有external链接属性,也就是说该标识符可以跨文件访问。对于external属性的标识符,无论在不同文件中声明多少次,代表着都是同一个实体。

  • 使用static关键字可以使得原先拥有external属性的标识符变为internal属性。static改变标识符链接属性,只对具有文件作用域的标识符有效。链接属性只能修改一次,一旦从external变为internal就无法变回。

存储类型

  • 存储类型指的是存储变量的值的内存类型。
  • 有三个地方可以存储变量值,分别是普通内存、运行时堆栈以及硬件寄存器。
  • 代码块之外定义的变量存储在静态内存中。
  • 代码块内部定义的变量存储在运行时堆栈中,该变量被定义为自动变量。
  • 要是自动变量存储在硬件寄存器中,在定义变量时需要使用register关键字。
  • static关键字用于代码块内部的变量时,会将变量的存储类型从自动变量改为静态变量。

static关键字

  • static关键字有两个作用,一是改变标识符的链接属性,二是改变变量的存储类型。
  • static关键字作用于具有文件作用域的标识符时,会将标识符的链接属性从默认的external变为internal。
  • static关键字作用域具有代码块作用域的标识符时,会将标识符的存储类型从自动变量变为静态变量,即变量的存储位置从运行时堆栈改为静态内存。

extern关键字

  • extern关键字将标识符的链接属性设置为external,可以在不同的源文件中访问。

  • extern关键字用来声明标识符时,表明该标识符定义在其他源文件中。(变量和函数)

    // main.c
    #include <stdio.h>
    #include "test.h"
    extern int d; //extern关键字用来声明变量d,表明变量d定义在其他文件中
    int main(){
        int a=100;//定义并声明变量a,并对变量a进行初始化,已分配内存
        int b; //定义并声明变量b,不对变量b初始化,已分配内存
        b=20;
        printf("a=%d,b=%d\n",a,b);
        fun_test();
        printf("c=%d,d=%d\n",c,d);
    }
    
    //test.h
    //extern关键字用来声明变量c,表明变量c定义在其他源文件中。
    extern int c;
    //extern关键字用来声明函数,表明函数定义在其他源文件中。
    //因为函数的默认链接属性时extern,因此此处的extern只是显式的指明了链接属性。此处的extern可有可无。
    extern void fun_test();