用于在初始化期间表示数组大小的有效和无效语法

时间:2018-02-26 03:35:27

标签: c arrays

我正在研究c阵列,并且对在初始化期间能够和不能用于表示阵列大小的内容感到困惑。

我是正确的假设

#define SIZE 5 

const int SIZE = 5; 

彼此根本不同吗?

他们有区别,一个让我感到困惑的特殊例子是

#define SIZE 5
int arr[SIZE] = {11, 22, 33, 44, 55}; 

是有效的语法,但

const int SIZE = 5;
int arr[SIZE] = {11, 22, 33, 44, 55};

是无效的语法。虽然有趣,但

const int SIZE = 5;
int arr[SIZE];

是有效的语法。

特定语法是有效还是无效的背后的逻辑是什么?

3 个答案:

答案 0 :(得分:11)

  

我是正确的假设

     

#define SIZE 5
  和
  const int SIZE = 5;

     

彼此根本不同吗?

是的,你是对的。

#define只是textual replacement。基本上,C预处理器在编译过程(处理阶段)中为您进行“查找和替换”。然而,const限定对象意味着“存储在此位置的东西不能改变” - 大致相当于说它是“只读”。

  

#define SIZE 5
     int arr [SIZE] = {11,22,33,44,55};

     

是有效的语法。

这完全等同于写作:

int arr[5] = {11, 22, 33, 44, 55}; 

自己。当您使用define时,编译器只会为您执行替换作业。

  

const int SIZE = 5;

     

int arr [SIZE] = {11,22,33,44,55};

     

是无效的语法。

它无效但原因可能不止一个。如果arr具有静态存储持续时间(即,对象arr在整个程序执行期间仍处于活动状态),则它无效。因为C要求具有静态存储持续时间的数组对象的大小为constant expression

  

如果size是一个整型常量表达式,并且元素类型具有已知的常量大小,则数组类型不是可变长度数组类型;否则,数组类型是可变长度数组类型。

因此,以下程序无效:

const int SIZE = 5;
int arr[SIZE] = {11, 22, 33, 44, 55};
int main(void)
{
}

因为SIZE不符合C中的“常量表达式”。请注意,这在C ++中完全有效,其中SIZE有资格作为常量表达式(这里两种语言不同)。< / p>

另一个原因是C标准不允许初始化可变长度数组。如果您在函数中有定义,例如:

  

要初始化的实体的类型应为未知大小的数组或不是可变长度数组类型的完整对象类型。

因此,如果您没有初始化程序,则它变为有效:

int main(void)
{
    const int SIZE = 5;
    int arr[SIZE];  /* This is OK */
}

同样,您也可以不使用const

int main(void)
{
    int SIZE = 5;
    int arr[SIZE]; /* Thi is OK too. */
}

在上述两个代码段中,arr只是variable-length array

  

虽然有趣,但

     

const int SIZE = 5;   int arr [SIZE];

     

是有效的语法。

只有在函数内部(如上所述)才有效 - 它是VLA。但是如果你使它具有静态存储持续时间,例如:

const int SIZE = 5;
int arr[SIZE]; /* arr has static storage duration just as
                 all objects defined at file scope */

int main(void)
{
}

它无效,因为如上所述,SIZE不是C中的"constant expression"

类似地,

int main(void)
{
    const int SIZE = 5;
    static int arr[SIZE]; /* arr has static storage duration */
}
尽管arr位于函数内,但

仍然无效,因为arr具有静态存储持续时间

但是,如果你有:

enum {SIZE = 5};
int arr[SIZE] = {11, 22, 33, 44, 55};
 int arr2[SIZE]; /* Valid without initializer too. */

int main()
{
}

它有效。为什么?因为C中的enum constants qualify as "constant expressions"

答案 1 :(得分:8)

标准解释了它6.7.9p3

  

要初始化的实体的类型应为未知大小的数组或不是可变长度数组类型的完整对象类型。

在第二种情况下,它是一个VLA,但在第一种情况下它不是。预处理后,它与int arr[5] = {..}相同。

在VLA情况下,编译器在定义VLA时并不知道它的大小,因此无法检查初始化程序的有效性。这就是为什么不允许这种初始化的原因。

顺便说一句,使用const并不意味着它是一个编译时间常数 - 它只是意味着它无法改变。

同样来自6.7.6.2p4,对于何时是VLA以及何时不是VLA有明确的区别: -

  

...如果大小是整型常量表达式且元素类型具有已知的常量大小,则数组类型不是可变长度数组类型;否则,数组类型是可变长度数组类型。

答案 2 :(得分:6)

#define SIZE 5定义SIZE 整数常量表达式。虽然const int SIZE = 5;SIZE定义为变量表达式,但不得修改其值。 const限定符不会使它成为整数常量表达式(在c ++中它会这样做)。

标准说

n1570-§6.7.6.2(p4):

  

[...]如果大小是整型常量表达式且元素类型具有已知的常量大小,则数组类型不是可变长度数组类型;否则,数组类型是可变长度数组类型。 [...]

当你这样做时

#define SIZE 5
int arr[SIZE] = {11, 22, 33, 44, 55}; 

它将arr声明为不是可变长度数组(VLA)的数组。而

const int SIZE = 5;
int arr[SIZE];

arr声明为可变长度数组,因为SIZE不是整数常量表达式,而是变量表达式。但是同样的声明因初始化列表而失败,那是因为对VLA有限制是因为它们无法使用列表初始化器进行初始化。

§6.7.9(第2页和第3页):

  

初始化程序不应尝试为未初始化的实体中包含的对象提供值。

     

要初始化的实体的类型应为未知大小的数组或完整对象类型,而不是可变长度数组类型