器→工具, 编程语言

C语言学习之存储类

钱魏Way · · 10 次浏览

在C语言中,存储类定义了变量/函数的范围(可见性)和生命周期。这些说明符放在编译器前以理解变量的工作方式。C语言中有以下类型的存储类:

  • 自动(Auto):这是所有局部变量的默认存储类。在函数体、循环体等内部声明的变量是自动变量。
  • 寄存器(Register):这是自动的变体类,它告诉编译器使用寄存器来存储变量,而不是RAM。
  • 静态(Static):指示编译器保留变量,即使超出了它的范围也是如此。它还指示编译器在程序启动时对变量进行初始化。
  • 外部(Extern):指示我们的变量是在程序的其他地方定义的,也就是说,这是一个已存在的全局变量的引用。
  • 类型限定符(Type Qualifiers):const, volatile, 和 restrict 是类型限定符,不是存储类,但它们也可以改变变量的存储类。

auto存储类

auto是C语言中的一个存储类,用于声明自动变量。这是所有局部变量的默认存储类。”auto”只能在函数内部使用,它是局部变量的默认类型。下面是一个简单的例子:

#include <stdio.h>

void function() {
   int i = 0; // same as "auto int i = 0;"
   printf("%d ", i);
   i++;
}

int main() {
   function(); // prints: 0
   function(); // prints: 0
   return 0;
}

在这个例子中, i就是一个auto变量。当function()被调用时,变量i被初始化为0。当函数再次被调用时,i又被重新初始化为0。

需要注意的是,在C语言中,我们通常省略auto关键字,直接写变量的类型和名称。因为如果没有指定存储类,编译器默认变量为auto。

register 存储类

register是C语言中的一个存储类,用于建议编译器将某个变量存储在寄存器中,而不是RAM中。这样做的原因是,从寄存器中访问变量比从RAM中快得多。然而,这只是一个建议,编译器可能会忽略这个建议。

register只能用于局部变量和函数的输入参数。寄存器的数量是有限的,且可能已经被用于其他目的,因此并非所有请求都能被满足。

下面是一个使用register的例子:

#include <stdio.h>

void function() {
   register int i; // "register" suggests to store "i" in a register
   for (i=0; i<10000; i++) {
      printf("%d ", i);
   }
}

int main() {
   function();
   return 0;
}

在这个例子中,我们建议编译器将循环变量i存储在寄存器中。这样做是因为i将在循环中被频繁访问,将其存储在寄存器中可能会提高速度。

需要注意的是,对于现代的优化编译器,你可能不需要显式声明register,编译器会自动进行寄存器分配优化。

static 存储类

static是C语言中的一个存储类,它可以用于声明静态变量。

当变量被声明为static时,空间将在程序的生命周期内一直存在,即使超出了它的范围也是如此。同时,static变量在程序启动时只初始化一次。

static存储类有以下用途:

  • 如果在函数体内部声明静态变量,那么这个变量就会在函数调用之间保持其值。
  • 在函数外部声明的静态变量可以在整个文件中访问,而不仅仅是在声明它的函数中。这样,它们就像全局变量,但只能在一个文件中使用。

下面是一个使用static变量的例子:

#include <stdio.h>

void function() {
   static int i = 0; // "static" means "i" keeps its value between function calls
   printf("%d ", i);
   i++;
}

int main() {
   function(); // prints: 0
   function(); // prints: 1
   return 0;
}

在这个例子中,变量i是在函数内部声明的静态变量。尽管function()被调用了两次,但i在两次调用之间保持了它的值。

需要注意的是,如果static变量没有显式初始化,那么它会被隐式初始化为0。

extern 存储类

extern是C语言中的一个存储类,它用于提供一个全局变量的引用,全局变量对所有的程序文件都是可见的。当你使用extern时,对于变量或者函数的定义来说,你是在告诉编译器这个变量或者函数在别的地方已经定义过了,现在只需要使用就可以了。

下面是一个使用extern存储类的例子:

假设有两个C文件,file1.c和file2.c。

file1.c:

#include <stdio.h>

int count ; // Global variable, can be accessed by other files using extern

void main() {
   printf("The value of count is: %d", count);
}

file2.c:

#include <stdio.h>

extern int count; // count is defined in another file, this is just a declaration

void func() {
   count = 5;
}

void main() {
   func();
   printf("The value of count is: %d", count); // prints: 5
}

在这个例子中,count在file1.c中声明并定义,而在file2.c中,我们使用extern关键字来声明变量count。这告诉编译器,变量count是在别的地方定义的,只需要引用它就可以了。

注意,使用extern声明的变量,没有分配存储空间。其存储空间是在变量定义的地方分配的。

类型限定符(Type Qualifiers)

const,volatile和restrict被称为类型限定符,它们并不是存储类,但是它们可以影响变量的行为。

const

这个关键字让我们可以声明常量。常量是在程序执行期间不会改变的值。尝试改变常量的值会导致编译错误。

const int a = 10; // "a" is a constant, its value cannot be changed
a = 20; // Error

volatile

这个关键字用来告诉编译器,该变量的值可能会在外部被意外(不可预测的)改变,这样,编译器就不会对这个变量进行优化,每次引用时都会从其所在的内存中提取,而不会使用保存在寄存器中的备份。这对硬件访问操作、多线程编程等方面非常有用。

volatile int a = 10; // The value of "a" can be changed unexpectedly

restrict

这个关键字是C99标准中引入的,用于告诉编译器,所有修改某个对象或者对象所指向的值的操作,都必须直接通过该对象来完成(也就是说,不存在其他途径可以用来修改该对象或者对象所指向的值)。这个关键字主要被用在指针类型中,能够帮助编译器进行更好的优化。

int arr[10];
int * restrict restp = arr; // restp is the only way to access arr for the scope of restp

请注意,const和volatile可以同时应用于一个变量,例如const volatile int a;声明了一个既是const又是volatile的变量。

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注