看看这段代码:
#includeusing namespace std; int main() { const char* str0 = "Watchmen"; const char* str1 = "Watchmen"; char* str2 = "Watchmen"; char* str3 = "Watchmen"; cerr << static_cast ( const_cast ( str0 ) ) << endl; cerr << static_cast ( const_cast ( str1 ) ) << endl; cerr << static_cast ( str2 ) << endl; cerr << static_cast ( str3 ) << endl; return 0; }
这会产生如下输出:
0x443000 0x443000 0x443000 0x443000
这是在Cygwin下运行的g ++编译器.即使没有打开优化(),指针也指向相同的位置.-O0
编译器是否总是进行优化以至于它会搜索所有字符串常量以查看它们是否相等?可以依赖这种行为吗?
它不能依赖,它是一种优化,不是任何标准的一部分.
我已将代码的相应行更改为:
const char* str0 = "Watchmen"; const char* str1 = "atchmen"; char* str2 = "tchmen"; char* str3 = "chmen";
-O0优化级别的输出是:
0x8048830 0x8048839 0x8048841 0x8048848
但对于-O1来说:
0x80487c0 0x80487c1 0x80487c2 0x80487c3
如您所见,GCC(v4.1.2)在所有后续子串中重用了第一个字符串.编译器选择如何在内存中排列字符串常量.
这是一个非常容易的优化,可能是这样,以至于大多数编译器编写者甚至根本不考虑它的优化.毕竟,将优化标志设置为最低级别并不意味着"完全天真".
编译器在合并重复的字符串文字方面的积极程度会有所不同.他们可能将自己限制在一个子例程中 - 将这四个声明放在不同的函数中而不是单个函数中,您可能会看到不同的结果.其他人可能会做整个编译单元.其他人可能依赖链接器在多个编译单元之间进行进一步合并.
您不能依赖此行为,除非您的特定编译器文档说明您可以.语言本身在这方面没有要求.即使可移植性不是一个问题,我也会在自己的代码中依赖它,因为即使在单个供应商的编译器的不同版本之间,行为也可能发生变化.
你肯定不应该依赖这种行为,但大多数编译器都会这样做.任何文字值("Hello",42等)将被存储一次,任何指向它的指针自然会解析为该单个引用.
如果您发现需要依赖它,那么请安全并重新编码如下:
char *watchmen = "Watchmen"; char *foo = watchmen; char *bar = watchmen;
你当然不应该指望这一点.优化器可能会对您做一些棘手的事情,应该允许这样做.
然而这很常见.我记得早在1987年,一位同学正在使用DEC C编译器并且有这个奇怪的错误,他的所有文字3都变成了11(数字可能已经改变以保护无辜者).他甚至做了一个printf ("%d\n", 3)
并打印出来11.
他打电话给我,因为它太奇怪了(为什么这会让人们想起我?),经过大约30分钟的头部刮擦,我们找到了原因.这是一条大致如下的线:
if (3 = x) break;
请注意单个"="字符.是的,这是一个错字.编译器有一个小错误并允许这个.效果是将整个程序中的所有文字3都转换为当时在x中发生的任何事情.
无论如何,它明确C编译器将所有文字3放在同一个地方.如果80年代的C编译器能够做到这一点,那么做起来就不会太难了.我希望它很常见.
我不会依赖于这种行为,因为我怀疑C或C++标准是否会明确这种行为,但编译器会这样做是有意义的.即使在没有为编译器指定任何优化的情况下,它也表现出这种行为; 它没有任何权衡.
C或C++中的所有字符串文字(例如"字符串文字")都是只读的,因此是常量.当你说:
char *s = "literal";
从某种意义上说,你将字符串向下转换为非const类型.然而,你无法取消字符串的只读属性:如果你试图操纵它,你将在运行时而不是在编译时被捕获.(const char *
在将字符串文字分配给您的变量时,这实际上是一个很好的理由.)