1

这个问题与为什么需要静态字符 * 而不是静态字符 * 的常量初始化有关

对上述问题的回答告诉我,static const char*初始化必须是在编译时可解析的地址。同样的答案指出,指针必须是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
4

2 回答 2

3

它不依赖于架构。您的编译器版本以及可用的优化都不同。

注意:

8.2.0

对比

4.7.3 20130102 (prerelease)

您可能可以tgt2通过使用-O2.

于 2019-10-21T23:00:50.303 回答
2

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

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

关于初始化器,C89 包含以下语言约束:

具有静态存储持续时间的对象的初始化程序中的所有表达式 [...] 应为常量表达式。

而 C11 中的类似约束是

具有静态或线程存储持续时间的对象的初始化程序中的所有表达式都应为常量表达式或字符串文字。

在您对 C11 中的“字符串文字”添加感到兴奋之前,我注意到有问题的初始化表达式 ,text明确不是字符串文字,尽管所涉及的变量本身是如何初始化的。因此,问题归结为它是否是“常量表达式”。然而,不管const它的类型有什么特点,它都不是。

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

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

地址常量是指向指定静态存储持续时间对象的左值的指针,或指向函数指示符的指针;它应使用一元 & 运算符显式创建,或使用数组或函数类型的表达式隐式创建。数组下标 [] 和成员访问。和 -> 运算符、地址 & 和间接 * 一元运算符以及指针转换可用于创建地址常量,但不应使用这些运算符访问对象的值。

C11 是这样说的:

地址常量是空指针、指向指定静态存储持续时间对象的左值的指针或指向函数指示符的指针;它应使用一元 & 运算符或转换为指针类型的整数常量显式创建,或通过使用数组或函数类型的表达式隐式创建。数组下标 [] 和成员访问。和 -> 运算符、地址 & 和间接 * 一元运算符以及指针转换可用于创建地址常量,但不应使用这些运算符访问对象的值。

(在这两种情况下都添加了重点。)

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

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

于 2019-10-22T00:26:11.110 回答