C 中的存储类别 (auto, extern, static, register)

2025年7月26日 | 阅读 11 分钟

在 C 编程中,存储类别用于确定变量的生命周期、可见性、内存位置和初始值。使用存储类别,我们可以找到并看到变量的作用域,变量的值在函数调用之间是否保持不变,以及变量是存储在 RAM 还是 CPU 存储中。

C 中存储类别的类型

C 中有四种类型的存储类别。它们如下

Storage Classes in C (auto, extern, static, register)

给定的存储类别表显示了它们在不同区域的工作方式,例如存储位置、默认值、作用域和生命周期。

存储类存储位置默认值范围生命周期
Auto内存垃圾值局部在声明它们的函数内部。
Extern内存全球直到主程序结束。可以声明在程序的任何位置。
静态内存局部直到主程序结束。它在多次函数调用之间保留值。
寄存器寄存器垃圾值局部在声明它们的函数内部。

在这里,我们将逐一讨论 C 中的这些存储类别。

自动 (auto) 存储类别

自动 (Auto) 存储类别是局部变量或块变量的默认存储类别。

  • 自动变量的内存会在运行时自动分配。
  • 自动变量的可见性和作用域仅限于定义它们的块。
  • 自动变量默认初始化为垃圾值。
  • 退出块时,分配给自动变量的内存将被释放。
  • 定义自动变量的关键字是 'auto'。

C 中自动存储类别的示例

让我们举一个例子来说明 C 中的自动存储类别。

示例

编译并运行

输出

32766  0.000000 

说明

在此示例中,我们声明了一个整数变量,该变量对 main 方法是局部的。变量 a 默认是 auto。之后,我们声明了一个 char 变量和一个 float 变量。最后,它打印了三个不同类型的已声明的 auto 变量的初始默认值。

C 中自动变量的作用域和隐藏

让我们举一个例子,使用自动存储类别来说明 C 中变量的作用域和隐藏。

示例

编译并运行

输出

11 20 20 20 11 

说明

在此示例中,我们演示了 C 中的变量作用域和隐藏。首先,我们将变量 a 初始化为 10,然后递增到 11 并打印。之后,我们创建了一个嵌套块,其中我们将一个 auto 局部整数变量 a 初始化为 20,然后执行了一个小于 3 的 for 循环,并后递增了 i 的值。它会打印局部变量 a 的值 3 次。块结束后,外部的 a (11) 会再次打印。

C 中的静态存储类别

在 C 编程中,静态存储类别通知编译器将局部变量存储在内存中直到程序退出,而无需每次进入和退出作用域时都创建和销毁它。

  • 定义为 static 说明符的变量可以在多次函数调用之间保持其值。
  • 静态局部变量仅对定义它们的函数或块可见。
  • 同一个静态变量可以声明多次,但只能初始化一次。
  • 静态整数变量的默认初始值为 0;否则为 NULL。
  • 静态全局变量的可见性仅限于声明它的文件。
  • 使用 'static' 关键字定义静态变量。

C 中静态存储类别的示例

让我们举一个例子来说明 C 中的静态存储类别。

示例

编译并运行

输出

0 0 0.000000 (null)  

说明

在此示例中,我们声明了三个 char 类型的静态变量 c、一个 integer 类型的 i 和一个 float 类型的 f。之后,我们声明了一个大小为 100 的 char[] 数组 s。在 main 块中,c、i 和 f 的初始默认值。

C 中静态变量在函数调用之间保留值

让我们举一个例子来说明 C 中静态变量在函数调用之间保留值。

示例

编译并运行

输出

10 24
11 25
12 26

说明

在此示例中,我们创建了一个返回类型为 void 的 sum() 方法。首先,我们初始化了两个静态局部整型变量 a 和 b。之后,a 和 b 的值依次递增 1。在 main() 函数中,我们声明了一个局部整型变量 i。之后,它执行一个 for 循环三次,然后后递增 i 的值。最后,它调用 sum() 方法,该方法告诉静态变量在多次函数调用之间保持其值。

C 中的寄存器存储类别

在 C 编程中,register 存储类别主要用于局部变量,将它们存储在寄存器中,而不是存储在随机存取存储器 (RAM) 中。当一个变量存储在寄存器中时,意味着该变量的最大大小等于寄存器大小,并且不能对其应用一元 AND 运算符。

  • 定义为 register 的变量会在 CPU 寄存器内存中分配,这取决于 CPU 中剩余内存的大小。
  • 我们不能解引用寄存器变量,即不能对寄存器变量使用 & 运算符。
  • 寄存器变量的访问时间比自动变量快。
  • 寄存器局部变量的初始默认值为 0。
  • 使用 'register' 关键字来存储在 CPU 寄存器中的变量。但是,变量是否可以存储在寄存器中取决于编译器。
  • 我们可以将指针存储在寄存器中,即寄存器可以存储变量的地址。
  • 静态变量不能存储在寄存器中,因为我们不能对同一个变量使用多个存储说明符。
  • 当我们想要快速访问变量时,例如访问计数器,可以使用 register 存储类别。

注意:定义 register 并不一定意味着变量将仅存储在寄存器中。它可能存储在寄存器中,也可能不存储,这完全取决于硬件和实现限制。

C 中寄存器存储类别的示例

让我们举一个例子来说明 C 中的 register 存储类别。

示例

编译并运行

输出

0 

说明

在此示例中,我们声明了一个局部寄存器整型变量 a,它将在 CPU 寄存器中分配,初始默认值为 0。最后,它打印变量 a 的值。

C 中的外部 (extern) 存储类别

在 C 编程中,extern 存储类别为全局变量提供了一个引用,并且对所有代码文件都可见。

  • extern 存储类别用于告知编译器,使用 'extern' 关键字定义的变量是在程序其他地方声明的。
  • 声明为 extern 的变量不分配任何内存。它只是一个声明,目的是指定该变量已在程序的其他地方声明。
  • 外部整数类型的默认初始值为 0;否则为 NULL。
  • 我们只能在全局范围内初始化 extern 变量,即不能在任何块或方法内初始化 extern 变量。
  • 外部变量可以声明多次,但只能初始化一次。
  • 如果一个变量被声明为 extern,编译器会在程序中搜索该变量的初始化位置,可能是 extern 或 static。如果不存在,编译器将报错。
  • 当我们想共享两个或更多文件时,使用 extern 修饰符。

C 中外部存储类别的示例

让我们举一个例子来说明 C 中的 extern 存储类别。

示例

编译并运行

输出

main.c:(.text+0x6): undefined reference to `a'
collect2: error: ld returned 1 exit status

说明

在此示例中,我们初始化了一个局部且外部的整型变量 a,这意味着它不会被分配任何内存。之后,它打印变量 a 的值,并抛出错误,因为我们没有给变量 a 任何引用。

C 中带有全局变量的外部 (extern) 示例

让我们举一个例子来说明 C 中带有全局变量的 extern 示例。

示例

编译并运行

输出

0   

说明

在此示例中,我们初始化了一个全局整型变量 a。在 main() 方法中,我们初始化了一个局部的外部整型变量 a,这意味着它不会被分配内存。之后,它打印变量 a 的值。它将打印 0,因为局部变量 a 的引用是通过全局变量 a 完成的,这是 extern 变量的默认值。

C 中使用 extern 关键字访问稍后定义的全局变量的示例

让我们举一个例子来说明如何在 C 中使用 extern 关键字访问稍后定义的全局变量。

示例

编译并运行

输出

20 

说明

在此示例中,我们演示了使用 extern 关键字访问稍后在 C 中定义的全局变量。在 main() 方法中,我们声明了一个局部的 extern 整型变量 a。之后,它打印变量 a 的值,编译器会搜索程序中是否定义并初始化了变量 a。

C 中存储类别的特征

C 中的存储类别有几个关键特征。其中一些如下

  • 可见性: auto 和 register 变量的作用域是块或方法,不能在外部访问。我们可以访问声明它们的同一文件中的全局静态变量。extern 变量允许在不同的代码文件之间共享全局变量,这使得大型程序具有模块化。
  • 存在性: auto 和 register 变量仅在方法执行时存在,并在方法执行完成或退出后销毁。静态变量在整个程序执行期间保留其值,无论方法调用多少次。extern 变量在整个程序执行期间都存在,这对于共享状态很有用。
  • 保持代码的模块化和可读性: 适当使用存储类别可以提高代码的模块化和组织性。通过在需要时限制变量的作用域,可以轻松地进行调试、扩展和维护。
  • 实现更快的循环和性能关键代码: 对于高性能计算,当最小化内存访问时间至关重要时,register 存储类别更好。使用 register 关键字声明的变量存储在 CPU 寄存器中,与存储在 RAM 中的变量相比,访问速度更快。这有助于提高循环和密集数学运算的效率。

C 中存储类的用法

C 中存储类别有多种用法。其中一些如下

  • 隐式使用 auto 存储类别用于局部变量,因为它是局部变量的默认存储类别。
  • 如果我们想频繁快速地访问变量,请使用 register 存储类别。它主要用于 'for' 循环计数器或频繁使用的变量以提高性能。不建议使用 register 存储类别来访问内存地址或大型数据类型。
  • 当需要变量在方法调用之间保留其值时,使用 static 存储类别。
  • 如果我们想限制全局变量以防止任何不必要的修改,请使用 static 存储类别来限制其作用域。
  • extern 存储类别用于在多个文件中访问全局变量。
  • 通过使用函数参数或静态局部变量,而不是将所有变量声明为 extern 变量,来最小化全局变量的使用。

存储类别常见问题解答

1) C 中局部变量的默认存储类别是什么?

在 C 编程中,局部变量的默认存储类别是 'auto'。因此,当我们不指定存储类别时,变量就被视为 'auto' 变量。

2) C 中 'static' 和 'extern' 变量有什么区别?

下面是 extern 和 static 变量之间的区别

  • Static: 'static' 变量在函数调用之间保留其值,并且仅限于其定义的作用域。
  • Extern: 'extern' 变量用于访问在另一个文件或作用域中定义的变量。它不为变量分配存储空间,而是引用一个现有的变量。

3) 是否可以在 C 中用多个存储类别声明一个变量?

不可以,我们不能同时用多个存储类别声明一个变量。但是,可以在不同的作用域中使用不同的存储类别。

4) 具有 'static' 存储类别的变量的生命周期是多久?如果我们不在 C 编程中初始化静态变量会怎样?

在 C 编程中,具有 'static' 存储类别的变量的生命周期持续整个程序。静态变量仅初始化一次,并在函数调用之间保留其值。如果我们不初始化静态变量,它将由 C 编程语言中可用的运行时自动初始化为零或指针类型的空指针。

5) 是否可以使用 '&' (地址) 运算符来使用 'register' 变量?

不,我们不能对 register 变量使用 address-of (&) 运算符,因为它们不能存储在内存中,但可以存储在 CPU 寄存器中。


下一主题C 中的递归