C语言笔记2:Storage duration

C语言的存储期(Storage Duration) 是对象(变量)生命周期的核心属性,它决定了对象从创建到销毁的时间段。

C语言标准(C11/C17/C23)明确定义了四种存储期

存储期(Storage Duration)生命周期开始时间生命周期结束时间典型声明方式常见例子内存位置(通常)
static程序启动前程序终止后文件作用域变量
static 修饰的变量
全局变量、文件作用域 static 变量、函数内 static 局部变量数据段 / BSS段
thread(C11起)线程启动时线程终止时thread_local 变量(可与 static/extern 组合)每个线程独立的计数器、全局变量副本线程本地存储(TLS)
automatic进入声明所在的块(block)时离开该块时函数内普通局部变量(默认)
auto(C11前可显式写)
普通局部变量、函数参数栈(Stack)
allocated(动态分配)调用 malloc/calloc/realloc 成功时调用 free 或程序结束(泄漏)通过 malloc 等分配的内存动态数组、链表节点、对象池堆(Heap)

1. static storage duration(静态存储期)

特点

  • 程序整个运行期间都存在
  • 只在程序启动前初始化一次(若未显式初始化,则自动清零)
  • 同一对象在整个程序中只有一份

典型声明方式

// 文件作用域(全局)变量,默认就是 static storage duration
int global_var = 10;          

// 显式 static(文件作用域时相当于给 internal linkage)
static int file_static = 20;

// 函数内部的 static 变量(仍然是 static storage duration)
void func() {
    static int counter = 0;   // 只初始化一次
    counter++;
    printf("%d\n", counter);
}

2. thread storage duration(线程存储期)—— C11 新增

特点

  • 每个线程拥有自己独立的实例
  • 线程开始时分配,线程结束时销毁
  • 初始化在第一次使用时进行(类似函数内 static 的行为)

使用方式(需要包含 <threads.h> 或使用 C11+ 编译器):

#include <threads.h>
#include <stdio.h>

thread_local int tls_counter = 0;   // 每个线程有自己的 tls_counter

int thread_func(void* arg) {
    tls_counter++;
    printf("Thread %d: tls_counter = %d\n", thrd_current(), tls_counter);
    return 0;
}

注意thread_local 可以和 staticextern 一起使用,但不能auto/register 一起使用。

3. automatic storage duration(自动存储期)

特点

  • 每次进入块(block)时分配
  • 离开块时销毁(栈上变量出栈)
  • 未初始化时值不确定(可能是垃圾值)
  • 大多数局部变量都属于这一类

典型例子

void func(int param) {          // param 是 automatic
    int x;                      // automatic,未初始化 → 垃圾值
    auto int y = 10;            // C11 前可写 auto,C11 后基本不用写
    {
        int z = 30;             // 新的 automatic 变量
    } // z 在此处销毁
} // x, param, y 在函数结束时销毁

4. allocated storage duration(分配存储期 / 动态存储期)

特点

  • 由程序员手动控制(malloc/calloc/realloc 创建,free 销毁)
  • 生命周期与调用 free 相关,与任何块或线程无关
  • 未释放会导致内存泄漏
  • 分配失败返回 NULL

典型用法

#include <stdlib.h>

int* create_array(int size) {
    int* p = malloc(size * sizeof(int));  // allocated storage
    if (p == NULL) return NULL;

    // ... 使用 p ...

    return p;  // 记得在外面 free
}

// 使用
int* arr = create_array(100);
if (arr) {
    // 使用...
    free(arr);          // 必须释放
    arr = NULL;         // 好习惯
}

常见问题与面试高频对比

问题static 存储期automatic 存储期thread 存储期
生命周期整个程序当前块当前线程
初始化次数一次每次进入块每个线程一次
默认初始化值0(全局/静态)不确定(垃圾值)0(C11规定)
典型内存位置数据段/BSS线程本地存储(TLS)
能否取地址(&)
多线程下是否安全所有线程共享(需加锁)每个函数调用栈独立天然线程安全(每个线程一份)
函数内声明 static int x; 的行为整个程序一份,保持值
函数内声明 thread_local int x;每个线程一份,保持值

小结表格(便于记忆)

存储期关键字存在时间典型场景是否共享
static无(全局)/ static程序全程全局变量、计数器、配置
threadthread_local线程全程每个线程独立的全局变量
automatic无(局部)/ auto块作用域普通局部变量、循环变量
allocated无(malloc 等分配)手动 free 前动态数据结构(链表、树等)是(需同步)

希望这篇笔记能帮你把 C 语言中“变量能活多久”这个核心概念理清楚!
有哪部分还想再深入(比如 static 在多文件中的 linkage 行为、线程局部变量的初始化细节、VLA 与存储期关系等)可以继续问~

文章已创建 4206

发表回复

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

相关文章

开始在上面输入您的搜索词,然后按回车进行搜索。按ESC取消。

返回顶部