栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

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

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

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

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

0x01 实验

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

#include 

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++中借鉴的,真的只用于编译器检查,没有具体的优化动作。这就是所说,学只学得表象,未得其精髓。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/983099.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号