C语言之作用域 链接属性 存储时期 存储类型

本文详细解析了C语言中的标识符与变量概念,包括作用域、链接属性、存储时期和存储类型的定义及其相互间的关系。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

  • 重要的概念
    标识符: C语言中变量名, 函数名, 函数参数名都称为标识符.
    作用域: 标识符的作用域指的是此标志符在当前源代码中可见(可被访问)的范围
    链接属性: 标识符的链接属性指的是如何处理不同文件中出现的相同标识符
    存储时期: 变量的存储时期指的是变量内存在程序执行中存在的时期
    存储类型: 变量的存储类指的是存储变量的内存的类型
    存储类说明符: C语言的存储类说明符用来改变标识符, 变量的链接属性, 存储时期和存储类型.

C语言的标识符有4种不同类型的作用域:

  • 代码块作用域
    位于一对花括号之间的所有语句称为一个代码块, 在任意代码块内 声明 的标识符具有代码块作用域, 作用域为该标识符声明的位置到代码块结束花括号..
#include <stdio.h>
int main(void)
{ // 1
    int num; // 具有代码块作用域, 作用域为1~2
    for(int i = 0; i < 10; i++){ // 3, i也具有代码块作用域, 作用域为3~4
        char ch; // 具有代码块作用域, 作用域为3~4
        ...
    } // 4
} // 2
  • 文件作用域
    一个源代码文件表示一个文件单位, 在所有代码块之外声明的标识符都具有文件作用域, 从它们声明之处到文件结尾都可以被访问到. 比如在 main 函数外声明的变量.
// main.c文件
#include <stdio.h>
int n = 1; // 具有文件作用域, 在整个main.c文件中可见
void print(void); // print具有文件作用域

int main()
{
    printf("%d", n); // main函数中访问n

    return 0;
}
void print(void)
{
    printf("%d", n); // 自定义函数中访问n
}
  • 原型作用域
    在函数原型(函数声明)中, 形参列表中的形参名具有原型作用域. 形参名只在形参列表中可见(两个圆括号之间).
// 函数原型
void print(int n, char ch); // n和ch只在形参列表中可见
  • 函数作用域
    简单来说, 函数作用域指的是语句标签可见范围为函数内. 由于语句标签更 goto 相关, 而使用goto是不好的习惯, 所以不讨论函数作用域.

C语言标识符有3种不同的链接属性:

  • external(外部)链接属性
    具有external(外部)链接属性的标识符在不同源文件的均表示同一实体.
    • 具有文件作用域的标识符在缺省存储类说明符的情况下具有external(外部)链接属性
    • 使用extern关键字可以改变标识符的链接属性. 详细的用法
// main.c
#include <stdio.h>
int n = 3; // n具有文件作用域, 外部链接属性
void print(void); // print具有文件作用域外部链接属性
int main(void)
{
    print();
    return 0;
}

// print.c
void print(void)
{
    printf("hello world");
}
  • internal(内部)链接属性
    具有internal(内部)连接属性的标识符在同一源文件内的所有声明都指向同一实体, 但是位于不同源文件的多个声明则分属不同的实体.

    • 正常情况下具有external(外部)链接属性的标识符使用static存储类说明符修饰后具有internal(内部)链接属性
  • none(无)链接属性
    none(无)链接属性的标识符的多个声明均表示不同实体.

    • 具有代码块作用域或者函数原型作用域的标识符具有none(无)链接属性

C语言变量有2种存储时期

  • auto(自动)存储期
    具有自动存储时期的变量在定义变量时分配内存, 在退出定义变量的代码块时销毁内存.

    • 具有代码块作用域的标识符代表的变量在缺省存储类修饰符的情况下具有auto(自动)存储期
  • static(静态)存储期
    具有静态存储期的变量在程序执行的整个时期都存在.

    • 具有文件作用域的标识符代表的变量具有static(静态)存储期
    • 具有代码块作用域的标识符代表的变量在使用static存储类修饰符后具有static(静态)存储期

C语言变量有3种存储类型

  • 静态内存
    在任何代码块之外声明的变量存储在静态内存中.

    • 具有文件作用域的标识符代表的变量存储于静态内存中
    • 具有代码块作用域的标识符代表的变量在使用static存储类修饰符后也存储于静态内存中
  • 运行时堆栈
    在代码块内部声明的变量缺省情况下是存储在运行时堆栈中.

    • 具有代码块作用域的标识符代表的变量在缺省存储类修饰符的情况下存储与运行时堆栈
  • 硬件寄存器
    使用 register 关键字声明的变量优先存储在寄存器中, 可以更加快速的读取访问.

    • 具有代码块作用域的标识符代表的变量在使用register存储类修饰符后可能存储于硬件寄存器中

C语言有5种存储类说明符
详细的用法


关于本文的几个关键点:

  • 分清楚标识符与变量的概念
    标识符是从源代码文件的角度定义的, 指的就是源代码中变量的名字, 函数的名字, 参数的名字等. 变量更多时候是从内存的角度定义的, 指的是一段内存空间. 显然, 变量名标识符有时是与变量内存 “绑定” 的.

  • 标识符的属性
    作用域与链接属性指的是标识符的属性. 作用域表示某个标识符的可访问范围, 链接属性表示看待不同文件中出现的相同的标识符的方式.

  • 变量的属性
    存储时期和存储类型都是针对变量来说的. 存储时期反映了变量在何时创建何时销毁, 存储类型反映了变量 “放在哪”.

  • 存储时期与存储类型的关系
    从上面可以看出, 存储类型为寄存器的变量是自动存储期变量的一种特殊情况. 可以用下图反应二者的关系:
    存储时期与存储类型

  • 标识符的作用域只与标志声明的位置有关
    存储类说明符无法改变标识符的作用域. 确定标识符的作用域至关重要.
    这里可能会有个疑问, 比如, 标识符最大的作用域是一个文件, 那么 为什么在一个文件中定义的函数可以在另一个文件中调用呢? 其实这是通过 声明 外部链接 来实现的. 首先, 既然我们想在main.c文件中调用print.c文件中定义的函数, 而函数名标识符的作用域是print,c文件, 那我们就要在main.c文件中再次声明(注意, 不是定义)这个函数; 但是, 程序又怎么知道main.c文件中的print函数指的就是print.c文件中的print函数呢? 所以我们需要设置函数名标识符为外部链接属性, 这样print标识符在main.c和print.c中指向的就是同一实体. 事实上, 由于 具有文件作用域的标识符在缺省存储类说明符的情况下具有external(外部)链接属性 所以函数名默认为外部链接属性, 我们不需要显式的设置. 那么对于变量标识符呢? 如何实现在一个文件中使用另一个文件中定义的变量呢? 方法是同样的. 但是对于变量标识符会带来一个问题. 我们可以很容易的区分函数声明与函数定义, 但是不那么容易区分变量声明与变量定义. 比如我们在main.c中定义了 int i = 5; 想要在print.c中使用 i 时就需要再次声明 i 如果简单的在print.c中添加 int i; 会造成 i 的重复定义. 这是不允许的. 这时就需要使用 extern 关键字再次声明而不是定义 i .

  • 存储类型说明符独立的修改标识符链接属性和变量存储类型
    存储类型说明符可以修改标识符的链接属性和变量的存储类型(存储时期和存储类型相关), 并且这种修改是独立的. 比如, static 关键字用于具有文件作用域的标识符时修改的就是该标识符的链接属性, 而作用于代码块内定义变量时修改的就是该变量的存储类型.

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值