变量声明与定义
变量是程序中存储数据的命名内存空间。在 C 语言中,使用变量前必须先声明或定义,告诉编译器变量的类型和名称。C99 允许声明与代码交织,不必像 C89 那样把所有声明放在代码块开头,这让代码更灵活,可以在需要时才引入变量。
声明与定义的区别
定义创建实际的变量,分配内存空间:
int count; /* 定义:分配内存,未初始化 */
int total = 0; /* 定义并初始化 */
声明告诉编译器变量的类型和名称,但不分配内存(用于引用在其他地方定义的变量):
extern int global_count; /* 声明:global_count 在其他文件中定义 */
一个变量只能被定义一次,但可以被声明多次。
基本语法
变量定义的格式为:类型 标识符 [= 初始值];
int age; /* 定义未初始化的 int 变量 */
int score = 100; /* 定义并初始化为 100 */
double pi = 3.14159; /* 定义并初始化 double */
char grade = 'A'; /* 定义并初始化 char */
bool passed = true; /* 定义并初始化 bool(C99) */
可以在一条语句中定义多个同类型变量:
int a, b, c; /* 三个 int 变量,均未初始化 */
int x = 1, y = 2, z = 3; /* 三个 int 变量,均初始化 */
初始化与未初始化
自动变量(函数内的局部变量)如果不初始化,其值是不确定的(垃圾值):
int main(void)
{
int x;
printf("%d\n", x); /* 未定义行为!x 的值不确定 */
return 0;
}
某些编译器在调试模式下会将未初始化变量设为 0,但发布模式下不会。永远不要依赖未初始化变量的值。
静态变量(static 修饰的局部变量和全局变量)自动初始化为 0:
int main(void)
{
static int count; /* 自动初始化为 0 */
printf("%d\n", count); /* 输出 0 */
return 0;
}
C99 混合声明
C89 要求所有变量声明必须放在代码块的开头。C99 允许在需要时才声明变量,甚至可以在 for 循环中声明:
int main(void)
{
int n = 10;
printf("n = %d\n", n);
double arr[n]; /* C99:VLA,在语句后声明 */
for (int i = 0; i < n; i++) { /* C99:for 循环内声明 i */
arr[i] = i * 0.5;
}
int sum = 0; /* C99:在代码中间声明 */
for (int i = 0; i < n; i++)
sum += (int)arr[i];
printf("sum = %d\n", sum);
return 0;
}
for (int i = 0; ...) 中的 i 作用域仅限于循环体,避免了循环变量污染外部命名空间。
作用域
变量的可见范围由声明位置决定:
块作用域:在代码块 { ... } 内声明的变量,只在该块内可见:
int main(void)
{
int a = 10; /* 块作用域:从声明到 main 结束 */
{
int b = 20; /* 块作用域:从声明到内层块结束 */
printf("%d %d\n", a, b); /* 可以访问 a 和 b */
}
/* printf("%d\n", b); */ /* 错误:b 已不可见 */
printf("%d\n", a); /* 正确 */
return 0;
}
文件作用域:在所有函数之外声明的变量,从声明位置到文件末尾可见:
#include <stdio.h>
int global_count = 0; /* 文件作用域 */
void increment(void)
{
global_count++; /* 可以访问 */
}
int main(void)
{
printf("%d\n", global_count); /* 可以访问 */
increment();
return 0;
}
同名变量的遮蔽:内层块可以声明与外层同名的变量,内层变量会遮蔽外层变量:
int main(void)
{
int x = 10;
{
int x = 20; /* 遮蔽外层的 x */
printf("%d\n", x); /* 输出 20 */
}
printf("%d\n", x); /* 输出 10 */
return 0;
}
存储类说明符
auto、register、static、extern 控制变量的存储期和链接属性:
auto int a = 10; /* auto:自动存储期(默认,极少显式写) */
register int counter = 0; /* register:建议编译器放入寄存器 */
/* 现代编译器优化已足够智能,register 基本无用 */
/* C11 起 register 仅作为提示,不能取地址 */
static int persistent = 0; /* static:静态存储期,只初始化一次 */
extern int shared; /* extern:声明外部定义的变量 */
static 局部变量在函数调用之间保持值:
int counter(void)
{
static int count = 0; /* 只初始化一次 */
return ++count;
}
int main(void)
{
printf("%d\n", counter()); /* 1 */
printf("%d\n", counter()); /* 2 */
printf("%d\n", counter()); /* 3 */
return 0;
}
常见错误
未初始化就使用:
int sum;
for (int i = 0; i < 10; i++)
sum += i; /* 错误:sum 初始值不确定 */
重复定义:
int x = 10;
int x = 20; /* 错误:同一作用域重复定义 */
声明与定义混淆:
extern int x = 10; /* 有初始化,这是定义而非声明 */
/* 某些编译器警告或报错 */
VLA 在文件作用域:
int n = 10;
int arr[n]; /* 错误:VLA 只能在块作用域 */