问题 在C中,const变量是否保证在内存中是不同的?


说到字符串文字,C99标准说(6.4.5.6):

如果这些数组的元素具有适当的值,则这些数组是否不同是未指定的。如果程序试图修改此类数组,则行为未定义。

我找不到类似的警告或对const变量的明确保证。可以表达 &x == &y 在上下文中 const int x=12; const int y=12; 评价为 1? const变量和字符串文字怎么样(即是 &x == "\014\000\000" 保证是 0 甚至在32位小端平台上)?

对于它的价值,在“字符串文字”部分 这篇博文 给出了问题的背景。


9496
2018-06-04 12:34


起源

请注意,gcc只会发出 x 和 y 如果可以采用其地址,那么它会在不同的地址发出它们。 - ninjalj
也可以看看: C语言中限定词的深层分析 - Cody Gray♦


答案:


在标准中,平等在第6.5.9节“平等运算符”中讨论, & 在第6.5.3.2节“地址和间接运算符”中讨论,以及 const 在第6.7.3节“类型限定符”中讨论。关于指针平等的相关段落是§6.5.9.6:

两个指针比较相等,当且仅当两个指针都是空指针时,它们都指向   相同的对象(包括指向对象和开头的子对象的指针)或函数, [或超过数组末尾的指针]

唯一的定义 & 就是那个“一元 & 运算符产生其操作数的地址。 [...]结果是指向由其操作数指定的对象或函数的指针。“(§6.5.3.2.3)。遗憾的是,“地址”一词没有正式定义;但是不同的对象(用于由...定义的相等) ==)具有不同的地址,因为地址是由上面的相等定义区分的指针。

至于的意思 const,§6.7.3并未表明这一点 const 与对象的对象有关(这是“执行环境中的”数据存储区域,其内容可以通过§3.14表示值“)。脚注进一步表明“如果从未使用过地址,则实现不需要为这样的对象分配存储”。虽然这是非规范性的,但强烈表明如果使用地址则必须分配存储 对于每个对象

请注意,如果对象是 const volatile,那就相当清楚了(尽可能清楚 volatile 他们不能拥有相同的地址,因为 const volatile 对象是可执行的可变对象。 (§6.7.3.10有一个使用例子 const volatile。)

即使在非易失性情况下, const 仅表示该程序的这一部分不允许修改该对象,而不是该对象通常是只读的。合并一个存储 const 对于其他东西的对象,大胆的实现者必须保证没有任何东西可以修改对象。对于具有单独编译的实现中具有外部链接的对象而言,这是相当困难的(但当然,我们正在远离标准并进入未实践的领域)。

如果这是关于编写C程序,那么您可以通过为对象提供不同的值来增加机会:

const int x = __LINE__;
const int y = __LINE__;

如果这是关于C的理论模型,我会选择使对象区别开来。您必须通过在论文(扩展版本)的段落中总结答案来证明这一选择的合理性。

另一方面,如果这是关于编写优化编译器,我怀疑它会伤害许多真实世界的程序来合并常量。我想在嵌入式编译器中进行合并,用户习惯于使用边缘情况安全地播放它,并且节省的内存可以是不可忽略的。我反对在托管平台上合并,任何收益都可以忽略不计。

(参考文献 N1256 a.k.a. C99 + TC3。我不认为版本有所作为。)


5
2018-06-04 13:49



当然,如果您要实现不符合要求的优化,即使在嵌入式目标上,您也应该将其关闭并打开。在这种情况下假设在任何地方都需要它,你也可以为任何/所有目标提供它,并留给用户决定它是否会破坏他们的程序以及他们是否需要节省空间。 - Steve Jessop
我喜欢你的论点,但关于非易失性const的段落“Even ...”, GCC 编译 const int x = 12; int y; main() { y = x; f(); y += x; printf("%d\n", y); }成 movl $12, … 和 addl $12, …, 假如说 f() 不会更改const变量 x (它假设 f() 可能会改变 y但是。 - Pascal Cuoq
@Pascal:为了让这个测试可靠,你需要说服gcc那个地址 x 被采取,即有 &x 在一些不会被优化的地方。你还需要说服gcc它不知道是否 f 可能会看到 x;有 x 是 extern 和 f 在另一个文件中定义应该这样做。 - Gilles
啊,是的,拿着地址 x 以一种可能泄漏的方式 f() 更好(和 GCC 继续使用 $12)。注意 f()放在另一个文件中,可以访问 x 在初始版本中,因为 x 不是 static 无论如何。 - Pascal Cuoq
GCC有这样一面旗帜: -fmerge-all-constants。它的描述有以下有趣的句子: 像C或C ++这样的语言要求每个非自动变量具有不同的位置,因此使用此选项将导致不一致的行为。 - ninjalj


据我所知,标准不允许任何类型的两个命名对象具有相同的地址(联盟成员除外)。从6.5.9 / 6:

两个指针比较等于if和only   如果两者都是空指针,则两者都是   指向同一个对象的指针......

字符串文字不是const变量,所以你的第二个问题没有实际意义,我不知道32位和字节序有什么关系。


6
2018-06-04 12:43



好吧,提出第二个问题 const char t[4]={'a', 'b', 'c'}; 和 "abc" 如果你更喜欢。 - Pascal Cuoq
与它有关的是对象表示 x (整数12)恰好在32位小端平台上发生,包含与字符串文字的对象表示相同的字节 "\014\000\000"。所以我认为问题的一部分是,可以将字符串文字折叠成 x,或者只能将字符串文字折叠成其他字符串文字。 - Steve Jessop
@Steve哦,我明白了。谢谢。 @Pascal嗯,据我所知,标准只说字符串文字的值可能不明显。但是,为负面提供证据很难。 - Neil Butterworth


const int x=12;
const int y=12;

x 和 y 是不同的变量(都是const限定的)因此具有不同的地址。

对于另一个例子也是如此。

注意 const 是对象的限定符。关于内存布局,如果它存在与否则没有区别。


3
2018-06-04 12:45





6.4.5 / 6说明了与字符串文字对应的数组:

这些阵列是不确定的   是不同的提供他们的元素   有适当的价值观。

这是一个允许折叠字符串文字的特定规则。我不知道标准中对于其他对象说同样的事情。


1
2018-06-04 13:06