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

Ric*_*cht 4 c++ assembly compilation const visual-c++

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

const int K = 123456 ;
Run Code Online (Sandbox Code Playgroud)

当我编译程序时,生成的可执行文件123456在所有使用该值的地方(数十次)都包含文字值。

但是,如果删除const限定符,则该值123456在整个可执行文件(在本.data节中)中仅出现一次。
这是我要寻找的结果。我希望该值123456仅出现一次,以便可以通过使用十六进制编辑器编辑.exe文件来更改它。

但是,我不想删除const限定符,因为我希望编译器防止我意外修改源代码中的常量。

是否可以指示编译器不内联所述常量的值?


我需要这样做的原因是使可执行文件易于被学生“修改”示例程序以改变其行为的学生容易地修改。对于没有经验的人,练习必须足够简单。

Ker*_*g73 5

如果您不想K被内联,则将其放在头文件中:

extern const int K;
Run Code Online (Sandbox Code Playgroud)

这意味着“ K在其他位置定义”。然后将其放在cpp文件中:

const int K = 123456;
Run Code Online (Sandbox Code Playgroud)

在所有使用的地方K,编译器只知道那K是一个const int声明的extern盟友。编译器不知道的值,K因此无法进行内联。链接器将K在cpp文件.data中的可执行文件部分中找到它的定义。

另外,您可以这样定义K

const volatile int K = 123456;
Run Code Online (Sandbox Code Playgroud)

这意味着“ K可能会发生神奇的变化,因此您最好不要假设其值”。它与先前的方法具有类似的效果,因为编译器不能内联,K因为它不能假定K总是这样123456。如果启用了LTO,则以前的方法将失败,但是volatile在这种情况下使用应该可以。

I must say, this is a really weird thing to do. If you want to make your program configurable, you should put the value of K into a text file and then read the file at startup.

  • @HeinzKessler好吧,`const`只能说它不能从代码中更改,在许多情况下,某些硬件(例如传感器数据)的值可能会更改,但不能从代码中更改。 (4认同)
  • 好主意,但是如果启用链接时间优化,则可能会失败。同意这首先是一件很奇怪的事情:) (2认同)

Pet*_*des 2

最简单的选择可能是将其声明为全局without const,因此编译器不能假设它仍然具有静态初始值设定项的值。

int K = 123456;
Run Code Online (Sandbox Code Playgroud)

假设您在程序中调用任何函数,即使链接时优化也无法知道库函数不会访问此全局函数。

如果您使用static int K = 123456;,编译器可能会注意到编译单元中没有函数写入该值,并且它们都没有传递或返回其地址,因此整个编译单元的转义分析可以发现它实际上是一个常量并且可以被优化掉。

(如果您确实希望它是这样static int K;,请包含一个您从未实际调用过的全局函数void setK(int x){K=x;}。如果没有链接时优化,编译器将不得不假设此编译单元之外的某些东西可能调用了该函数并更改了K,并且任何调用定义不可见的函数可能会导致此类调用。)


请注意,这volatile const int K = 123456;比不优化对优化的损害更大const,尤其是当您的表达式K多次使用时。

(但其中任何一个都会造成很大的伤害,具体取决于可能的优化。恒定传播可能是一个巨大的胜利。)

编译器需要发出 asm,K每次 C 抽象机读取它时,它只加载一次。(例如,读取K被视为可见的副作用,例如从 MMIO 端口或具有硬件观察点的位置读取。)

如果您想让编译器每个循环加载一次它,并假设它K是一个循环不变式,那么使用它的代码应该这样做int local_k = K;。这取决于您想重新阅读的频率K,即您执行/重做的范围local_k = K

在 x86 上,使用在 L1d 缓存中保持热状态的内存源操作数可能不会造成太大的性能问题,但它会阻止自动矢量化。

我需要这样做的原因是,学生可以轻松修改可执行文件,他们的任务是“破解”示例程序以改变其行为。对于没有经验的人来说,练习必须足够简单。

对于这个用例,yesvolatile正是您想要的。当场从内存中重新读取所有使用,比遵循寄存器中缓存的值稍微简单一些。

性能本质上是无关紧要的,你不会想要自动矢量化。可能只是轻微的优化,这样学生就不必在每个 C++ 语句之后费力地存储/重新加载所有内容。像 gcc 一样-Og是理想的选择。

对于 MSVC,也许可以尝试-O1一下-O2,看看它是否会造成任何混乱。我不认为它有一些选项,但不是太激进的优化,它可能是调试构建(适合单步执行 C++ 源代码,不适合读取 asm),或者针对大小或速度进行完全优化。