为什么我的初始化程序在一个版本中是常量,但在另一个版本中无效?

abe*_*nky 2 c static initialization compound-literals language-lawyer

我试图声明一个静态结构数组,并且可以在全局声明它时这样做,但在函数内声明静态时则不行。

这是一些可以正常工作的示例代码:

#include <stdio.h>

enum
{
    Mammals,
    Amphibians,
    Avians,
    Fish,
    Human,  Elephant,   Horse,
    Frog,   Salamander,
    Eagle, Sparrow, Duck,
    Salmon, Carp, Tuna
};

typedef struct
{
    int category;
    int* examples;
    int num_examples;
} classification_t;

static classification_t classifications[] = {
    { Mammals,    (int[]){Human, Elephant, Horse}, 3 },
    { Amphibians, (int[]){Frog, Salamander},       2 },
    { Avians,     (int[]){Eagle, Sparrow, Duck},   3 },
    { Fish,       (int[]){Salmon, Carp, Tuna},     3 }
};

int main(void)
{
    for(int i=0; i<4; ++i)
    {
        printf("Category %d has %d examples\n", classifications[i].category, classifications[i].num_examples);
    }
    
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

输出:

Category 0 has 3 examples
Category 1 has 2 examples
Category 2 has 3 examples
Category 3 has 3 examples
Run Code Online (Sandbox Code Playgroud)

这是可行的,因为大分类表是一个静态全局变量。

但是,当我尝试在(或任何其他函数)内移动表格时main,出现错误:

prog.c:25:17: error: initializer element is not constant { Mammals,    (int[]){Human, Elephant, Horse}, 3 },
Run Code Online (Sandbox Code Playgroud)

在此代码中:

#include <stdio.h>

enum
{
    Mammals,
    Amphibians,
    Avians,
    Fish,
    Human,  Elephant,   Horse,
    Frog,   Salamander,
    Eagle, Sparrow, Duck,
    Salmon, Carp, Tuna
};

typedef struct
{
    int category;
    int* examples;
    int num_examples;
} classification_t;

int main(void)
{
    //
    // The ONLY change is to move the table from outside main to inside main.
    // error: initializer element is not constant { Mammals,    (int[]){Human, Elephant, Horse}, 3 },
    //
    static classification_t classifications[] = {
        { Mammals,    (int[]){Human, Elephant, Horse}, 3 },
        { Amphibians, (int[]){Frog, Salamander},       2 },
        { Avians,     (int[]){Eagle, Sparrow, Duck},   3 },
        { Fish,       (int[]){Salmon, Carp, Tuna},     3 }
    };

    for(int i=0; i<4; ++i)
    {
        printf("Category %d has %d examples\n", classifications[i].category, classifications[i].num_examples);
    }
    
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

我完全不清楚为什么数组初始化在第一个示例中起作用,但在第二个示例中不起作用。

是否有一种简单的技术可以使这个大表静态,但在函数作用域内声明?



正如一位评论者指出的那样,整个问题可以更直接地描述如下:
static int* foo = (int[]) { 3, 4, 5 };
// This is Valid; apparently, the anonymous array is a constant-pointer

int main()
{
  static int* foo = (int[]) { 3, 4, 5 };
  // This is NOT valid; the anonymous array is likely on the stack.

  static int* foo = (static int[]) { 3, 4, 5 };
  // This is NOT valid; the static keyword is invalid before int[]
  // But it was an attempt to make the array "static"
}
Run Code Online (Sandbox Code Playgroud)

有没有办法在函数中声明静态、匿名数组?

Vla*_*cow 6

区别在于用作静态对象初始值设定项的复合文字的存储持续时间。

\n

当您在文件范围内使用它们时,它们具有静态存储持续时间,并且可以用作常量表达式来初始化对象,

\n

当您在 main (块作用域)中使用它们时,它们具有自动存储持续时间,即它们不能用作常量表达式来初始化具有静态存储持续时间的对象。

\n

来自 C 标准)6.6 常量表达式)

\n
\n

2 常量表达式可以在翻译期间而不是运行时求值,因此可以在常量所在的任何地方使用。

\n

7初始化程序中的常量表达式允许有更多的自由度。\n这样的常量表达式应为以下之一,或计算为以下之一:\n

\n

//...

\n

\xe2\x80\x94一个地址常量,或者

\n

//...

\n
\n

\n
\n

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

\n
\n

请注意用作数组初始值设定项并具有数组类型的复合文字,例如

\n
(int[]){Human, Elephant, Horse}\n
Run Code Online (Sandbox Code Playgroud)\n

隐式转换为指向其第一个元素的指针。

\n


dbu*_*ush 5

您在函数中的声明中遇到错误,因为初始化程序在编译时未知。

C 标准第 6.5.2.5 节中有关复合文字的内容:

复合文字的值是由初始值设定项列表初始化的未命名对象的值。如果复合文字出现在函数体之外,则该对象具有静态存储持续时间;否则,它具有与封闭块关联的自动存储持续时间。

因此,当您的对象在文件范围内声明时,复合文字具有静态存储持续时间,因此它们的地址在编译时已知。但是,当您static在函数内部声明对象时,复合文字具有自动存储持续时间,因此不能用于初始化具有静态存储持续时间的对象。