常量折叠

之前面试xx公司的时候被问到关于const常量折叠的问题,当时就挺懵逼的。。。
下去后,查了下,这个常量折叠还是蛮有意思的,工作中也竟然没有注意到这点,不过话说这种写法应该也没人会去写吧。。。
先贴上一段代码:

#include <iostream>
using namespace std;

int main()
{
	const int a = 10;
	int* pa = const_cast<int*>(&a);
	*pa = 20;

	printf("&a=%p\n", &a);
	printf("pa=%p\n", pa);
	printf("a=%d\n", a);
	printf("*pa=%d\n", *pa);

    return 0;
}

运行上面的代码,就会发现a的地址和pa指向的地址是一模一样的,但是,他们的取值却是不同的,a的值依旧为10,而*pa的值位20。
这个结果挺颠覆我们对指针的理解是不是。地址都是一样的,为何通过指针访问地址修改数据后,a的值没有被变化呢??
利用vs的反汇编调试,断点进去查看,发现汇编码是这样子的:

printf("a=%d\n", a);
00CF1F80  push        0Ah  
00CF1F82  push        offset string "a=%d\n" (0CF8BE8h)  
00CF1F87  call        _printf (0CF13F2h)  
00CF1F8C  add         esp,8  
printf("*pa=%d\n", *pa);
00CF1F8F  mov         eax,dword ptr [pa]  
00CF1F92  mov         ecx,dword ptr [eax]  
00CF1F94  push        ecx  
00CF1F95  push        offset string "*pa=%d\n" (0CF8B3Ch)  
00CF1F9A  call        _printf (0CF13F2h)  
00CF1F9F  add         esp,8  

通过这个汇编码就能看到,a的值实际上并不是通过内存取出来的,而是直接将0Ah,也就是10这个值直接拿出来了。而pa取值是通过寄存器来内存取值完成的。
这个其实就是所谓的const的内存折叠,其实就是编译器的优化手段,对于const会再预编译时就进行优化处理,其指向的值直接从常量表获取,而不再通过内存访问取值,以此来加快效率。
如果一定要修改const的值,可以加入关键字volatile即可。比如讲上述代码对a的定义改成const volatile int a = 10; 这样子便能告诉编译器,我们的a是可变的,每次取值编译器就会从内存获取。当然,这个volatile的具体优化也取决于编译器。

### 常量折叠优化的限制条件与失效场景 常量折叠是一种编译时优化技术,旨在将可以在编译阶段计算出结果的表达式直接替换为其结果,从而减少运行时的计算开销。然而,这种优化并非在所有情况下都能被应用,其限制条件和失效场景主要包括以下几种情况: 1. **运行时变量参与表达式计算** 当表达式中包含在运行时才能确定值的变量时,编译器无法进行常量折叠。例如,非 `final` 修饰的变量可能在运行时被修改,其值在编译时无法确定。这种情况下,编译器无法将表达式视为常量,从而无法进行优化[^1]。 ```java int a = 5; int b = 10; int c = a + b; // 此处 a 和 b 不是 final 变量,无法进行常量折叠 ``` 2. **涉及副作用的操作** 如果表达式中的某些操作会产生副作用(如函数调用、I/O 操作、修改全局变量等),则编译器通常不会进行常量折叠。因为折叠这些表达式可能会导致副作用丢失,从而影响程序行为。 ```java int x = someFunction() + 5; // someFunction() 可能有副作用,无法进行常量折叠 ``` 3. **动态加载的类或反射机制** 在 Java 等语言中,如果表达式依赖于运行时动态加载的类或通过反射机制获取的值,则编译器无法在编译时确定其值,因此无法进行常量折叠。 4. **浮点数精度问题** 某些浮点数运算在编译时可能会因精度问题而无法被折叠。例如,涉及浮点数的舍入误差、NaN(非数字)或无穷大的表达式,可能导致编译器无法安全地进行折叠[^2]。 ```java double d = 0.1 + 0.2; // 浮点精度问题可能导致无法进行常量折叠 ``` 5. **编译器优化级别限制** 常量折叠依赖于编译器的优化级别。如果编译器被配置为较低的优化级别(如 `-O0`),则可能不会执行常量折叠优化。在较高优化级别(如 `-O2` 或 `-O3`)下,编译器会尝试进行更多常量折叠操作[^2]。 6. **跨模块或跨文件的常量引用** 如果表达式中引用的常量定义在另一个模块或文件中,并且该常量不是 `inline` 或 `constexpr`(在 C++ 中),则编译器可能无法在当前模块中进行常量折叠,因为其值在当前编译单元中不可见。 7. **使用 `volatile` 修饰的变量** 在 C/C++ 中,`volatile` 修饰的变量表示其值可能在程序之外被修改(例如由硬件或中断处理程序)。因此,编译器不会对涉及 `volatile` 变量的表达式进行常量折叠。 ```cpp volatile int flag = 1; int result = flag + 5; // 由于 flag 是 volatile,无法进行常量折叠 ``` 8. **泛型或模板参数依赖** 在 C++ 模板编程中,如果表达式依赖于模板参数或泛型类型,且这些参数在实例化时才确定,则编译器可能无法在早期阶段进行常量折叠。 9. **异常处理或控制流依赖** 如果表达式的结果依赖于异常处理机制或控制流结构(如 `if`、`switch`、`try-catch` 等),则编译器无法在编译时确定其值,因此无法进行常量折叠。 --- ###
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值