编译器未对SCCS中的“what”字符串进行优化处理。

3

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

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

在C语言代码中。

从man(1) what命令得知:

what工具会为每个文件名搜索模式为@(#)的出现情况,该模式是SCCS get命令(参见sccs-get(1))替换的@(#) ID关键字,并打印其后跟随“,”,“>”,NEWLINE,“\”或NULL字符的内容。

这个变量只有一个实例,且从未被引用。有人建议编译器可能会对其进行优化。

我在C和C++中使用了这种技术多年,使用了各种编译器,但我从未看到过what字符串被优化掉。

有人有想法为什么它们不会被优化掉吗?

6个回答

2
通常这种情况不会发生,因为多余的字符串成本很小,在某些情况下也可能有用(例如,存储一堆字符串资源,只有第一个实际上在代码中被引用)。

编译器可能不知道您是否以其他方式引用它。此外,在数据部分添加它也没有任何损害。 - user7116

2
它们可能不会被优化掉,因为编译器知道这样的字符串可以用于这些目的。
当然,只要程序的行为(更确切地说是可观察的行为)没有改变,编译器完全可以将其优化掉。这意味着对易失性变量的写入和读取序列以及库函数的调用没有改变。
通过优化应用程序中的这种字符串,我认为行为不会改变。但是编译器希望易于使用,并且尽量不妨碍用户。这就是为什么它们也包含有用的扩展。如果您想确保在某些情况下它不被优化掉,请查看编译器的扩展。GCC具有“unused”属性,使其不会对未使用的对象发出警告。也许类似的东西可以帮助你防止变量被优化掉。
从语言角度来看,没有强制编译器保留它的实用程序。
编辑:关于这个主题有一个Usenet帖子here,其中有有用的答案。

2
直到最近(我在2005年中期发现了该问题),仍然可以使用以下方法:
static const char sccs[] = "@(#)%W% %E%";

在源代码中,类似于这样的常量字符串在GCC和大多数其他编译器中都无法被优化掉。从那个时候开始(可能是2005年4月发布的GCC 4.0.x),这些常量字符串被省略在二进制文件中。因此,我不得不修改我的源代码,使变量外部可见。编译器无法仅仅通过查看目标文件来得出字符串未使用的结论,因为文件外的某些东西可能会引用它。所以,我的文件现在包含:

#ifndef lint
extern const char jlss_id_filename_c[];
const char jlss_id_filename_c[] = "@(#)$Id$";
#endif /* lint */

好的,这是一个混合体;我真正使用RCS来存储源代码,但是我仍然更喜欢使用what而不是ident来标识文件 - 而且我有自己的修改版what,可以同时执行whatident以及一些我自己的调整。但我在一些文件中有声明,而不是所有文件中都有声明,而在所有文件中都有定义。(在一些警告标志集下,我在变量被声明之前进行定义时会收到警告。可能是GCC的更改解决了这个问题;我不确定了。)

当我创建一个新文件时,我的模板生成器会用适当的文件名替换'filename_c'。头文件也是如此 - 但为了避免多次定义,识别字符串只嵌入在一个文件中。

我更喜欢旧系统的静态常量 - 但这已经对我有效了三年。


如果我们运气不好的话,你可能只能再用这个技巧三年,直到谷歌GCC团队在他们的[WHOPR effort](http://gcc.gnu.org/projects/lto/whopr.pdf)方面取得进展,这将使你的技巧过时。;-) - none
我猜在那个时候,我必须找到一种方法来打印或以其他方式使用任意名称列表。肯定有其他人想要像这样的可执行文件中的元数据;GCC应该提供一种机制来标记“这不是用于但仍必须出现在二进制文件中”。 - Jonathan Leffler

2
(是的,我知道这个问题很久以前就问过并得到了答案。 但是现在有了这种新式的答案,所以....)
在GCC(至少在3.3及以上版本)中,现在有编译器指令__attribute__((unused))来标记变量为“已知它可能不被引用”,以抑制警告,以及__attribute__((used))来标记它为使用(因此不是优化的候选对象),即使没有其他代码实际引用它。
因此,这可能适合您:
static const char what_ident[] __attribute__((used)) = "@(#) $Id$";

如果链接器仍将其优化掉,则可能需要将其放在标记为“保留”的部分中,无论链接时间引用如何。
static const char what_ident[] __attribute__((section("what"), used)) = "@(#) $Id$";

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


1

微软的Visual C++ 2005具有一个链接器选项,该选项应该控制它如何处理未使用的数据:/OPT:UNREF强制链接器保留未使用的数据,/OPT:REF允许其消除未使用的数据。

然而,在我的简单测试中,该选项对语句没有影响。

static char VersionString[] = "HELLO_WORLD 2.0";

无论标志是什么,在发布版本和调试版本的二进制文件中都出现了该字符串。

这可能取决于您使用的优化级别。 - Jonathan Leffler

1
没有 "static" 关键字,变量无法被优化掉,因为另一个模块可能会声明对它的引用(使用 extern)。由于 C/C++ 通常一次只编译一个文件,编译器无法知道是否存在外部引用。
通过添加 static 关键字,告诉编译器该名称仅在编译中可见,并且可以进行优化。
我认为链接器也可以检测未使用的全局变量并将其优化掉,如果对象格式允许的话,尽管我不确定是否有人这样做。

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