通过实例研究C++常量传播

某年面试候选人时,突然想到一个问题。C/C++中的常量是否真的不能修改?因为C/C++中常量只是给编译器看的,用于编译器的代码检查,所以理论上是可以绕过其约束进行修改的。于是有以下的实验。

0x01 实验

如下代码,在Linux下使用gcc 11.2使用默认参数g++ const.cpp进行编译并运行。大部分人可能跟我一开始理解一样,两行的输出应该是一样的。

#include <iostream>

using namespace std;

int main()
{
    const int a = 20;
    int* b = (int*) &a;

    *b = 10;

    cout << a << " " << *b << endl;
    cout << *&a << " " << *b << endl;
}

结果如下:

20 10
10 10

意外吧!

添加-g参数看下汇编代码。如下

   0x00005555555551e9 <+0>:     endbr64
   0x00005555555551ed <+4>:     push   %rbp
   0x00005555555551ee <+5>:     mov    %rsp,%rbp
   0x00005555555551f1 <+8>:     sub    $0x20,%rsp
   0x00005555555551f5 <+12>:    mov    %fs:0x28,%rax
   0x00005555555551fe <+21>:    mov    %rax,-0x8(%rbp)
   0x0000555555555202 <+25>:    xor    %eax,%eax
   0x0000555555555204 <+27>:    movl   $0x14,-0x14(%rbp) # 对a赋值
   0x000055555555520b <+34>:    lea    -0x14(%rbp),%rax 
   0x000055555555520f <+38>:    mov    %rax,-0x10(%rbp) # 将a地址赋给b
   0x0000555555555213 <+42>:    mov    -0x10(%rbp),%rax # 取b的地址
   0x0000555555555217 <+46>:    movl   $0xa,(%rax) # 将b指向的内存内容设为10
=> 0x000055555555521d <+52>:    mov    $0x14,%esi 
   0x0000555555555222 <+57>:    lea    0x2e17(%rip),%rax        # 0x555555558040 <_ZSt4cout@GLIBCXX_3.4>
   0x0000555555555229 <+64>:    mov    %rax,%rdi
   0x000055555555522c <+67>:    call   0x5555555550f0 <_ZNSolsEi@plt>
   0x0000555555555231 <+72>:    mov    %rax,%rdx
   
   ...
   
=> 0x000055555555526b <+130>:   mov    -0x14(%rbp),%eax
   0x000055555555526e <+133>:   mov    %eax,%esi
   0x0000555555555270 <+135>:   lea    0x2dc9(%rip),%rax        # 0x555555558040 <_ZSt4cout@GLIBCXX_3.4>
   0x0000555555555277 <+142>:   mov    %rax,%rdi
   0x000055555555527a <+145>:   call   0x5555555550f0 <_ZNSolsEi@plt>

通过汇编也确实看到a指向的地址内容被修改了。第1个胖箭头指向的那行正是关键,cout的入参为$0x14,居然是个立即数。第2行cout对应第2个胖箭头,参数来自a指向的地址。

0x02 解释

一开始的理解没毛病。“只是给编译器看的,用于编译器的代码检查”,这句话不错,但是编译器还做了我们没想到的事情,即编译期优化。编译器看到a为常量20,就会在编译生成汇编代码时,将a出现的地方都替换为立即数0x14,即16进制20。这也就是标题中所说的常量传播。

Q1 第2行为什么没被优化?

A1 因为编译器没智能到识别出这种优化点,再说了,这种骚操作一般脑子正常的人(😃)是不会做的。

Q2 C语言中的行为是什么样的?
A2 试了下,C语言没有这个现象。两行输出结果是一样的,猜测下原因,const是C从C++中借鉴的,真的只用于编译器检查,没有具体的优化动作。这就是所说,学只学得表象,未得其精髓。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值