SCCS“什么”字符串未被编译器优化掉

时间:2009-01-27 17:34:05

标签: c compiler-optimization

我们尝试在二进制对象中嵌入一个字符串,以便我们可以看到已部署的可执行文件或共享库的版本号。通常我们在这个字符串中嵌入标准的CVS Id信息。例如,我们可能会嵌入:

const char cvsid[] = "@(#)OUR_TEAM_staging_remap_$Revision: 1.30 $ $Name:  $";

在C代码中。

从男人(1)那里:

  

what实用程序搜索每个文件名的出现次数   SCCS get命令的模式@(#)(参见sccs-get(1))   替换@(#)ID关键字,并打印后面的内容   最多为“,>,NEWLINE,\或NULL字符。

此变量只有一个实例,它永远不会被引用。有人建议这可能会被编译器优化掉。

我在C和C ++以及各种编译器中使用这种技术多年了,我还没有看到优化后的字符串。

任何人都知道为什么他们没有被优化掉?

6 个答案:

答案 0 :(得分:2)

通常这不会发生,因为无关的字符串成本很低,并且在这种情况下可能很有用(例如,存储一堆字符串资源,只有代码中实际引用的第一个)。

答案 1 :(得分:2)

它们可能没有被优化掉,因为你的编译器知道这些字符串可以用于那些目的。

当然,只要程序的行为(更准确地说是可观察的行为)没有改变,编译器就完全被允许优化它。这意味着对volatile的写入和读取顺序以及对库函数的调用不会改变。

通过在您的应用中优化此类字符串,我认为该行为不会改变。但是编译器希望可以使用并尝试不以用户方式进行攻击。这就是为什么它们也包含有用的扩展。如果你想确定它有时没有被优化,可能需要查看编译器扩展。 GCC具有unused属性,这使得它不会为未使用的对象发出警告。也许那个或类似的东西可以帮助你变量没有被优化掉。

从语言的角度来看,虽然强制编译器保留它但没有实用工具。

修改:有关于该主题here的usenet帖子,并提供了有用的答案。

答案 2 :(得分:2)

直到最近(我在2005年中期发现了这个问题),才有可能使用:

static const char sccs[] = "@(#)%W% %E%";

或类似的源代码和GCC和大多数其他编译器不会优化它。从大约那个时候发布GCC开始(可能是GCC 4.0.x,源自2005年4月),这些常量字符串被排除在二进制文件之外。所以,我不得不四处修改我的源代码,使变量在外部可见。编译器不可能单独查看目标文件并断定该字符串未使用,因为文件外部的某些内容可能会引用它。所以,我的文件现在包含:

#ifndef lint
extern const char jlss_id_filename_c[];
const char jlss_id_filename_c[] = "@(#)$Id$";
#endif /* lint */
好的 - 这是一个混合体;我真的使用RCS存储源代码,但我仍然更喜欢whatident来识别文件 - 此外我还有自己的what同时执行whatident {1}}加上我自己的一些调整。但是我在一些文件中有声明 - 不是全部 - 以及所有文件中的定义。 (在一些警告标志下,现在还没有记住,在声明变量之前我得到警告。可能是GCC中的一个变化解决了这个问题;我不再确定了。)

当我创建一个新文件时,我的模板生成器将'filename_c'替换为正在生成的文件的相应名称。类似地,对于标头 - 尽管标识字符串仅嵌入在一个文件中以避免多个定义。

我更喜欢使用静态常量的旧系统 - 但这对我来说已经有3年多了。

答案 3 :(得分:1)

Microsoft的Visual C ++ 2005有一个链接器选项,可以控制它对未使用数据的作用:/OPT:UNREF强制链接器保留未使用的数据,/OPT:REF允许它消除它。

但是,在我的简单测试中,该选项对语句

没有影响
static char VersionString[] = "HELLO_WORLD 2.0";

无论标志如何,字符串都出现在发布和调试二进制文件中。

答案 4 :(得分:1)

如果没有“static”关键字,则无法优化变量,因为另一个模块可能会声明它的引用(使用extern)。由于C / C ++通常一次编译一个文件,因此编译器无法知道是否存在外部引用。

通过添加static关键字,您可以告诉编译器该名称仅在编译中可见,并且可以对其进行优化。

我认为链接器可以检测到一个未使用的全局变量,并且如果对象格式允许的话也会对其进行优化,但我不确定是否有人这样做。

答案 5 :(得分:1)

(是的,我知道很久以前就被问到并回答了。但是这种新的答案是可用的,所以....)

gcc中(至少在3.3及以下),现在有编译器指令__attribute__((unused))将变量标记为"已知可能被引用"禁止警告,并__attribute__((used))将其标记为已使用(因此不是优化的候选者),即使没有其他代码实际引用它。

所以这可能适合你:

static const char what_ident[] __attribute__((used)) = "@(#) $Id$";

如果linker仍然优化了它,那么您可能需要将其放在标记为 keep 的部分中,而不管链接时间引用。

static const char what_ident[] __attribute__((section("what"), used)) = "@(#) $Id$";

并添加gcc选项-Wl,-bkeepfile:file.o,以便链接器不会抑制file.c输出中未引用的部分。