如何指示VC++编译器不将常量内联?

5

我在我的C++程序中有以下全局常量:

const int K = 123456 ;

当我编译程序时,生成的可执行文件在所有使用该值(数十次)的地方都包含文字值123456
但是,如果我去掉const限定符,数值123456只会出现一次(在.data部分中)整个可执行文件中。
这是我期望的结果。我希望数值123456仅出现一次,以便可以通过使用HEX编辑器轻松地更改.exe文件进行修改。
然而,我不想删除const 限定符,因为我希望编译器防止我意外修改源代码中的常量。
是否有可能指示编译器以某种方式不内联所述常量的值?
我需要这样做的原因是,可执行文件可以轻松地被学生修改,他们将被要求“破解”示例程序以改变其行为。 这个练习必须足够简单,适合没有经验的人。
3个回答

5
如果您不想内联 K,则将以下内容放入标头文件中:
extern const int K;

这意味着“K在其他地方被定义”。然后将此放入cpp文件中:
const int K = 123456;

在所有使用 K 的地方,编译器只知道 K 是一个被 extern 声明为 const int 的变量。编译器不知道 K 的值,因此它不能被内联。链接器将在 cpp 文件中找到 K 的定义,并将其放置在可执行文件的 .data 部分。
或者,您可以像这样定义 K:
const volatile int K = 123456;

这意味着“K值可能会以神奇的方式改变,因此您最好不要假设其值”。这与上一种方法具有类似的效果,因为编译器不会内联 K,因为它不能假设 K 始终为 123456。如果启用 LTO,则上一种方法将失败,但在这种情况下使用 volatile 应该是可行的。
我必须说,这是一个非常奇怪的做法。如果您想使程序可配置,应该将 K 的值放入文本文件中,然后在启动时读取该文件。

2
好主意,但如果启用链接时优化,这可能会出现问题。虽然同意这是一件非常奇怪的事情 :) - Thomas
volatile const 有类似的效果吗? - Richt
@Richt 我完全忘记了 volatile!我会更新我的答案。 - Indiana Kernick
4
“const”只是表示在代码中无法更改变量的值,但有很多情况下一个值可能会因为某些硬件(比如传感器数据)而发生改变,但是不能从代码中改变它。 - πάντα ῥεῖ
@πάνταῥεῖ:啊,我明白了。有道理! - Heinz Kessler
显示剩余2条评论

2
最简单的选择可能是将其声明为全局变量,不要使用const,这样编译器就不能假设它仍然具有静态初始化程序的值。
int K = 123456;

即使在你的程序中调用任何库函数,链接时优化也无法知道该库函数是否访问此全局变量。如果你使用了static int K = 123456;,编译器可以注意到编译单元中没有函数写入该值,也没有传递或返回其地址,因此整个编译单元的逃逸分析可以发现它实际上是一个常量,并可被优化掉。(如果你真的想让它成为static int K;,就包含一个全局函数,如void setK(int x){K=x;},你从未真正调用它。没有链接时优化,编译器将不得不假定这个编译单元外的某些东西可能已经调用了这个函数并改变了K,而且任何调用不可见函数定义的函数都可能导致这样的调用。) 请注意,volatile const int K = 123456;对于使其非const的优化影响要大得多,特别是如果你有使用K多次的表达式。(但是这两种情况都可能会受到很大的影响,具体取决于可能的优化。常数传播可以是一个巨大的胜利。)编译器需要发出精确加载asm,每次C抽象机读取它时都会加载K一次。(例如,读取K被认为是可见的副作用,就像从MMIO端口或具有硬件监视点的位置读取一样。)如果你想让编译器每个循环加载一次,并假定K是循环不变的,则使用它的代码应该使用int local_k = K;。你可以自行决定要多少次重新读取K,即在何处/何时重新执行local_k = K
在x86上,使用保持在L1d缓存中的内存源操作数可能不是太大的性能问题,但它将防止自动向量化。
我需要这样做的原因是让学生轻松地修改可执行文件,他们将被要求“破解”示例程序以改变其行为。这个练习对于没有经验的人来说必须足够简单。
对于这种用例,是的,volatile正是你想要的。所有使用都在现场重新读取内存,使其比遵循寄存器中缓存的值稍微简单一些。而且性能基本上是无关紧要的,你也不需要自动向量化。可能只需轻微优化,以便学生不必在每个C++语句之后浏览所有存储/重新加载的内容。像gcc的-Og就很理想。

对于MSVC,也许可以尝试使用-O1-O2选项,看看是否会产生混淆。我认为它没有某些不太激进的优化选项,可能是调试构建(适合单步执行C++源代码,但不适合阅读汇编代码),也可能是完全针对大小或速度进行了优化。

最初的回答:

使用MSVC时,可以尝试使用-O1-O2选项,查看是否会有任何混淆情况。该编译器似乎没有一些不太激进的优化选项,可能只有调试构建(适合单步执行C++源代码,但不适合阅读汇编代码)或完全优化大小或速度。


0

尝试将常量声明为volatile。这应该会产生一个单一且可变的值,不会被内联。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接