【问题标题】:Why would a const local array/struct ever not be static in c?为什么 const 局部数组/结构在 c 中永远不是静态的?
【发布时间】:2017-03-09 16:18:10
【问题描述】:

我正在为 FLASH 存储空间和 RAM 使用都受到关注的嵌入式系统编程。

根据 c11 标准第 6.2.4 节:

其标识符声明为没有链接且没有存储类说明符static 的对象具有自动存储持续时间 ...该对象的初始值是不确定的。如果 为对象指定了初始化,每次在块的执行中到达声明或复合文字时都会执行它

如果我理解正确,这意味着功能如下:

static void func(void){
   const struct MyStruct j={5,6,7,8};
   const char k[]="long string of some sort";
   /*...some code...*/
}

jk 需要在进入函数时(重新)初始化,这意味着 j 和 k 的存储位置必须放在 RAM 中,并从 FLASH 中的数据部分初始化像memcpy。这意味着它正在使用 RAM 和 FLASH 中的资源。与类似的东西相比:

static void func(void){
   static const struct MyStruct j={5,6,7,8};
   static const char k[]="long string of some sort";
   /*...some code...*/
}

现在jk 只初始化一次,可以直接从FLASH 引用,而不是复制到RAM。

不将您的(大于寄存器大小)consts 声明为static 有什么好处吗?

基本上,我想知道我是否应该始终将我的consts 声明为static,或者是否有理由不这样做。

【问题讨论】:

  • C 标准中没有堆栈。如果在启用优化的情况下编译,您是否检查过机器代码?
  • 添加static真的是一个微优化。
  • @Olaf 我从 C 标准中添加了一些引号并清理了我的术语。我确实检查了(优化的)机器代码,确实没有static 修饰符,我的consts 正在通过调用memcpy 初始化到堆栈中,但是static 他们只是直接从FLASH 引用。
  • @Rick:嗯,它可以在现代平台上生成相同的代码。但是,在 AVR 或 PIC 上会引起问题。这一切都取决于实际代码,这就是我们需要minimal reproducible example 和所有必要信息的原因。
  • @M.M:它不会造成伤害,但忽略它会导致大量的复制开销。在 PC 上这可能会很麻烦,但在 MCU 上可能会打乱整个时序。

标签: c embedded


【解决方案1】:

我用它做了一些实验,代码如下:

extern void other_call (int);
extern void other_call2 (const char *);

void test ()
{
    const int var = 32;
    const char * const var2 = "this is a string";
    int var3;

    other_call (var);
    var3 = var * 10;
    other_call2 (var2);
    other_call (var3);
}

然后我用:

arm-none-eabi-gcc -std=gnu99 -O0 -fno-common -g3 -Wall -c -fmessage-length=0 -fno-builtin -ffunction-sections -fdata-sections "-Wa,-adhln=test.s" -mcpu=cortex-m0 -mthumb -specs=redlib.specs -MMD -MP "../src/test.c"

并在const 初始化时得到了这个程序集:

  42 0004 00AF          add r7, sp, #0
   6:../src/test.c ****     const int var = 32;
  44                    .loc 1 6 0
  45 0006 2023          movs    r3, #32
  46 0008 FB60          str r3, [r7, #12]
   7:../src/test.c ****     const char * var2 = "this is a string";
  47                    .loc 1 7 0
  48 000a 0C4B          ldr r3, .L2
  49 000c BB60          str r3, [r7, #8]

编译器将 const 值 32 加载到 R3,然后存储在 RAM 中的堆栈中(R7 加载了 SP)。 对于字符串var2,编译器将执行与余数相同的操作,不同之处在于它将使用.L2,即字符串地址的占位符。

请注意,它确实在不必要的时候将变量写入了堆栈,因为它们是 const 并且不会被更改。

现在使用 `-O3 启用优化:

   6:../src/test.c ****     const int var = 32;
   7:../src/test.c ****     const char * var2 = "this is a string";
   8:../src/test.c ****     int var3;
   9:../src/test.c **** 
  10:../src/test.c ****     other_call (var);
  37                    .loc 1 10 0
  38 0002 2020          movs    r0, #32
  39 0004 FFF7FEFF      bl  other_call
  40                .LVL1:
  11:../src/test.c ****     var3 = var * 10;
  12:../src/test.c ****     other_call2 (var2);
  41                    .loc 1 12 0
  42 0008 0348          ldr r0, .L2
  43 000a FFF7FEFF      bl  other_call2

请注意,这里不涉及 STACK 操作。它将常量加载到寄存器并跳转到使用它们的函数。甚至 var3 也被优化了。

让我们看看没有const 限定符会发生什么?好吧,我不需要发布任何程序集,因为它没有什么不同。在建议的代码中,const 限定符不会在生成的程序集中产生任何变化。即使在优化-O0 编译器仍会为 const 变量分配堆栈。如果您真的想强制编译器避免堆栈,请启用优化。我还使用#define 而不是const 进行了测试,它仍然使用-O0 上的堆栈。

现在,转动变量static,编译器完成了它应该做的事情,它将变量存储在RAM中,并在使用前将其加载到寄存器中,但不在-O3中。启用优化后,编译器只是忽略了示例中的static。考虑到变量只被读取而从未被写入,这种优化是正确的。

我知道这是单个编译器的单一案例,但它代表了大多数情况,必须有一些极端案例 const 可能会产生我不知道的差异,但是,关于内部变量函数,如果你想优化使用标志不是const 也不是static

【讨论】:

  • static 限定符怎么样?当我添加时,我看到 msp430 编译器停止使用堆栈。
  • @Rick 我也在用静态做一些实验后编辑了答案。
猜你喜欢
  • 2015-06-07
  • 2019-09-27
  • 1970-01-01
  • 1970-01-01
  • 2013-06-10
  • 1970-01-01
  • 2017-07-04
  • 1970-01-01
相关资源
最近更新 更多