const在C与C++中的区别

下面的代码在C和C++下编译,输出会是什么?

1  void main(void)
2  {
3 	 const int a = 0x00;
4	 int *pi;
5
6	 pi = (int *)&a;
7	 *pi = 0xFF;
8
9	 printf("*pi=%d,a=%d\n", *pi, a);
10   return 0;
11 }

注意,上面的代码第6行必须写为pi = (int *)&a;,才能编译通过,否则在gcc下会有警告,g++有编译错误(gcc版本6.5,低版本gcc不会):

$ gcc cnst.c -g -o cnst
cnst.c: In function ‘main’:
cnst.c:6:12: warning: initialization discards ‘const’ qualifier from pointer target type [-Wdiscarded-qualifiers]
  int *pi = &a;

$g++ cnst.cpp -g -o cnst
cnst.cpp: In function ‘int main()’:
cnst.cpp:6:12: error: invalid conversion from ‘const int*’ to ‘int*’ [-fpermissive]
  int *pi = &a;

在Linux下分别保存为cnst.c和cnst.cpp,用GCC和G++编译,结果如下:

$ gcc cnst.c -o cnst
$ ./cnst 
*pi=255,a=255
$ g++ cnst.cpp -o cnst
$ ./cnst 
*pi=255,a=0

从执行结果来看,在C语言中,通过pi指针可以将int a的值修改为255,但是在C++中,似乎并不能通过pi指针修改const int a的值,why?

很奇怪?那就来看看汇编吧:

//C语言反汇编
#include <stdio.h>

int main()
{
  400526:	55                   	push   %rbp
  400527:	48 89 e5             	mov    %rsp,%rbp
  40052a:	48 83 ec 10          	sub    $0x10,%rsp
	const int a = 0x00;
  40052e:	c7 45 f4 00 00 00 00 	movl   $0x0,-0xc(%rbp)
	int *pi = (int *)&a;
  400535:	48 8d 45 f4          	lea    -0xc(%rbp),%rax
  400539:	48 89 45 f8          	mov    %rax,-0x8(%rbp)

	*pi = 0xFF;
  40053d:	48 8b 45 f8          	mov    -0x8(%rbp),%rax
  400541:	c7 00 ff 00 00 00    	movl   $0xff,(%rax)
	printf("*pi=%d,a=%d\n",*pi,a);
  400547:	8b 55 f4             	mov    -0xc(%rbp),%edx
  40054a:	48 8b 45 f8          	mov    -0x8(%rbp),%rax
//注意下面这行
  40054e:	8b 00                	mov    (%rax),%eax
  400550:	89 c6                	mov    %eax,%esi
  400552:	bf f4 05 40 00       	mov    $0x4005f4,%edi
  400557:	b8 00 00 00 00       	mov    $0x0,%eax
  40055c:	e8 9f fe ff ff       	callq  400400 <printf@plt>

	return 0;
  400561:	b8 00 00 00 00       	mov    $0x0,%eax
}

//C++反汇编
#include <stdio.h>

int main()
{
  400546:	55                   	push   %rbp
  400547:	48 89 e5             	mov    %rsp,%rbp
  40054a:	48 83 ec 10          	sub    $0x10,%rsp
	const int a = 0x00;
  40054e:	c7 45 f4 00 00 00 00 	movl   $0x0,-0xc(%rbp)
	int *pi = (int *)&a;
  400555:	48 8d 45 f4          	lea    -0xc(%rbp),%rax
  400559:	48 89 45 f8          	mov    %rax,-0x8(%rbp)

	*pi = 0xFF;
  40055d:	48 8b 45 f8          	mov    -0x8(%rbp),%rax
  400561:	c7 00 ff 00 00 00    	movl   $0xff,(%rax)
	printf("*pi=%d,a=%d\n",*pi,a);
  400567:	48 8b 45 f8          	mov    -0x8(%rbp),%rax
  40056b:	8b 00                	mov    (%rax),%eax
//注意下面这行
  40056d:	ba 00 00 00 00       	mov    $0x0,%edx
  400572:	89 c6                	mov    %eax,%esi
  400574:	bf 14 06 40 00       	mov    $0x400614,%edi
  400579:	b8 00 00 00 00       	mov    $0x0,%eax
  40057e:	e8 9d fe ff ff       	callq  400420 <printf@plt>

	return 0;
  400583:	b8 00 00 00 00       	mov    $0x0,%eax
}

从上面的汇编可以看出,C++在printf输出const int a的地方,直接将a替换成了const int a的值“0”!虽然a在内存中的数据已经被修改为0xFF,但是printf输出的时候并不是输出a在内存中的值。注意C++中a的值同样被修改了,可以通过gdb来验证。

(gdb) l
1	#include <stdio.h>
2	
3	int main()
4	{
5		const int a = 0x00;
6		int *pi = (int *)&a;
7	
8		*pi = 0xFF;
9		printf("*pi=%d,a=%d\n",*pi,a);
10	
(gdb) n
6		int *pi = (int *)&a;
(gdb) n
8		*pi = 0xFF;
(gdb) n
9		printf("*pi=%d,a=%d\n",*pi,a);
(gdb) n
*pi=255,a=0
11		return 0;
(gdb) p a
$1 = 255
(gdb) 

编译器的这种行为叫做常量折叠,常量折叠就是将常量表达式计算求值,并用求得的值来替换表达式,放入常量表,在预编译阶段,编译器会将所有的常量进行求值,然后替换。这C++编译器提高性能的一种方式。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值