【C语言】-存储类别

0、前言

程序员通过C的内存管理系统指定变量的作用域和生命期,实现对程序的控制。而合理使用内存存储数据是设计程序的一个要点。

1、存储类别相关基本概念

  • 对象(object):从硬件上来看,被存储的每个值都占用一定的物理内存,即这块内存便被称为对象。

  • 标识符(identifier):从软件来看,通过声明对象来访问对象,而标识符可以用来指定特定对象的内容。

  • 作用域(scope):描述程序可以访问标识符的区域。

    • 1、块作用域(block scope):块是用一对花括号扩起来的代码区域,块作用域变量的可见范围是从定义处到包含该定义的块的末尾。(函数的形参也属于块作用域)
    • 2、函数作用域(function scope):仅用于goto语句的标签,即即使一个标签首次出现在函数的内层块中,它的作用域也会延伸至整个函数。
    • 3、函数原型作用域(function prototype scope):用于函数原型中的形参名,即从形参定义处到原型声明结束。即意味着,编译器在处理函数原型中的形参时只关心其类型,而形参名通常无关紧要。
    • 4、文件作用域(file scope):变量的定义在函数的外面。即该变量从它的定义处到该定义所在文件的末尾均可见。
  • 链接(linkage)

    • 1、外部链接:可以在多文件程序中使用。
    • 2、内部链接:只能在一个翻译单元中使用。
    • 3、无链接:具有块作用域、函数作用域或函数原型作用域的变量。
  • 存储期(Storage Period):描述通过标识符访问的对象的生存期。

    • 1、静态存储期:该变量在程序执行期间一直存在,所有的文件作用于变量都具有静态存储期。
    • 2、线程存储期:用于并发程序设计,该对象(关键字_Thread_local)从被声明时到线程结束一直存在。
    • 3、自动存储期:当程序进入定义这些变量的块时,为这些变量分配内存,当退出这个块时,释放刚才分配的内存。
    • 4、动态分配存储期: 用库函数分配和管理内存。

2、五种存储类别

在C语言中存储类别定义了变量或函数的作用域、生命周期和链接属性。其共有五种存储类别,可以通过auto、register、static、extern和默认隐式等关键字声明。

存储类别与变量属性对照表

存储类别关键字作用域生命周期存储位置默认初始化值地址获取链接属性
自动auto代码块代码块执行期间栈内存未定义可获取
寄存器register代码块代码块执行期间寄存器未定义不可获取
静态(内部)static文件程序运行期间静态存储区0可获取内部
静态(局部)static代码块程序运行期间静态存储区0可获取
静态外部extern文件程序运行期间静态存储区0可获取外部
隐式(全局)文件程序运行期间静态存储区0可获取外部

2.1、自动存储类别

属于自动存储类别的变量具有自动存储期、块作用域和无链接。默认情况下,声明在块或者函数头中的任何变量都属于自动存储类别。存储位置是栈内存。

  • 特点:
    • 每次进入代码块时重新创建,退出时销毁。
    • 未初始化时值为未定义(随机)。
    • auto关键字很少使用,可以忽略。
void func() {
    int x = 10;  // 等价于 auto int x = 10;
    // x 在函数执行期间存在
}  // x 在此处销毁

2.2、寄存器存储类别

变量通常存储在计算机内存中,幸运的话,寄存器变量存储在CPU的寄存器中。与普通变量相比,访问和处理这些变量的速度更快。存储位置是CPU 寄存器(或高速缓存)。

  • 特点:
    • 建议编译器将变量存储在寄存器中,提高访问速度。
    • 无法获取变量的地址(即&register_var非法)。
    • 编译器可能忽略register请求,将变量存储在内存中。
void loop() {
    register int i;  // 建议将i存储在寄存器中
    for (i = 0; i < 1000; i++) {
        // 高频访问的变量使用register可能提升性能
    }
}

2.3、无链接和内部链接的静态变量

块作用域的静态变量和自动变量一样,具有相同的作用域,但是程序离开它们所在的函数后,这些变量并不会消失,多次调用该函数也不会重新初始化该变量,即该变量的初始化只执行一次,后续改变的值存储在当前位置,不会丢失。

  • 关键字:static
  • 作用域:
    • 内部链接:文件作用域(仅当前文件可见)。
    • 无链接:代码块作用域(仅函数内可见,但保持全局寿命)。
  • 生命周期:程序整个运行期间
  • 存储位置:静态存储区
  • 特点:
    • 未初始化时自动初始化为0(全局变量也如此)。
    • 内部链接的static变量可避免命名冲突(类似 C++ 的namespace)。

内部链接(文件作用域)

// file1.c
static int file_private = 100;  // 仅file1.c可见

void func() {
    // 可访问file_private
}

// file2.c
extern int file_private;  // 错误!无法外部链接

无链接(块作用域)

void counter() {
    static int count = 0;  // 仅首次调用时初始化
    count++;
    printf("Count: %d\n", count);
}

int main() {
    counter();  // 输出1
    counter();  // 输出2
    return 0;
}

2.4、 外部存储类别(Extern)

  • 关键字:extern
  • 作用域:文件作用域(全局可见)
  • 生命周期:程序整个运行期间
  • 存储位置:静态存储区
  • 特点:
    • 声明已有变量或函数,不分配内存。
    • 用于跨文件共享全局变量或函数。
// file1.c
int global_var = 10;  // 定义全局变量

// file2.c
extern int global_var;  // 声明外部变量

void func() {
    printf("%d\n", global_var);  // 使用file1.c中的global_var
}

3、常见的使用技巧

1、静态变量的常见用途

  • 实现函数内的计数器或状态保持;
  • 缓存计算结果。

2、extern使用技巧
声明与定义分离,比如头文件中函数或外部链接变量声明。
3、register使用
常用于高频访问的变量,但不可获取地址。(且不一定能成功申请为寄存器变量)
4、auto变量
自动变量定义时,一定要进行初始化,否则不可使用其数值。
5、静态变量的线程安全问题
在多线程环境中,静态变量的修改需要加锁保护。

#include <pthread.h>

void thread_func() {
    static int counter = 0;
    pthread_mutex_lock(&mutex);
    counter++;  // 线程不安全操作,需加锁
    pthread_mutex_unlock(&mutex);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值