4. 【C语言】数据的模样:常量和变量

前几篇我们一直在和 Hello, World! 较劲,程序“说”了什么,却从没真正“记”住什么。如果程序只能输出固定的文字,那它不过是个复读机。要让程序有记忆力、能处理不同的数据,你就得和“变量”与“常量”交朋友了。

这篇文章里,你会第一次接触 C 语言的数据世界,了解如何给数据起名字、如何在内存里存放值、以及如何定义不会改变的量。这是往后所有复杂程序的地基——地基打得牢,才盖得起高楼。


一、变量是什么?一个容易理解的比喻

变量,就是一块带标签的内存抽屉。

想象你面前有一整面墙的小抽屉,每个抽屉能放下一个数字或一个字符。当你要存储数据时,你指着一个空抽屉说:“这个抽屉叫 age,里面放 25。”以后只要说 age,就能从那个抽屉里取出 25,也可以把它换成别的数字。

这就是变量的本质:

  • 你的程序向操作系统申请一小块内存。
  • 你给它取个名字(比如 age)。
  • 你往里面放数据。
  • 之后通过名字来读、来改。

名字本身不占内存,它只是“标签”,但抽屉(内存空间)会实际占用若干个字节。至于占多少字节,取决于你打算在抽屉里放什么类型的数据。


二、标识符命名规则——给抽屉起名字的规矩

在 C 语言里,变量名、函数名等统称为标识符。起名字不能随心所欲,必须遵守以下规则:

  1. 只能由字母(A-Za-z)、数字(0-9)和下划线(_)组成。
  2. 第一个字符不能是数字。 2ndValue 不合法,value2 合法。
  3. 区分大小写。 ageAgeAGE 是三个完全不同的变量。
  4. 不能使用 C 语言的关键字。 比如 intreturnifwhile 等已经被系统占了,你不能再拿来用。
  5. 长度没有硬性上限,但只有前 31 个字符保证有意义(现代编译器通常支持更长的有效长度,但过长的名字没必要)。

此外,还有一些强烈建议遵守的“好品味”

  • 用有意义的英文单词:agestudent_countmax_speed,不要用 ax1dsf(除非是临时循环变量)。
  • 变量名通常全小写,单词间用下划线连接(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 不需要 =,也不需要 ;


七、小结

今天我们了解了变量和常量的基础。变量让程序有了“记忆”,常量让固定值有了名字且更安全。你还学会了命名标识符的规矩,以及声明、初始化的正确姿势。

可能你会觉得这些内容有点“干”,但它们是构建一切程序的地基。就像学外语要先背单词,学音乐要练音阶,枯燥的基本功,决定了你以后能走多远。

下一篇文章,我们要进入数据类型的家族:intfloatdoublechar 都是什么样的抽屉,各占多少空间,彼此怎么转换。一个个认识它们,你会逐渐看清 C 语言内存世界的轮廓。


课后小练习

  1. 声明一个 float 变量存储你的身高(米),一个 int 变量存储你的年龄,分别初始化,然后用 printf 打印出来。(提示:%f 打印 float,%d 打印 int)
  2. const 定义一年中月份的数量(12),再用 #define 定义一周的天数(7),写程序计算一年大约有多少周,输出结果。
  3. 故意写一个未初始化变量并打印它的值,看看编译器是否会给出警告。如果有警告,读一读它说了什么。
  4. (思考题)下面这段代码哪里错了?
    #include <stdio.h>
    int main(void) {
        const int max_students;
        max_students = 45;
        printf("%d\n", max_students);
        return 0;
    }
    
    试着编译看结果,然后解释为什么。

💡获取本系列示例代码请访问 GitCode 仓库

内容概要:本文围绕基于粒子群算法(PSO)的风电与水电(抽水蓄能)联合优化调度问题展开研究,旨在通过智能优化算法实现可再生能源的高效利用与电力系统的经济稳定运行。文中系统阐述了粒子群算法的核心原理及其在电力调度中的适用性,构建了综合考虑风电出力不确定性、抽水蓄能电站调节能力及系统运行约束的联合优化调度模型。采用Matlab进行算法编程与仿真求解,验证了该方法在降低系统综合运行成本、提升新能源消纳水平、增强电网调峰调频能力等方面的优越性能。研究进一步设计了多种对比场景,分析不同调度策略下的系统表现,充分展示了所提模型在应对复杂运行条件时的鲁棒性与实用价值。; 适合人群:具备一定电力系统分析基础Matlab编程能力的研究生、科研人员,以及从事新能源并网调度、电力系统规划与运行等相关领域的工程师; 使用场景及目标:①应用于风电场与抽水蓄能电站的协同优化调度决策支持;②为高比例可再生能源接入的电力系统提供经济可靠的低碳调度方案;③服务于高校及科研院所中关于智能优化算法在能源系统中应用的教学与科研实验; 阅读建议:建议读者结合提供的Matlab代码深入理解算法实现细节与模型构建逻辑,重点关注目标函数设计、约束条件处理及参数设置对优化结果的影响,并通过复现仿真结果来掌握粒子群算法解决复杂非线性调度问题的关键技术要点。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值