目录
1 什么是常量
2 常量的分类
3 常量的定义
3.1 使用 #define 定义常量
3.1.1 介绍
3.1.2 定义格式
3.1.3 案例演示
3.1.4 执行时机
3.2 const 关键字
3.2.1 介绍
3.2.2 const 定义常量的格式
3.2.3 案例演示
3.2.4 执行时机
3.2.5 注意事项
4 #define 和 const 定义常量的区别
4.1 执行时机
4.2 类型检查
4.3 作用域和存储类别
4.4 调试和可读性
5 测试题
1 什么是常量
程序运行时,其值不能改变的量,即为常量。
2 常量的分类
字面量常量,直接使用的常量,不需要定义或声明,包括整数常量、浮点数常量、字符常量。
标识符常量,使用标识符作为常量名,包括 #define 定义的标识符常量和 const 关键字定义的标识符常量以及枚举常量。
3 常量的定义
字面常量不需要定义或声明,我们主要学习如何定义标识符常量,习惯上常量名使用大写,方便与变量区分。
3.1 使用 #define 定义常量
3.1.1 介绍
#define 来定义常量,也叫作宏定义,就是用一个标识符来表示一个常量值,如果在后面的代码中出现了该标识符,那么在预处理阶段就会全部替换成指定的常量值,即用宏体替换所有宏名,简称宏替换。
对于 #define 指令,预处理器会查找源代码中所有匹配宏定义名称的实例,并将它们替换为宏定义中指定的内容。这个过程是纯粹的文本替换,不进行语法检查或类型检查。因此,如果在宏替换后产生了语法错误,这些错误只有在预处理后的代码被编译器处理时才会被发现。
3.1.2 定义格式
#define 常量名 常量值
- 不要以分号结尾,如有分号,分号会成为常量值的一部分。
- #define 必须写在 main 函数的外面(有些编译器的扩展允许 #define 写在 main 函数里,但我们强烈不建议这么做)。
3.1.3 案例演示
#include <stdio.h>
// 使用宏定义定义常量 PI,常量值 3.14
#define PI 3.14
int main()
{
double area;
// 圆的半径
double r = 1.2;
// 计算圆的面积
area = PI * r * r;
// 输出面积
printf("圆的面积是(保留到小数点后两位) : %.2f\n", area);
// 将圆的半径扩大二倍
r = 2.4;
// 计算圆的面积
area = PI * r * r;
// 输出面积会扩大四倍
printf("圆的面积是(保留到小数点后两位) : %.2f\n", area);
return 0;
}
输出结果:
3.1.4 执行时机
在终端中输入预处理指令:gcc -E .\chapter04\define.c -o .\chapter04\define.i ,然后查看生成的 define.i 文件内容,会发现代码中的 PI 已经全部替换成了对应的数值,如下所示:
3.2 const 关键字
3.2.1 介绍
C99 标准新增,这种方式跟定义一个变量是一样的,只需要在变量的数据类型前再加上一个 const 关键字。
跟使用 #define 定义宏常量相比,const 定义的常量有详细的数据类型,而且会在编译阶段进行安全检查,在运行时才完成替换,所以会更加安全和方便。推荐使用!
3.2.2 const 定义常量的格式
const 数据类型 常量名 = 常量值;
3.2.3 案例演示
#include <stdio.h>
// 使用 const 关键字定义常量 PI
const double PI = 3.14;
int main()
{
double area; // 声明变量 area 用于存储圆的面积
double r = 1.2; // 声明并初始化变量 r 为圆的半径
// 计算圆的面积并赋值给变量 area
area = PI * r * r;
// 使用 printf 函数输出圆的面积,保留两位小数
printf("圆的面积是(保留到小数点后两位) : %.2f\n", area);
return 0; // 程序正常结束
}
输出结果:
3.2.4 执行时机
在终端中输入预处理指令:gcc -E .\chapter04\const.c -o .\chapter04\const.i ,然后查看生成的 const.i 文件内容,会发现代码中的 PI 并没有替换成对应的数值,还是常量的形式,如下所示:
3.2.5 注意事项
在 C 语言中,使用 const 关键字定义的常量需要在定义时进行初始化,不可以后续再赋值。const 关键字用于声明一个变量的值在初始化之后不能被修改,因此它必须在声明时就被赋予一个初始值。这个规则确保了常量在程序中的值在整个运行期间都是固定不变的。
4 #define 和 const 定义常量的区别
4.1 执行时机
#define:这是一个预处理指令,它在编译之前的预处理阶段执行。预处理器会扫描源代码,将所有 #define 指令中定义的宏(包括常量)替换为它们对应的文本。这个过程是文本替换,不涉及任何编译或类型检查。
const:这是一个关键字,用于在编译时定义常量。与 #define 不同,const 常量是在编译过程中处理的,并且会进行类型检查。这意味着如果尝试将 const 常量赋值给不兼容类型的变量,编译器将报错。
4.2 类型检查
#define:由于 #define 只是简单的文本替换,它不涉及类型检查。因此,如果宏定义被错误地用于不兼容的上下文(例如,将整数宏用于浮点数运算),编译器可能不会在宏替换后立即报错,而是在后续的代码检查中才发现问题。
const:const 常量需要指定数据类型,并且会在编译时进行类型检查。这有助于防止类型不匹配的错误,并提高了代码的类型安全性。
4.3 作用域和存储类别
#define:#define 定义的宏没有作用域的概念,它们在整个编译单元(通常是源文件)中都是可见的。此外,#define 宏也没有存储类别(如自动、静态或外部),因为它们在预处理阶段就被替换了。
const:const 常量可以具有作用域和存储类别。它们可以定义在函数内部(具有自动存储期),也可以定义在全局作用域中(具有静态或外部存储期,取决于是否使用了 static 关键字)。这允许更精细的控制常量的可见性和生命周期。
4.4 调试和可读性
#define:由于 #define 宏只是文本替换,它们在调试时可能会隐藏一些重要的信息。例如,如果宏被错误地替换,调试器可能无法显示原始宏名称,而是显示替换后的文本。
const:const 常量在调试时更容易识别和理解,因为它们保留了类型信息和作用域信息。此外,const 常量还可以被调试器直接显示其名称和值。
5 测试题
请写出以下代码的运行结果:
#include <stdio.h>
const int PI = 300;
int main()
{
PI = 250;
printf("%d", PI);
}
【答案】报错
【解析】常量的值不能修改!