前几篇我们一直在和 Hello, World! 较劲,程序“说”了什么,却从没真正“记”住什么。如果程序只能输出固定的文字,那它不过是个复读机。要让程序有记忆力、能处理不同的数据,你就得和“变量”与“常量”交朋友了。
这篇文章里,你会第一次接触 C 语言的数据世界,了解如何给数据起名字、如何在内存里存放值、以及如何定义不会改变的量。这是往后所有复杂程序的地基——地基打得牢,才盖得起高楼。
一、变量是什么?一个容易理解的比喻
变量,就是一块带标签的内存抽屉。
想象你面前有一整面墙的小抽屉,每个抽屉能放下一个数字或一个字符。当你要存储数据时,你指着一个空抽屉说:“这个抽屉叫 age,里面放 25。”以后只要说 age,就能从那个抽屉里取出 25,也可以把它换成别的数字。
这就是变量的本质:
- 你的程序向操作系统申请一小块内存。
- 你给它取个名字(比如
age)。 - 你往里面放数据。
- 之后通过名字来读、来改。
名字本身不占内存,它只是“标签”,但抽屉(内存空间)会实际占用若干个字节。至于占多少字节,取决于你打算在抽屉里放什么类型的数据。
二、标识符命名规则——给抽屉起名字的规矩
在 C 语言里,变量名、函数名等统称为标识符。起名字不能随心所欲,必须遵守以下规则:
- 只能由字母(
A-Z、a-z)、数字(0-9)和下划线(_)组成。 - 第一个字符不能是数字。
2ndValue不合法,value2合法。 - 区分大小写。
age、Age、AGE是三个完全不同的变量。 - 不能使用 C 语言的关键字。 比如
int、return、if、while等已经被系统占了,你不能再拿来用。 - 长度没有硬性上限,但只有前 31 个字符保证有意义(现代编译器通常支持更长的有效长度,但过长的名字没必要)。
此外,还有一些强烈建议遵守的“好品味”:
- 用有意义的英文单词:
age、student_count、max_speed,不要用a、x1、dsf(除非是临时循环变量)。 - 变量名通常全小写,单词间用下划线连接(snake_case),或者用驼峰命名法(camelCase,如
studentCount)。在一个项目里保持一致即可,C 社区里 snake_case 更为常见。
三、声明与初始化——要抽屉、放东西
要使用变量,必须先声明后使用。声明的作用是告诉编译器:“我需要一个抽屉,请给我准备出足够的空间。”
声明的基本格式是:
类型 变量名;
比如:
int age; // 声明一个整数变量 age
float score; // 声明一个单精度浮点数变量 score
char grade; // 声明一个字符变量 grade
这时候,抽屉是准备好了,但里面放的是不确定的垃圾值(之前留在那儿的随机数据)。直接读取未初始化的变量是危险且无意义的。
所以,最好在声明的同时给它一个初始值,这叫做初始化:
int age = 25;
float score = 98.5;
char grade = 'A';
也可以一次声明多个同类型变量:
int x, y, z; // 三个未初始化的 int
int a = 1, b = 2, c = 3; // 三个初始化了的 int
初始化形式还有一些灵活的地方,比如可以用常量表达式、甚至之前声明过的变量:
int a = 10;
int b = a + 5; // b 初始化为 15
int c = b * 2; // c 初始化为 30
注意:C 语言中,全局变量(在所有函数外面定义的)和静态局部变量如果不手动初始化,会被自动初始化为 0;而普通的局部变量(在函数内部定义的)则不会自动初始化,内容是垃圾值。初学阶段我们主要和局部变量打交道,所以请务必养成声明即初始化的习惯。
四、常量的两种面孔:const 与 #define
有时候,程序里需要用到一些固定不变的值,比如圆周率、一天的小时数、最大用户数量。这些值在程序运行过程中不应该被修改。我们就可以用常量来表示它们。C 语言给了你两种主流的定义方式。
方式一:const 关键字
const 是“constant”的缩写,加在类型声明前面,意思是“这个抽屉里的东西只读,不能改”。
const int HOURS_PER_DAY = 24;
const float PI = 3.14159;
一旦用 const 声明并初始化后,试图修改它就会导致编译错误:
const int MAX = 100;
MAX = 200; // 编译报错:assignment of read-only variable 'MAX'
用 const 定义常量的好处是它有明确的类型,编译器能做类型检查,用起来和普通变量一样安全,只是不能赋值。常量名通常全大写字母加下划线,这是一种约定,看到全大写就知道它是常量。
方式二:#define 预处理宏
我们之前学过 #include,#define 也是预处理指令。它的用法是:
#define PI 3.14159
#define HOURS_PER_DAY 24
这行代码做了一件文本替换的事:在预处理阶段,编译器会把代码里所有出现 PI 的地方,原样替换成 3.14159。它不是变量,不占运行时内存,没有类型。
例如:
#include <stdio.h>
#define GREETING "你好,世界"
int main(void) {
printf("%s\n", GREETING);
return 0;
}
预处理后,printf 那行实际上变成了:
printf("%s\n", "你好,世界");
你可以用 gcc -E 查看替换后的结果,会看到 GREETING 消失了,只剩字符串。
#define 宏的优点:它不占用内存,执行时不需取址。缺点也很明显:没有类型检查,出现错误时编译器给出的信息可能很隐晦。比如:
#define PI 3.14159
// ...
int x = PI * 2; // PI 是 double,这里会有隐式类型转换,编译器可能不会警告
如果 #define 用来定义复杂表达式,不加括号还可能导致诡异的错误(后面讲带参宏时会深入)。对于简单的数值常量或字符串常量,两种方式都可以,但初学阶段建议优先用 const,它更安全,更像“真正的”变量。
五、代码示例:常量与变量同台登场
我们写一个小程序,计算半径为 5 的圆的周长和面积,把所有学到的都用上:
#include <stdio.h>
#define PI 3.14159 // 宏定义常量
int main(void) {
const int radius = 5; // const 常量:半径
float circumference; // 未初始化变量,存放周长
float area; // 存放面积
circumference = 2 * PI * radius; // 计算周长
area = PI * radius * radius; // 计算面积
printf("半径: %d\n", radius);
printf("周长: %.2f\n", circumference);
printf("面积: %.2f\n", area);
return 0;
}
%.2f 表示输出的浮点数保留两位小数,后面学格式化输入输出会详细解释。
运行输出:
半径: 5
周长: 31.42
面积: 78.54
试试把 const int radius 改成 int radius,再在下面加一行 radius = 10;,看看周长和面积的变化。你开始体会到:变量的威力在于可变,常量的价值在于安全。
六、常见错误与陷阱
1. 未声明就使用变量
int main(void) {
a = 10; // 错误!a 从未声明
return 0;
}
编译报错:'a' undeclared。必须先声明后使用。
2. 声明了但未初始化就读取
int age;
printf("%d\n", age); // 未定义行为,打印垃圾值
始终给变量一个初始值。
3. 试图修改 const 常量
const int MAX = 100;
MAX = 200; // 编译错误
这很直观,错就错在去修改。
4. 宏定义里加等号或分号
#define PI = 3.14159; // 错误!
宏替换后,PI 会被替换成 = 3.14159;,代码立刻就乱套了。#define 不需要 =,也不需要 ;。
七、小结
今天我们了解了变量和常量的基础。变量让程序有了“记忆”,常量让固定值有了名字且更安全。你还学会了命名标识符的规矩,以及声明、初始化的正确姿势。
可能你会觉得这些内容有点“干”,但它们是构建一切程序的地基。就像学外语要先背单词,学音乐要练音阶,枯燥的基本功,决定了你以后能走多远。
下一篇文章,我们要进入数据类型的家族:int、float、double、char 都是什么样的抽屉,各占多少空间,彼此怎么转换。一个个认识它们,你会逐渐看清 C 语言内存世界的轮廓。
课后小练习
- 声明一个
float变量存储你的身高(米),一个int变量存储你的年龄,分别初始化,然后用printf打印出来。(提示:%f打印 float,%d打印 int) - 用
const定义一年中月份的数量(12),再用#define定义一周的天数(7),写程序计算一年大约有多少周,输出结果。 - 故意写一个未初始化变量并打印它的值,看看编译器是否会给出警告。如果有警告,读一读它说了什么。
- (思考题)下面这段代码哪里错了?
试着编译看结果,然后解释为什么。#include <stdio.h> int main(void) { const int max_students; max_students = 45; printf("%d\n", max_students); return 0; }
2365

被折叠的 条评论
为什么被折叠?



