简体   繁体   English

clang/gcc 不能将全局变量设置为一个地址常量加上另一个地址常量

[英]clang/gcc cannot set global variables to an address constant plus another address constant

The program below compiles without errors.下面的程序编译没有错误。

#include <stdio.h>

char addr_a[8];
char addr_b[8];

unsigned long my_addr = (unsigned long)addr_a + 8;                          // PASS
// unsigned long my_addr = (unsigned long)addr_a + (unsigned long)addr_b;   // FAIL (error: initializer element is not constant)

int main() {
        printf("%lx\n", my_addr);
        return 0;
}

Interestingly, when I set unsigned long my_addr = (unsigned long)addr_a + (unsigned long)addr_b the compiler throws "error: initializer element is not constant."有趣的是,当我设置unsigned long my_addr = (unsigned long)addr_a + (unsigned long)addr_b时,编译器会抛出“错误:初始化元素不是常量”。

I know globals can only be initialized with a constant expression.我知道全局变量只能用常量表达式初始化。 I also know that the types of constant expressions that can be used in an initializer for a global are specified in section 6.6p7 of the C standard :我还知道可以在全局初始化程序中使用的常量表达式的类型在C 标准的第 6.6p7 节中指定:

More latitude is permitted for constant expressions in initializers.初始化器中的常量表达式允许更大的自由度。 Such a constant expression shall be, or evaluate to, one of the following:这样的常量表达式应为或评估为以下之一:

  • an arithmetic constant expression,算术常数表达式,
  • a null pointer constant,一个 null 指针常量,
  • an address constant, or地址常量,或
  • an address constant for a complete object type plus or minus an integer constant expression.完整 object 类型的地址常量加上或减去 integer 常量表达式。

Note that an address constant plus an integer constant is allowed, but not an address constant plus another address constant.请注意,允许一个地址常量加上一个 integer 常量,但不允许一个地址常量加上另一个地址常量。

Question:问题:

Why does the C standard restrict the ways you can initialize global variables?为什么 C 标准会限制初始化全局变量的方式? What is stopping the C standard from accepting unsigned long my_addr = (unsigned long)addr_a + (unsigned long)addr_b ?是什么阻止了 C 标准接受unsigned long my_addr = (unsigned long)addr_a + (unsigned long)addr_b

Objects with static storage duration (ie globals, plus locals defined as static ) can only be initialized with a constant expression .具有static 存储持续时间的对象(即全局变量,加上定义为static的局部变量)只能用常量表达式初始化。

The types of constant expression that can be used in an initializer for such an object is specified in section 6.6p7 of the C standard : object 的初始化程序中可以使用的常量表达式类型在C 标准的第 6.6p7 节中指定:

More latitude is permitted for constant expressions in initializers.初始化器中的常量表达式允许更大的自由度。 Such a constant expression shall be, or evaluate to, one of the following:这样的常量表达式应为或评估为以下之一:

  • an arithmetic constant expression,算术常数表达式,
  • a null pointer constant,一个 null 指针常量,
  • an address constant, or地址常量,或
  • an address constant for a complete object type plus or minus an integer constant expression.完整 object 类型的地址常量加上或减去 integer 常量表达式。

Note that an address constant plus an integer constant is allowed, but not an address constant plus another address constant.请注意,允许一个地址常量加上一个 integer 常量,但不允许一个地址常量加上另一个地址常量。 Granted this still isn't exactly what you have, as you have address constants casted to integer type.当然,这仍然不是您所拥有的,因为您将地址常量转换为 integer 类型。 So let's check 6.6p6 which defines an integer constant expression :因此,让我们检查 6.6p6,它定义了一个integer 常量表达式

An integer constant expression shall have integer type and shall only have operands that are integer constants, enumeration constants, character constants, sizeof expressions whose results are integer constants, _Alignof expressions, and floating constants that are the immediate operands of casts. An integer constant expression shall have integer type and shall only have operands that are integer constants, enumeration constants, character constants, sizeof expressions whose results are integer constants, _Alignof expressions, and floating constants that are the immediate operands of casts. Cast operators in an integer constant expression shall only convert arithmetic types to integer types , except as part of an operand to the sizeof or _Alignof operator. integer 常量表达式中的强制转换运算符只能将算术类型转换为 integer 类型,但作为sizeof_Alignof运算符的操作数的一部分除外。

This paragraph doesn't allow for casting an address constant to an integer type, but apparently this seems to be supported as an extension.本段不允许将地址常量转换为 integer 类型,但显然这似乎作为扩展得到支持。

What is stopping the C standard from accepting unsigned long my_addr = (unsigned long)addr_a + (unsigned long)addr_b?是什么阻止了 C 标准接受 unsigned long my_addr = (unsigned long)addr_a + (unsigned long)addr_b?

The underlying reason is "Because why would anyone want that?"根本原因是“因为为什么会有人想要那个?” It's not meaningful to add two absolute addresses together;两个绝对地址相加没有意义; the result isn't the address of anything in particular.结果不是任何特定的地址。

It's thus a sort of chicken-and-egg thing.因此,这是一种先有鸡还是先有蛋的事情。 The language doesn't support it because it's useless, but also because existing linkers and object file formats don't support such a relocation.该语言不支持它,因为它没用,还因为现有的链接器和 object 文件格式不支持这种重定位。 For instance, for ELF on x86-64, see the psABI Table 4.9 for a list of supported relocations, and note there is no S+S .例如,对于 x86-64 上的 ELF,请参阅psABI表 4.9 以获取支持的重定位列表,并注意没有S+S And the linkers don't support it because it's useless, and because the language doesn't require it to be supported.链接器不支持它,因为它没用,而且语言不需要支持它。

I guess originally, the tools probably came before the language (the earliest C compilers would have used linkers designed for assembly programs).我猜最初,这些工具可能出现在语言之前(最早的 C 编译器会使用为汇编程序设计的链接器)。 So the original tools probably didn't support this, the language saw no need to demand that they do so, and over time, neither one ever saw a need to add it.所以最初的工具可能不支持这一点,语言认为没有必要要求他们这样做,而且随着时间的推移,也没有人认为需要添加它。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM