在 C 语言中,关键字const常被用来定义常量。故而const关键字起着至关重要的作用,它能够帮助我们更好地控制数据的可变性,增强程序的可读性。今天就来详细聊聊const在不同场景下的用法。
关键字const相较于预编译指令的优势
当一个变量被const修饰后,其值便不能再被改变。既然 C 语言中有#define,为何还要使用const呢?任何事物的存在都有其道理,const的存在自然也有它的合理性。与预编译指令相比,const修饰符具有以下优点:
-
预编译指令只是单纯地进行值的替换,无法实施类型检查。而
const
在定义常量时能够明确类型,有效避免使用过程中出现类型不匹配的状况。 -
const
能够为被修饰的内容提供保护,防止意外的修改操作,大大提升程序的稳定性。相较而言,通过预编译指令定义的常量在特定情况下可能会被意外改动,进而引发程序错误。 -
通常情况下,编译器不会为普通的
const
常量分配实际的存储空间,而是将它们存储在符号表中。这使得const
常量成为编译期间的常量,无需执行存储和读取内存的操作,极大地提高了程序的执行效率。
C 语言中 const
关键字的多样用法
一、const 修饰普通变量
当 const
修饰普通变量时,这个变量就成为了常量,一旦初始化赋值后,就不能再更改其值了。
#include <stdio.h>
int main() {
const int num = 10; // 定义一个被 const 修饰的普通变量
// num = 20; // 这行代码会导致编译错误,因为不能修改 const 修饰的变量的值
printf("The value of num is: %d\n", num);
return 0;
}
在上述代码中,我们定义了 const int num = 10
,如果尝试像注释掉的那行代码一样去修改 num
的值,编译器会报错,保证了 num
的值在程序运行过程中始终为 10
。
二、const 修饰指针
(1)const 修饰 p
当 const
修饰指针本身(也就是 p
)时,指针变量的地址不能改变,但是指针所指向的值可以改变。
#include <stdio.h>
int main() {
int a = 5, b = 10;
int* const p = &a; // 定义一个指针,指针本身是常量,不能再指向其他地址
// p = &b; // 这行代码会导致编译错误,不能修改指针指向的地址
*p = 8; // 可以修改指针所指向的值
printf("The value of a is: %d\n", a);
return 0;
}
这里 int* const p = &a
定义了指针 p
,它被 const
修饰,所以不能再让 p
指向别的变量地址,如 &b
,但可以通过 *p
来修改它所指向的变量 a
的值。
(2)const 修饰 *p
当 const
修饰指针所指向的内容(即 *p
)时,指针所指向的值不能通过该指针来改变,但指针可以指向其他地址。
#include <stdio.h>
int main() {
int a = 5, b = 10;
const int *p = &a; // 指针所指向的值是常量,不能通过此指针修改
// *p = 8; // 这行代码会导致编译错误,不能通过该指针修改所指向的值
p = &b; // 可以改变指针的指向
printf("The value of a is still: %d\n", a);
return 0;
}
在这个例子中,const int *p = &a
使得通过 p
去修改 a
的值不被允许,不过可以让 p
重新指向变量 b
的地址。
(3)const 修饰 p
和 *p
这种情况下,指针本身和它所指向的值都成为了常量,既不能改变指针的指向,也不能通过该指针修改所指向的值。
#include <stdio.h>
int main() {
int a = 5, b = 10;
int const * const p = &a; // 指针本身和所指向的值都是常量
// p = &b; // 会导致编译错误,不能改变指针指向
// *p = 8; // 也会导致编译错误,不能修改所指向的值
return 0;
}
int const * const p = &a
这样的定义严格限制了 p
的使用,保证了更高的数据安全性和不可变性。
三、const 修饰数组
用 const
修饰数组,可以确保数组中的元素不被修改。
#include <stdio.h>
int main() {
const int arr[5] = {1, 2, 3, 4, 5};
// arr[0] = 10; // 这行代码会导致编译错误,不能修改 const 修饰的数组元素的值
for (int i = 0; i < 5; i++) {
printf("arr[%d] = %d\n", i, arr[i]);
}
return 0;
}
在代码里定义了 const int arr[5]
,之后若尝试去修改数组元素,比如 arr[0] = 10
,编译器会阻止这样的操作,保证数组元素的只读性。
四、const 修饰函数形参
(1)const 修饰普通形参变量
在函数参数是普通变量时,用 const
修饰可以避免在函数内部不小心修改了传入的参数值。
#include <stdio.h>
void printValue(const int arg) {
// arg = 20; // 会导致编译错误,不能修改 const 修饰的形参的值
printf("The value of arg is: %d\n", arg);
}
int main() {
int num = 15;
printValue(num);
return 0;
}
在 printValue
函数中,形参 arg
被 const
修饰,所以在函数内部不能对 arg
进行重新赋值操作,保障了传入值的原始性。
(2)const 修饰指针形参
如果函数参数是指针,用 const
修饰指针形参意味着在函数内部不能通过该指针修改它所指向的值。
#include <stdio.h>
void modifyValue(const int *p) {
// *p = 30; // 会导致编译错误,不能通过该指针修改所指向的值
printf("The value pointed by p is: %d\n", *p);
}
int main() {
int num = 25;
modifyValue(&num);
return 0;
}
在 modifyValue
函数里,const int *p
限定了不能通过 p
去改变它所指向的变量 num
的值,防止函数内部对传入指针指向的数据进行误修改。
(3)const 修饰引用形参
对于引用形参,用 const
修饰同样能保证在函数内部不会修改引用所关联的变量。
#include <stdio.h>
void printAndNotModify(const int &arg) {
// arg = 40; // 会导致编译错误,不能修改 const 修饰的引用形参的值
printf("The value of arg is: %d\n", arg);
}
int main() {
int num = 35;
printAndNotModify(num);
return 0;
}
printAndNotModify
函数中,const int &arg
确保了在函数内部不能改变与 arg
关联的变量 num
的值。
五、const 修饰函数返回值
(1)const 修饰普通类型的返回值
当函数返回普通类型值且被 const
修饰时,表示返回的值不能被修改。不过在实际中这种情况相对较少用,因为返回的普通值本身在接收后一般也不会去修改它的常量性。
#include <stdio.h>
const int getValue() {
return 50;
}
int main() {
const int result = getValue();
// result = 60; // 会导致编译错误,不能修改 const 修饰的返回值
printf("The result is: %d\n", result);
return 0;
}
const int getValue()
函数返回一个常量值,在 main
函数中接收到这个返回值后,不能再对其进行修改操作。
(2)const 修饰指针类型的返回值
若函数返回指针类型且被 const
修饰,意味着返回的指针所指向的值不能通过该指针修改。
#include <stdio.h>
const int* getPointer() {
static const int num = 70;
return #
}
int main() {
const int *p = getPointer();
// *p = 80; // 会导致编译错误,不能通过该指针修改所指向的值
printf("The value pointed by p is: %d\n", *p);
return 0;
}
在 getPointer
函数返回一个指向常量的指针,在 main
函数中拿到这个指针后,不能通过它去改变所指向的值,确保了数据的安全性。
希望通过以上这些详细的代码示例,大家对 C 语言中 const
关键字的各种用法都能有清晰的认识和深入的理解。