为什么常量初始化需要 static char* 而不是 static char**

Why is constant initialization need for static char* but not static char**

有人可以解释为什么这段代码...

// main.c

#include <stddef.h>

static const int    g_a   = 1;
static const char*  g_b   = "hello";
static const char*  g_c[] = { "a",    "b",    NULL };

typedef struct Foo
{
  int           a;
  const char*   b;
  const char**  c;
} Foo;

static Foo f[] =
{
  { g_a,
    g_b,
    g_c }
};

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

...产生此错误:

> gcc --version && gcc -g main.c 
gcc (GCC) 8.2.1 20181215 (Red Hat 8.2.1-6)
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.

main.c:19:5: error: initializer element is not constant
     g_b,
     ^~~
main.c:19:5: note: (near initialization for 'f[0].b')

我了解到编译器要求一个常量来初始化f[0].b,所以下面的初始化是编译错误的解决方案:

static Foo f[] = { { g_a, "hello", g_c } };

但是 为什么编译器不为 f[0].c 的初始化发出类似的 "constant required" 错误?(或者,就此而言,f[0].a?) 为什么这只是 f[0].b 的问题?

f[0].a 具有类型 "non-constant int" 并由类型为 "const int" 的 g_a 的值初始化,该类型在 运行 时不能更改并且是在编译时已知。所以这里没有错误。

f[0].b 的类型为 "non-constant pointer to const char",应由 g_b 的值初始化,该值也是 "non-constant pointer to const char"。即使它有一个初始化程序 g_b 可以在 运行 时间更改,并且 IIRC 初始化顺序未确定。所以它的值在编译时是未知的,因此是错误的。

f[0].c 具有类型 "non-constant pointer to non-constant pointer(s) to const char" 并由 g_c 初始化,它是类型 "non-constant pointer to const char" 的元素数组。数组的符号可以用作编译时已知的常量指针。所以这里没有错误。

这就是你所缺少的,我认为: 如果你想要一个指针是 const,请将修饰符放在指针上,在 [=24 后面=],而不是指向的值,像这样:[const] char * const pointer.

正式定义:看'address constant'定义:"C Language refence manual 6.19":

The address constant is a pointer to an object that has static storage duration or a pointer to a function. You can get these by using the & operator or through the usual conversions of array and function names into pointers when they are used in expressions. The operators [], ., ->, & (address of) and * (pointer dereference) as well as casts of pointers can all be used in the expression as long as they don't involve accessing the value of any object.

实际答案:"C"要求将静态变量初始化为可在编译时计算的常量表达式。常量计算可以使用 static/global 变量的地址 - 实际地址直到 link 时间才知道。

在那些情况下(例如,char *char_p = &char_var,或类似的),编译器将生成汇编指令以在 [= 处标记 'relocation' 的计算值48=] 时间。由 static/global 变量表示的重定位。在 link 时,linker 将 static/global 的实际地址添加到存储值中。

考虑:char char_var = 'A' ; char *char_p = &char_var;

        .file   "b.c"
        .text
        .globl  char_var
        .data
        .type   char_var, @object
        .size   char_var, 1

   # Char var initialized with a constant (65=A)
char_var:
        .byte   65
        .globl  char_p
        .section        .data.rel.local,"aw",@progbits
        .align 8
        .type   char_p, @object
        .size   char_p, 8

    # Initialize char_p to global symbol, actual address resolved at link time.
char_p:
        .quad   char_var

        .ident  "GCC: (Ubuntu 7.4.0-1ubuntu1~18.04.1) 7.4.0"
        .section        .note.GNU-stack,"",@progbits

linker 在 link 时计算地址的能力有限。它仅限于静态地址 +/- 常量偏移量:

  1. &static_var
  2. &static_var + 编译时间常数
  3. &static_var - 编译时间常量

但不是'&static_var_1 - &static_var_2',它会给出一条错误消息,提示已达到限制: b.c:3:9: error: initializer element is not computable at load time int v = &char_var - &char_v2 ;