目录
一、前言
最近看到有篇文章谈到:“C语言中const竟是个“冒牌货”!”通过百度等大量的资源搜索,小立才发现自己才是《入门级的水平》!!!
1.const是什么?
const是一个C语言(ANSI C)的关键字,具有着举足轻重的地位。它限定一个变量不允许被改变,产生静态作用。使用const在一定程度上可以提高程序的安全性和可靠性。另外,在观看别人代码的时候,清晰理解const所起的作用,对理解对方的程序也有一定帮助。
2.const的主要作用!
(1)可以定义const常量,具有不可变性;
(2)便于进行类型检查,使编译器对处理内容有更多了解,消除了一些隐患;
(3)可以避免意义模糊的数字出现,同样可以很方便地进行参数的调整和修改。 同宏定义一样,可以做到不变则已,一变都变;
(4)可以保护被修饰的东西,防止意外的修改,增强程序的健壮性。 还是上面的例子,如果在函数体内修改了i,编译器就会报错;
(5) 可以节省空间,避免不必要的内存分配;
(6) 提高了效率。
二、现状分析
1.const的基础知识
void main()
{
const int a;
int const b; // 和前面一个意思一样,代表常整型数
const int *c;
int const *d; // 和前面一个意思一样,表示所指向的内存数据不能被修改,但是本身可以修改
int * const e; // 指针变量不能指向其他的地址,但是它所指向内存数据可以被修改
const int * const f; // 指针变量不能指向其他的地址,它所指向内存数据也不可以被修改
}
2.实验A(C语言)
void main()
{
const int a;
int const b; // 和前面一个意思一样,代表常整型数
const int *c;
int const *d; // 和前面一个意思一样,表示所指向的内存数据不能被修改,但是本身可以修改
int * const e; // 指针变量不能指向其他的地址,但是它所指向内存数据可以被修改
const int * const f; // 指针变量不能指向其他的地址,它所指向内存数据也不可以被修改
}
// 编译报错:error: assignment of read-only variable ‘a’
从上面代码看来const好像确实是限定一个变量不允许改变(只读),定义的变量 a 貌似变成了一个常量一样,那我们接下来继续:
void main()
{
// 貌似定义的 a 是一个常量
const int a = 10;
// a = 11;
int *p = (int *)&a;
*p = 11; // 通过指针间接赋值试试看
printf("a = %d \n", a);
}
// 编译成功 打印结果 a = 11
我们发现貌似定义的 a是一个常量,但是通过指针却可以间接的修改 a 的值,const不是限定变量不允许修改吗?怎么被改了?这样看来,C语言中的const好像确实是一个“冒牌货”。
3.实验B(C++语言)
那么同样的代码,我们看看在C++中的表现:
void main()
{
// 貌似定义的 a 是一个常量
const int a = 10;
// a = 11;
int *p = (int *)&a;
*p = 11; // 间接赋值
printf("a = %d \n", a);
system("pause");
}
// 打印结果 a = 10 (结果不应该是 a = 11 ?????????)
// 大家可以尝试 C 和 C++ 都进行编译对比一下。
4.这个新分配的内存到底存不存在?
这个简单,我们加一句打印就行:
void main()
{
// 貌似定义的 a 是一个常量
const int a = 10;
// a = 11;
int *p = (int *)&a;
*p = 11; // 间接赋值
printf("*p = %d \n", *p); // 加上这句打印
printf(" a = %d \n", a);
system("pause");
}
// 打印结果:*p = 11 a = 10
从打印可以看出单独分配的这个内存空间值是11,和原来的 a 是不同的两个概念,这就是在 C++ 中 const 的符号表的实现机制。
三、找出原因
其实在 c++语言里面const修饰的才算是一个真正的常量,在 c 语言中 const 可以说是个“冒牌货”。为什么呢?
其实是 c++ 编译器对 const 进行了加强,当 c++ 编译器遇到常量声明时,不会像 c 语言一样给这样const对象单独分配内存,c 语言一般是放在只读数据区,而 c ++ 编译器是把const对象放在一个符号表里面(我个人觉得放在符号表里面的其中一个原因可能是想减少一些存储操作次数),至于符号表是属于内存布局中的哪一块,我也不知道,写 c++ 编译器的人才知道。
四、结论
在 c++ 中使用 const 对象(比如打印这个对象)的时候,就会从符号表里面把对象的值拿出来使用,比如printf("a = %d \n", a); ,这时候就是把 a 的值10拿出来使用,但是当你对 a 取地址(&a)的时候,c++ 编译器会为这个a单独分配一个内存空间,如果定义一个指针指向这个内存空间(int *p = (int *)&a;),那么这个指针指向的是这个新分配的一个内存空间,然后通过这个指针间接修改这个值(*p = 11;),这时候修改的其实新分配的这个空间的值,不管你间接修改的这个值是11、20、30还是100,都和符号表原本的 a 的值没有任何关系。所以,使用 a 的时候打印出来的结果是 a = 10。
这就是符号表,是 c++ 对 c 的一些扩展,这样就会发现 c++ 编译器把 const 变成符号表这个手段确确实实把 const 修饰的变量变成了一个常量,结论就是在 c 语言里面 const 确实是一个“冒牌货”。