static 与 extern
static 和 extern 是 C 语言中控制变量和函数可见性的两个关键字。static 用于限制标识符只在当前文件内可见(内部链接),或让局部变量具有静态存储期。extern 用于声明在其他地方定义的标识符。理解它们的精确语义,是组织多文件项目的基础。
static 关键字
static 在不同上下文中有不同含义:
全局变量/函数:内部链接
/* 只在当前文件可见 */
static int internal_count = 0;
static void helper(void)
{
/* ... */
}
局部变量:静态存储期
void counter(void)
{
static int count = 0; /* 只初始化一次,跨调用保持值 */
count++;
printf("%d\n", count);
}
/* 第一次调用:1 */
/* 第二次调用:2 */
/* 第三次调用:3 */
extern 关键字
extern 声明一个在其他地方定义的变量或函数:
/* 声明外部变量(不分配内存) */
extern int global_count;
/* 声明外部函数 */
extern void increment(void);
extern 声明可以出现在任何作用域:
/* 文件作用域的 extern */
extern int shared;
void func(void)
{
/* 块作用域的 extern */
extern int shared; /* 引用文件作用域的 shared */
printf("%d\n", shared);
}
块作用域的 extern 声明引用的是文件作用域的变量,不是创建新变量。
定义 vs 声明
定义:创建实际的变量,分配内存:
int global = 10; /* 定义 */
void func(void) { } /* 定义 */
声明:告诉编译器标识符的类型和名称,不分配内存:
extern int global; /* 声明 */
extern void func(void); /* 声明 */
一个标识符只能定义一次,但可以声明多次。
头文件中的使用
头文件应该只包含声明,不包含定义:
/* utils.h */
#ifndef UTILS_H
#define UTILS_H
/* 声明 */
extern int global_counter;
extern void increment(void);
/* 常量可以定义 */
#define MAX_SIZE 100
/* 内联函数可以定义(C99) */
static inline int max(int a, int b) /* static inline */
{
return (a > b) ? a : b;
}
#endif
/* utils.c */
#include "utils.h"
/* 定义 */
int global_counter = 0;
void increment(void)
{
global_counter++;
}
常见模式
单例模式(模拟):
/* counter.c */
static int count = 0; /* 内部链接:外部不可直接访问 */
int get_count(void) /* 外部链接:通过函数访问 */
{
return count;
}
void increment(void)
{
count++;
}
模块接口:
/* math_utils.c */
static double pi = 3.141592653589793; /* 内部常量 */
static double square(double x) /* 内部辅助函数 */
{
return x * x;
}
double circle_area(double r) /* 外部接口 */
{
return pi * square(r);
}
常见错误
头文件中定义变量:
/* bad.h */
int shared = 0; /* 危险:每个包含此头文件的 .c 文件都有定义 */
/* good.h */
extern int shared; /* 声明 */
/* good.c */
int shared = 0; /* 定义 */
extern 带初始化器:
extern int x = 10; /* 有初始化器,这是定义! */
/* 如果其他文件也定义了 x,链接错误 */
忘记 extern:
/* file1.c */
int count = 0;
/* file2.c */
int count; /* 隐式定义(C89)或重复定义(C99) */
/* 应该是:extern int count; */
最佳实践
- 全局变量默认用
static限制为内部链接 - 需要共享的变量:一个 .c 文件定义,头文件
extern声明 - 不要在头文件中定义变量或函数(
static inline除外) - 用
static函数隐藏内部实现细节 - 用
extern声明引用其他文件的标识符 - 区分定义(分配内存)和声明(不分配内存)