为什么常量结构数组在按名称引用常量结构时不放在 .rodata 中?

Why isn't array of const structs placed in .rodata when it references a const struct by name?

本文关键字:结构 常量 rodata 数组 为什么 引用      更新时间:2023-10-16

我有以下结构的安排:

typedef struct { 
    int a;       
} Foo;
const Foo END = {0};   
const Foo table_1[] = {
    {2}, {0}           
};                     
const Foo table_2[] = {         
    {2}, END                    
};                              

基本上,我有一个结构体,以及该结构体的几个数组。现在,这些数组的内容永远不会改变:它们是一些查找表,在运行时使用,因此,在嵌入式环境中,我希望这些数据驻留在ROM中(我非常 ram约束,字面上为每几个字节而战)。可以想象,没有什么可以阻止所有这些表驻留在ROM中(所有表都是const)。

实际上,table_1最终在.rodata中(因此它在启动期间不会被复制到RAM中),table_2 -在.data中(消耗ROM和RAM)。当反汇编目标文件时,我还可以看到一些初始化table_2的代码。

我希望所有上述数组结束在.rodata,但它似乎只会发生,如果我写出结构的初始化"完整"(对不起,不确定,这是什么正确的术语)。

这些初始化的区别是什么?因为只有初始化不同,类型是相同的,实际数据也是相同的。它是否正在进行某种优化(不知道这里优化了什么)?有办法让它失效吗?我的意思是,我可以通过#define删除所有常见的表成员并完成它,但这看起来像是一个hack,而且,我真的很想了解这里发生了什么。

我正在使用gcc-arm-none-eabi工具链,使用-Os构建,gcc版本为4.8.1。

gcc 5.1及以上版本执行此优化;在[basic.start.init]:

中,它不是强制的,但由c++标准考虑。

3 - 允许使用静态存储执行非局部变量的初始化duration作为静态初始化,即使这种初始化不需要静态地完成

  • 初始化的动态版本不会改变命名空间中任何其他对象的值
  • 初始化的静态版本在初始化的变量中产生相同的值如果所有不需要静态初始化的变量都被初始化,则由动态初始化生成动态初始化。

如果你的gcc版本支持constexpr,那么标记END constexpr应该足以让它静态初始化table_2;您也可以将table_2标记为constexpr以确保(同上):

2 -执行常量初始化:[…]

  • 如果[…在初始化式中出现的所有完整表达式都是常量表达式。

为什么我们在这里需要constexpr -为什么const不够?这是因为const对象仍然可以有mutable成员(可能是成员的成员,等等),这将允许它在END初始化和table_2初始化之间进行更改:

struct Bar { mutable int a; };
const Bar END = {0};
int unused = ++END.a; // !!
struct Foo { int a; };
const Foo table_2[] = { {2}, {END.a} };

constexpr通常会阻止此操作,因为[expr. cn]const]/2确保具有mutable成员的复合对象不能用于constexpr对象的初始化。constexpr对象仍然可以拥有自己的mutable成员,但这会阻止它被用来初始化另一个constexpr对象。

假设是c++。

table_2的初始化不是c++标准中的常量表达式。常量表达式是所有不具有左值到右值转换(使用END时具有这种转换)的内容,除非左值(END):

  1. 具有整型或枚举类型,并引用完整的非易失性
  2. 使用常量表达式初始化const对象。

END既不是整型也不是enum。

  • 是指向字符串元素的非易失性全局值文字
  • END不是字符串字面值。

  • 具有文字类型,并引用使用constexpr定义的非易失性对象或其不可变的子对象
  • END没有被constexpr定义。

  • 具有文字类型,并引用一个非易失性对象,该对象的生存期开始于该表达式
  • 的求值。

    END的生命周期没有在table_2的评估范围内开始。

    因此编译器别无选择,只能将table_2初始化从静态上下文中移开。如果你用constexpr声明END,我想条件3就满足了,你最终会得到rodata中所有的东西。

    c++标准的相关文本在[expr]中。