为什么这个 static const char* 初始化架构特定?

Why is this static const char* initialization architecture-specific?

此问题涉及

对上述问题的回答告诉我 static const char* 初始化必须针对在编译时可解析的地址。同样的答案指出 pointer 需要是 const,而不是(必须)指针指向的内容。这反映在下面的代码中。

那么,为什么以下代码依赖于目标体系结构?(即,为什么这段代码会在某些而非所有交叉编译器中生成编译器错误?)

// main.c

static const char* const text = "foo";
static const char* tmp = text;

int main( int argc, char* argv[] ) { return 0; }

编译成功:

$ /path/to/tgt1-linux-gnu-gcc --version && /path/to/tgt1-linux-gnu-gcc ./main.c && echo $?
tgt1-linux-gnu-gcc (crosstool-NG 1.23.0.485-ee829 - next) 8.2.0
Copyright (C) 2018 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

0

编译失败:

$ /path/to/tgt2-linux-gnueabi-gcc --version && /path/to/tgt2-linux-gnueabi-gcc ./main.c
tgt2-linux-gnueabi-gcc (crosstool-NG 1.18.0) 4.7.3 20130102 (prerelease)
Copyright (C) 2012 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

./main.c:4:1: error: initializer element is not constant

它不依赖于体系结构。您的编译器版本不同,可用的优化也不同。

通知:

8.2.0

对比

4.7.3 20130102 (prerelease)

您可以通过 -O2.

编译在 tgt2 GCC 上解决这个问题(未测试)

代码不依赖于体系结构,但确实不符合任何版本的 C 语言标准。编译器没有义务以一致的方式处理不一致的代码。他们甚至不需要拒绝,根据细节,他们甚至可能根本不需要说任何事情。

正如@JL2210 已经观察到的,您实际上使用了两个不同的编译器,即使它们都是 GCC 的版本。默认情况下,这些特定版本符合完全不同的语言标准——GCC 4 默认为 C89,而 GCC 8 默认为 C2011——但这本身并不是问题所在。 C 标准的所有版本在适用领域的措辞非常相似,细微差别不会改变它们在您的代码中的应用方式。

关于初始化程序,C89 包含此语言约束:

All the expressions in an initializer for an object that has static storage duration [...] shall be constant expressions.

而 C11 中的类似约束是

All the expressions in an initializer for an object that has static or thread storage duration shall be constant expressions or string literals.

在您对 C11 中的 "string literals" 添加感到兴奋之前,我注意到有问题的初始化表达式 text 明确地 不是 字符串文字,尽管所涉及的变量本身是如何初始化的。因此,问题归结为它是否是 "constant expression"。然而,不管其类型中的所有 const-ness,它都不是。

常量表达式是标准中定义的术语。有几种变体,但这里的表达式不是空指针常量,这里需要的替代方法归结为 地址常量 或这样的常量加上或减去整数常量表达式。这里不涉及整型常量表达式,所以地址常量本身是剩下的选项。

在C89中,这是相关的定义:

An address constant is a pointer to an lvalue designating an object of static storage duration, or to a function designator; it shall be created explicitly, using the unary & operator, or implicitly, by the use of an expression of array or function type. The array-subscript [] and member-access . and -> operators, the address & and indirection * unary operators, and pointer casts may be used in the creation an address constant, but the value of an object shall not be accessed by use of these operators.

C11 是这样说的:

An address constant is a null pointer, a pointer to an lvalue designating an object of static storage duration, or a pointer to a function designator; it shall be created explicitly using the unary & operator or an integer constant cast to pointer type, or implicitly by the use of an expression of array or function type. The array-subscript [] and member-access . and -> operators, the address & and indirection * unary operators, and pointer casts may be used in the creation of an address constant, but the value of an object shall not be accessed by use of these operators.

(在两种情况下都添加了强调。)

表达式 text 不是一元运算符 & 中的显式表达式。它不是转换为指针类型的整数常量。它不是数组类型(也不是函数类型)的表达式。

无论如何,您的代码不仅不符合要求,而且还违反了语言限制。因此,符合标准的编译器有义务发出诊断信息。 GCC 没有义务拒绝该代码,但这是一个符合要求的替代方案。在这种情况下,版本 8 意识到初始化器确实是常量是完全合理的,尽管在技术上不是常量表达式。但是,为了使其真正符合诊断要求,您可能需要为其提供 -pedantic 选项。发出所有此类必需的诊断正是该选项的目的。