如何创建可修改的编译期初始化局部变量,规避全局/static变量?
问题背景与需求
定义结构体类型的全局变量时,编译器会把结构体及其所有初始值直接嵌入二进制的
.data段,无需运行时创建。例如以下代码:
// 所有内容位于全局作用域 struct test { int x; int y; float z; }; test t{34, 1, 394.6};
使用MSVC x64最新版本、C++20 /O2编译后,对应的汇编输出如下:
_DATA SEGMENT test t DD 022H ; t DD 01H DD 043c54ccdr ; 394.6 _DATA ENDS ...
这种全局变量可读写,支持修改单个字段;而constexpr变量虽会在编译期生成相同结构,但处于只读状态,无法调整字段值。
当前需求:需要使用Win32 API的大型结构体TASKDIALOGCONFIG,希望获得全局初始化的优势(结构体在目标文件中提前创建,无需运行时初始化),但不能将其设为全局变量。同时调用结构体的函数会被多次执行,每次需修改结构体中1-2个字段,因此也不能使用static局部变量(静态变量仅初始化一次,后续调用会复用之前的修改,无法满足每次调整不同值的需求)。
尝试过的方案及问题
曾尝试用mutable修饰字段配合constexpr实现需求,示例代码如下:
struct test { mutable int x; int y; float z; }; void sample() { constexpr test xx{1, 2, 3}; xx.x = 23; // 模拟TASKDIALOGCONFIG结构体 TaskDialogIndirect(&xx); }
对应的编译输出:
__real@40400000 DD 040400000r ; 3 voltbl SEGMENT _volmd DD 0ffffffffH DDSymXIndex: FLAT:void sample(void) DD 0eH DD 043H voltbl ENDS xx$ = 32 __$ArrayPad$ = 48 void sample(void) PROC ; sample $LN3: sub rsp, 72 ; 00000048H mov rax, QWORD PTR __security_cookie xor rax, rsp mov QWORD PTR __$ArrayPad$[rsp], rax mov DWORD PTR xx$[rsp], 1 mov DWORD PTR xx$[rsp+4], 2 movss xmm0, DWORD PTR __real@40400000 movss DWORD PTR xx$[rsp+8], xmm0 mov DWORD PTR xx$[rsp], 23 lea rcx, QWORD PTR xx$[rsp] call void TaskDialogIndirect(test const *) ; TaskDialogIndirect mov rcx, QWORD PTR __$ArrayPad$[rsp] xor rcx, rsp call __security_check_cookie add rsp, 72 ; 00000048H ret 0 void sample(void) ENDP
但该方案存在两个无法解决的问题:
- 无法修改
TASKDIALOGCONFIG的结构体定义,它属于Win32 API头文件,不能添加mutable修饰; - 从编译清单可见,
constexpr变量依然是在运行时创建的,没有达到提前嵌入目标文件的效果。
提问
有没有可行的方案能满足需求?还是只能退而求其次使用static或全局变量?
内容的提问来源于stack exchange,提问作者Arush Agarampur
相关产品推荐
相关产品推荐

