| 上一篇 | 下一篇 |
|---|---|
| C 语言实现矩阵乘法 |
目 录
- [const 关键字的作用(和 define 比呢?)](#const 关键字的作用(和 define 比呢?))
-
- [1)const 修饰变量](#1)const 修饰变量)
-
- [① 局部变量(函数内部)](#① 局部变量(函数内部))
- [② 全局变量(文件作用域)](#② 全局变量(文件作用域))
- [2)const 与指针的组合(重点难点)](#2)const 与指针的组合(重点难点))
- [3)const 修饰函数](#3)const 修饰函数)
-
- [① 修饰函数参数](#① 修饰函数参数)
- [② 修饰函数返回值](#② 修饰函数返回值)
- [4)const 修饰数组和结构体](#4)const 修饰数组和结构体)
-
- [① 常量数组(查找表、配置表)](#① 常量数组(查找表、配置表))
- [② 结构体成员为 const(较少用,但合法)](#② 结构体成员为 const(较少用,但合法))
- [5)const 与 #define 的区别](#define 的区别)
- 6)总结
const 关键字的作用(和 define 比呢?)
constant:[adj] 固定的、不变的,[n] 常数、常量
被 const 定义后本质上还是变量,只是无法修改。在必须应用常量的情况下是无法使用的,所以叫做 常变量。
总结在最后面!
在 C 语言中,const 是一个 类型限定符(type qualifier) ,用于声明某个对象为 只读(read-only) 。
这意味着:该对象的值在初始化后不能被程序直接修改。如果尝试修改,编译器会报错(除非通过强制类型转换绕过,但这是未定义行为,应避免)。
1)const 修饰变量
① 局部变量(函数内部)
c
void func(void) {
const int MAX_RETRY = 5;
// MAX_RETRY = 10; // 编译错误:不能修改 const 变量
}
用 const 修饰的局部变量依旧存储在栈上,但内容不可写,具有明确类型(比如int)。
② 全局变量(文件作用域)
c
// file.c
const double PI = 3.1415926;
默认仅在本 .c 文件可用(相当于隐含 static),若需在多个文件共享,需配合 extern 声明。在 .c 文件中定义,在对应的 .h 文件中依旧要使用 extern :
c
// constants.h
extern const double PI;
// constants.c
const double PI = 3.1415926;
// main.c
#include "constants.h"
double r = PI * 2;
2)const 与指针的组合(重点难点)
const 与指针结合时,位置决定含义。记住口诀:
const在*左边,内容不能改,但指针可以改;const在*右边,指针不能改,但内容可以改;*两边都有const,内容和指针都不能改。
| 声明 | 含义 | 能否修改指针? | 能否修改指向的内容? |
|---|---|---|---|
const int *p 或 int const *p |
指针常量(指向常量的指针) | ✔️ 可以(p = &x;) |
❌ 不可以(*p = 10; 错误) |
int *const p = &x; |
常量指针 | ❌ 不可以 | ✔️ 可以(*p = 10;) |
const int *const p = &x; |
指向常量的常量指针 | ❌ 不可以 | ❌ 不可以 |
应用示例:
c
const char *str = "Hello"; // 推荐写法:字符串字面量是只读的
// str[0] = 'h'; // × 运行时可能崩溃(写只读内存)
str = "World"; // √ 指针可变
char buffer[10];
char *const ptr = buffer; // 指针固定指向 buffer
ptr[0] = 'A'; // √ 内容可变
// ptr = another_buffer; // × 指针不可变
3)const 修饰函数
这是 const 最重要的应用场景之一:表达"只读"契约 。
① 修饰函数参数
-
指针参数加 const:
指针参数就是形如
void func(const int* x)cvoid print_array(const int arr[], size_t n) { for (size_t i = 0; i < n; ++i) printf("%d ", arr[i]); // √ 只读访问 // arr[0] = 999; // × 编译错误 }等价于
const int *arr,表明函数不会修改传入的内容,调用者可安全传入常量数组或普通数组。 -
字符串处理函数的标准做法:
csize_t my_strlen(const char *s); // 不修改 s int my_strcmp(const char *s1, const char *s2); // 不修改 s1, s2所有标准库如
strlen,strcpy,printf等对只读参数都使用const。 -
值参数(非指针参数),加 const 对实参没啥影响,但形参在函数内部不会被修改:
值参数就是形如
void func(const int x),入口参数不是指针类型的。-
对于传入的变量(实参):C 函数参数默认是 "按值传递" ,当你调用一个函数时,实参的值会被 复制 到形参中,这个传入的变量(实参)本来就不会被修改,加不加
const都一样。 -
对于形参来说:形参是函数内部的一个对实参的局部变量副本 ,给值参数加
const的效果,等于在函数内部给局部变量加const,表示在函数体内,不能修改这个值。
-
② 修饰函数返回值
-
修饰返回的指针 ------ 有意义:
cconst char* get_version(void) { return "v1.2.3"; // 字符串字面量是只读的 }返回
const char*表示:调用者不得修改返回的字符串(其他类型类似)。但不要返回局部变量的 const 指针:
cconst char* bad_func(void) { char buf[10] = "temp"; return buf; // ❌ 返回栈地址,函数返回后无效 } -
修饰返回的基本类型 ------ 无意义:
cconst int getValue(void) { return 42; } int x = getValue();函数返回的是临时值(右值,表示"临时值、计算结果",如字面量
42,字符串字面量天生就是 const ),无法被赋值,const 多余。C 标准允许,但毫无作用,现代编译器会忽略。
4)const 修饰数组和结构体
① 常量数组(查找表、配置表)
c
const uint8_t CRC_TABLE[256] = {0x00, 0x07, 0x0E};
- 编译器通常将其放入
.rodata段(只读数据段),节省 RAM。适用于嵌入式系统中资源受限场景。
② 结构体成员为 const(较少用,但合法)
c
struct DeviceConfig {
const int baud_rate;
const char *const name;
};
// 初始化必须在定义时完成(C99 支持复合字面量)
struct DeviceConfig dev = { .baud_rate = 115200, .name = "UART0" };
// dev.baud_rate = 9600; // × 错误
5)const 与 #define 的区别
const 是有类型、有作用域、可取地址的运行时常量(本质是常变量,在 C 中不是编译时常量),而 #define 是无类型、无作用域、纯文本替换的预处理宏,在必须使用编译时常量的地方,const 不可用。
具体如下:
| 特性 | const 变量 |
#define 宏 |
|---|---|---|
| 类型 | 有明确类型(如 const int) |
无类型,纯文本替换 |
| 作用域 | 遵循 C 作用域规则(块、文件) | 全局有效(从定义到 #undef) |
| 存储 | 占用内存(除非被优化掉) | 不占用内存,预处理阶段替换 |
| 取地址 | 可以(&PI 合法) |
不可以(宏不是对象) |
| 调试 | 调试器可查看符号和值 | 调试器看不到宏(已被替换) |
| 安全性 | 编译器检查类型和修改 | 无任何检查,易出错 |
| 初始化 | 运行时或编译时初始化 | 必须是常量表达式 |
| 数组大小 | C 标准中 不能 用于定义数组大小(除非是 VLA 或 C23) | 可以: #define N 10 int arr[N]; |
示例对比:
-
使用
#define(传统方式):c#define MAX_SIZE 100 #define PI 3.14159 int buffer[MAX_SIZE]; // √ 合法(常量表达式) -
使用
const(更安全,但有限制):cconst int MAX_SIZE = 100; // int buffer[MAX_SIZE]; // × C89/C99 中错误!const 不是"编译时常量"
关键点 ⚠️ :在 C 语言中,const 变量 不是编译时常量,因此不能用于:
数组长度(除非是变长数组 VLA,C99+)、case 标签、位字段宽度等需要常量表达式的地方。
解决方案✔️ :
-
对于需要编译时常量 的场景(如数组大小),仍需用
#define或enum。 -
对于运行时常量或需要类型安全的常量 ,优先用
const。
推荐实践:
c
// 数组大小、开关选项 → 用 #define 或 enum
#define BUFFER_SIZE 256
enum { MAX_USERS = 10 };
// 数学常量、配置值 → 用 const
const double EARTH_RADIUS_KM = 6371.0;
const char VERSION[] = "1.0.0";
6)总结
在 C 语言中,const 的核心作用是声明只读对象,通过类型系统告诉编译器和程序员:该数据在初始化后不应被修改。
-
它主要用于:
-
修饰指针参数(防止函数意外修改传入的数据);
-
定义具有类型安全的常量变量(替代部分宏);
-
以及返回只读指针(保护内部数据不被外部篡改)。
-
-
const是有类型、有作用域、可取地址的运行时常量(本质是常变量,在 C 中不是编译时常量),而#define是无类型、无作用域、纯文本替换的预处理宏,在必须使用编译时常量的地方,const不可用。 -
强制类型转换可绕过
const(但属未定义行为,可能崩溃或静默失败)。