C&C++中的const关键字

搬运自我的CSDN https://blog.csdn.net/u013213111/article/details/105896156

const关键字在C和C++中并非相同的含义。从字面含义上,const很容易让人认为它所限定的量是一个常量Constant,这样的理解在C++中是正确的,而在C语言中则是错误的。
在C语言中,const的作用,更准确地说是将变量限定为read-only。

Let's look at what is meant when const is used. It's really quite simple: const means that something is not modifiable, so a data object that is declared with const as a part of its type specification must not be assigned to in any way during the run of a program. It is very likely that the definition of the object will contain an initializer (otherwise, since you can't assign to it, how would it ever get a value?), but this is not always the case. For example, if you were accessing a hardware port at a fixed memory address and promised only to read from it, then it would be declared to be const but not initialized.
——https://publications.gbdirect.co.uk//c_book/chapter8/const_and_volatile.html

仔细想想,read-only其实并不意味着not change。
结合volatile来看就清晰了,用C Primer Plus中的栗子来说:

可以同时用const和volatile限定一个值。例如,通常用const把硬件时钟设置为程序不能更改的变量,但是可以通过代理改变,这时用 volatile。

至于在C++中,const的作用就就类似于C语言的#define了,由编译器进行值替代,这是Thinking in C++ 第8章中的解释:

当定义一个const时,必须赋一个值给它,除非用extern作出了清楚的说明。
通常C++编译器并不为const创建存储空间,相反它把这个定义保存在它的符号表里。但是,上面的extern强制进行了存储空间分配(另外还有一些情况,如取一个const的地址,也要进行存储空间分配),由于extern意味着使用外部连接,因此必须分配存储空间,这也就是说有几个不同的编译单元应当能够引用它,所以它必须有存储空间。
通常情况下,当extern不是定义的一部分时,不会分配存储空间。如果使用const,那么编译时会进行常量折叠。

——但是对于“不为const创建存储空间”的说法,目前还不太理解。。。

来看看下面这段代码:

int main(void)
{
	int iVal;
	const int constVal = 5;

	int *ptr = (int *)&constVal; //强制类型转换
	*ptr = 3;

	iVal = constVal;

	return 0;
}

当作为C语言时得到的汇编代码如下,给iVal赋值时实际上是先从constVal所在的内存中取值,再将取得的值赋给iVal,由于在此之前通过ptrconstVal内存进行了修改,因此iVal值为3,而非constVal的初始值5。

main.c:
2       {
   0x0000000100401080 <+0>:     push   %rbp
   0x0000000100401081 <+1>:     mov    %rsp,%rbp
   0x0000000100401084 <+4>:     sub    $0x30,%rsp
   0x0000000100401088 <+8>:     callq  0x1004010d0 <__main>

3               int iVal;
4               const int constVal = 5;
   0x000000010040108d <+13>:    movl   $0x5,-0x10(%rbp)

5
6               int *ptr = (int *)&constVal;
   0x0000000100401094 <+20>:    lea    -0x10(%rbp),%rax
   0x0000000100401098 <+24>:    mov    %rax,-0x8(%rbp)

7               *ptr = 3;
   0x000000010040109c <+28>:    mov    -0x8(%rbp),%rax
   0x00000001004010a0 <+32>:    movl   $0x3,(%rax)

8
9               iVal = constVal; //iVal = 3;
   0x00000001004010a6 <+38>:    mov    -0x10(%rbp),%eax
   0x00000001004010a9 <+41>:    mov    %eax,-0xc(%rbp)

10
11              return 0;
=> 0x00000001004010ac <+44>:    mov    $0x0,%eax

12      }
   0x00000001004010b1 <+49>:    add    $0x30,%rsp
   0x00000001004010b5 <+53>:    pop    %rbp
   0x00000001004010b6 <+54>:    retq

然而作为C++时则得到不同的结果,在给iVal赋值时是直接将立即数0x5赋给iVal,也就是constVal的初始值;虽然此时constVal已经被修改为3了,但不会影响到iVal

main.cpp:
2       {
   0x0000000100401080 <+0>:     push   %rbp
   0x0000000100401081 <+1>:     mov    %rsp,%rbp
   0x0000000100401084 <+4>:     sub    $0x30,%rsp
   0x0000000100401088 <+8>:     callq  0x1004010d0 <__main>

3               int iVal;
4               const int constVal = 5;
   0x000000010040108d <+13>:    movl   $0x5,-0x10(%rbp)

5
6               int *ptr = (int *)&constVal;
   0x0000000100401094 <+20>:    lea    -0x10(%rbp),%rax
   0x0000000100401098 <+24>:    mov    %rax,-0x8(%rbp)

7               *ptr = 3;
   0x000000010040109c <+28>:    mov    -0x8(%rbp),%rax
   0x00000001004010a0 <+32>:    movl   $0x3,(%rax)

8
9               iVal = constVal; //iVal = 5, constVal = 3;
   0x00000001004010a6 <+38>:    movl   $0x5,-0xc(%rbp)

10
11              return 0;
=> 0x00000001004010ad <+45>:    mov    $0x0,%eax

12      }
   0x00000001004010b2 <+50>:    add    $0x30,%rsp
   0x00000001004010b6 <+54>:    pop    %rbp
   0x00000001004010b7 <+55>:    retq

如果把constVal定义为全局变量,那么无论是在C还是C++中,实际运行的时候都会出现 Segmentation fault。以C为例,使用nm命令看一下符号表,发现——

0000000100403000 R constVal

其中R说明该符号位于只读数据区,尝试修改只读数据的值显然会导致错误了。

关于C++中const的用法,还可以参考Const, constexpr, and symbolic constants,其中对runtime constants(initialization values can only be resolved at runtime ,when your program is running)和compile-time constants(initialization values can be resolved at compile-time,when your program is compiling)也进行了解释,上述栗子中的constVal是一个compile-time constants,因此C++编译器才能对其进行值替换。

猜你喜欢

转载自www.cnblogs.com/lyrich/p/12977113.html