static在C语言中如何使用?

99ANYc3cd6
预计阅读时长 17 分钟
位置: 首页 C语言 正文

static 有三大主要用途:

static 例子 c语言
(图片来源网络,侵删)
  1. 在函数内部:修饰局部变量,使其生命周期延长至整个程序运行期间,但作用域仍限制在函数内部。
  2. 在所有函数外部:修饰全局变量/函数,将其作用域限制在当前源文件(.c 文件)内,实现“内部链接”。
  3. 在结构体(C11标准):修饰结构体成员,使其在所有实例中共享同一份数据,而不是每个实例都有自己的副本。

下面我们通过具体的例子来逐一解释。


修饰局部变量(改变生命周期)

当一个局部变量被 static 修饰时,会发生两件事:

  • 生命周期:从程序开始运行时就存在,直到程序结束才销毁,它不再是“随函数调用而生,随函数结束而亡”。
  • 作用域:仍然仅限于声明它的函数内部,外部无法访问。
  • 初始化:如果没有显式初始化,static 局部变量会被自动初始化为 0(对于数值类型)或 NULL(对于指针类型)。

经典例子:统计函数被调用的次数

#include <stdio.h>
// 普通局部变量的版本
void count_normal() {
    int counter = 0; // 每次调用函数,counter 都被重新创建并初始化为 0
    counter++;
    printf("Normal call count: %d\n", counter);
}
// static 局部变量的版本
void count_static() {
    static int counter = 0; // 程序启动时初始化一次,之后一直存在
    counter++;
    printf("Static call count: %d\n", counter);
}
int main() {
    printf("--- Testing normal variable ---\n");
    for (int i = 0; i < 5; i++) {
        count_normal(); // 每次调用,count 都从 1 开始
    }
    printf("\n--- Testing static variable ---\n");
    for (int i = 0; i < 5; i++) {
        count_static(); // counter 的值在多次调用之间被保留下来
    }
    return 0;
}

输出结果:

static 例子 c语言
(图片来源网络,侵删)
--- Testing normal variable ---
Normal call count: 1
Normal call count: 1
Normal call count: 1
Normal call count: 1
Normal call count: 1
--- Testing static variable ---
Static call count: 1
Static call count: 2
Static call count: 3
Static call count: 4
Static call count: 5

代码分析:

  • count_normal() 中,int counter 是一个普通的局部变量,每次进入函数,它都在栈上分配内存,并初始化为 0,函数执行完毕,内存被释放,所以无论调用多少次,它都只打印 1
  • count_static() 中,static int counter 被存放在了静态存储区(通常是数据段),而不是栈上,它在 main 函数执行之前(程序加载时)就被初始化为 0,并且一直存在,每次调用 count_static(),它都能访问到同一个 counter 变量,因此其值能够累加。

修饰全局变量和函数(改变作用域/链接性)

默认情况下,一个在所有函数之外声明的全局变量或函数是“外部链接”(external linkage)的,这意味着它可以被同一个项目中的其他源文件(通过 extern 声明)访问。

static 修饰全局变量或函数时,它会将“外部链接”变为“内部链接”(internal linkage),这意味着这个变量或函数的作用域被限制在当前源文件内,其他文件即使使用 extern 也无法访问它。

主要目的:

static 例子 c语言
(图片来源网络,侵删)
  1. 防止命名冲突:在大型项目中,不同的文件可能不小心定义了同名的全局变量或函数,使用 static 可以将其作用域限定在文件内,避免冲突。
  2. 封装性:将一些只被单个文件使用的辅助函数或变量隐藏起来,作为文件的“私有”成员,使代码更清晰、更安全。

例子:

假设我们有两个文件,main.cutils.c

utils.c (工具函数文件)

#include <stdio.h>
// 这是一个只希望被 utils.c 内部使用的函数
// 加上 static,它就成了“私有”函数,外部无法调用
static void helper_function() {
    printf("This is a private helper function from utils.c\n");
}
// 这是一个可以被外部调用的公共函数
void public_function() {
    printf("This is a public function from utils.c\n");
    // 内部可以调用私有函数
    helper_function();
}
// 这是一个全局变量,加上 static 后,它只在 utils.c 内部可见
static int internal_data = 100;

main.c (主程序文件)

#include <stdio.h>
// 尝试声明并调用 utils.c 中的函数
// extern void public_function(); // 这个声明是有效的
// extern void helper_function(); // 这个声明会编译警告或错误
// extern int internal_data;     // 这个声明会编译警告或错误
void public_function(); // 假设我们从头文件中知道了这个函数的存在
int main() {
    // 可以正常调用 public_function
    public_function();
    // 输出:
    // This is a public function from utils.c
    // This is a private helper function from utils.c
    // 以下代码如果取消注释,会导致编译错误!
    // helper_function(); // 错误: 找不到函数 helper_function
    // printf("%d\n", internal_data); // 错误: 找不到标识符 internal_data
    return 0;
}

编译与链接:

你可能会想,main.c 怎么知道 public_function 的存在?在实际开发中,我们通常会创建一个头文件 utils.h

utils.h

#ifndef UTILS_H
#define UTILS_H
// 声明公共函数,但不暴露 private 的内容
void public_function();
#endif

然后在 main.c 中包含 utils.h,在 utils.c 中也包含 utils.h


修饰结构体成员(C11 新标准)

这是 C11 标准引入的一个新特性,当一个结构体的成员被声明为 static 时,这个成员的所有实例将共享同一份存储空间

例子:

#include <stdio.h>
// 定义一个结构体
struct SharedCounter {
    int instance_id;
    // 这个 counter 将被所有 SharedCounter 结构体实例共享
    static int counter; 
};
// 在所有函数外部定义并初始化静态成员
// 注意:定义时不需要 static 关键字
int struct SharedCounter::counter = 0;
int main() {
    struct SharedCounter s1;
    struct SharedCounter s2;
    s1.instance_id = 1;
    s2.instance_id = 2;
    // s1 和 s2 修改的是同一个 counter
    s1.counter++;
    printf("s1.counter = %d\n", s1.counter); // 输出 1
    printf("s2.counter = %d\n", s2.counter); // 输出 1 (因为共享)
    s2.counter += 10;
    printf("s1.counter = %d\n", s1.counter); // 输出 11
    printf("s2.counter = %d\n", s2.counter); // 输出 11
    return 0;
}

注意:

  • 这个用法在很多方面类似于 C++ 类中的静态成员。
  • 它在 C 语言中相对较新,可能不是所有编译器都完美支持(尤其是在编译选项开启 C11 标准的情况下)。
  • 在更传统的 C89/C99 标准中,如果想实现类似效果,通常会将共享数据放在结构体外部,并通过函数来访问它。

static 位置 作用 生命周期 作用域 链接性 典型例子
函数内部 (局部变量) 延长生命周期,保留上一次的值 整个程序运行期间 仅限函数内部 无链接 函数调用计数器
所有函数外部 (全局变量/函数) 限制作用域,防止命名冲突 整个程序运行期间 当前源文件 内部链接 文件级的私有工具函数或变量
结构体内 (C11) 成员在所有实例间共享 整个程序运行期间 取决于成员本身 无链接 所有实例共享的计数器或配置

理解 static 的这三种用法对于编写高效、健壮且可维护的 C 语言代码至关重要。

-- 展开阅读全文 --
头像
织梦当前位置为空,如何解决显示问题?
« 上一篇 02-24
C语言sprintf如何处理空格?
下一篇 » 02-24

相关文章

取消
微信二维码
支付宝二维码

目录[+]