使用const保护对象不被改写
关键字const可以限定指定类型的对象为只读属性(即不可写),这可以防止对象在不必要的情况下被改写。比如对于一个打印名称的函数,如果没有const限定参数,那么我们就不能阻止函数的实现者去改写其内容。
void print_name(char *name)
{
if (name != NULL)
{
name[0] = 'X';
printf("%s", name);
}
}
当使用const修饰name后,代码中的赋值语句在编译阶段就会被阻止,由此保证了数据的安全性。
void print_name(const char *name)
{
}
需要强调的是,在C语言中const限定对象是只读的,而不是表示对象是常量。因此它们不能用于数组定义、case语句等需要常量表达式的地方。
void foo()
{
const int size = 3; // 只读量而非常量
int arr[size]; // 错误,size不是常量
switch (1)
{
case size: // 错误:case 表达式不是常量
break;
}
}
在需要常量的地方,枚举常量与#define在这点上可以帮助我们。
void foo()
{
#define MAXSIZE 4
enum { BUFSIZE = 5 };
int cont[MAXSIZE]; // 正确
char buf[BUFSIZE]; // 正确
switch (1)
{
case MAXSIZE: // 正确
case BUFSIZE: // 正确
break;
}
}
由于语言标准自身的不完备,我们还是可以通过指针间接的改变只读量的值。
#include <stdio.h>
int main()
{
const int size = 3; // 在C语言下size是只读量
int *psize = &size;
*psize = 5;
printf("[C] size: %d\n", size);
return 0;
}

从语言层面讲,表达式&size取得的是一个const int*,我们不能将其赋予一个int*,这里在没有使用强制类型转换的前提下由编译器通过(编译器遵循语言规范实现,大多数现代编译器会对此行代码给出警告),然后通过这个int*间接了修改了只读变量的值。同样的代码在C++下,编译器会阻止这个指针赋值操作,除非使用强制类型转换,但这也改变不了size是个常量的事实。
#include <stdio.h>
int main()
{
const int size = 3; // 在C++下size是常量
int arr[size] = {0, 1, 2}; // 正确
int *psize = (int*)&size;
*psize = 5;
printf("[C++] size: %d\n", size);
return 0;
}

关键字const还可以应用于用户定义的类型上,如struct 、enum等,此时这些类型的对象将具有只读性质,改变其成员或值是不可行的。作为一条编码建议,推荐尽量使用const,它可以帮助我们尽早发现问题,提高程序的安全性。
#include <stdio.h>
int main()
{
struct Data
{
unsigned key;
unsigned hash;
};
enum MyEnum
{
E1, E2, E3
};
const struct Data dat = { 0, 0 };
const float arr[3] = { 3, 2, 1 };
const enum MyEnum e = E3;
// 下面所有的赋值语句都是错误的
// 被赋值的对象是不可修改的(只读性质)
e = E2;
arr[0] = 0;
dat.key = 1;
return 0;
}
