C:恒定长度不允许我声明数组

C: Constant length doesn't allow me to declare array

当我尝试编译一个简单的代码来理解指针时遇到了这个烦人的问题。 基本上,错误发生在数组的声明中:

// Use of pointers

#include <stdio.h>

int main(void) {
    const int SIZE = 5;

    int grades[SIZE]={78, 80, 75, 82, 83};
    //memset( grades, 0, SIZE*sizeof(int) );
    double sum = 0.0;
    double *ptr_sum = &sum;
    int i;

    printf("My grades are: \n");
    for (i=0; i<SIZE; i++) {
        printf("%d\n",grades[i]);
        sum = sum + grades[i];
    }
    printf("\n\n");
    printf("My average is %.2f\n\n", sum/SIZE);

    printf("sum is at %p, or %lu and is %lf\n",
        ptr_sum, ptr_sum, *ptr_sum);
    printf("Grades are at %lu to %lu\n", grades, grades+5);
    return 0;
}

尝试编译后报错如下:

pointers.c:8:5: error: variable-sized object may not be initialized int grades[SIZE]={78, 80, 75, 82, 83};

如果我对数组的长度使用常量,为什么会这样?

如果我删除数组内容(包括括号),程序会编译。如果我将数组留空,它会再次失败。

该数组被认为是可变长度数组,因为它的大小不是编译时常量,const 变量不符合此条件。又因为是变长数组,不能初始化

您需要制作 SIZE 宏以便完成直接标记替换。

#define SIZE 5

在 C 中与 C++ 相反,在这样的数组声明中使用常量对象。

const int SIZE = 5;

int grades[SIZE]={78, 80, 75, 82, 83};

声明了一个可变长度数组,该数组可能未在其声明中初始化。

相反,您需要使用整数常量表达式。

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

6 An integer constant expression117) shall have integer type and shall only have operands that are integer constants, enumeration constants, character constants, sizeof expressions whose results are integer constants, and floating constants that are the immediate operands of casts. Cast operators in an integer constant expression shall only convert arithmetic types to integer types, except as part of an operand to the sizeof operator.

在此引用中,术语 "integer constants" 表示整数文字,而不是使用限定符 const.

声明的对象

您可以通过以下方式声明数组。

enum { SIZE = 5 };

int grades[SIZE]={78, 80, 75, 82, 83};

//...

或者您也可以引入一个宏,例如。

#define SIZE 5

//...

int grades[SIZE]={78, 80, 75, 82, 83};

//...

或者您可以首先声明数组而不指定其大小,然后引入指定其大小的常量,例如

int grades[]={78, 80, 75, 82, 83};
const size_t SIZE = sizeof( grades ) / sizeof( *grades );

还有printf的这些调用。

printf("sum is at %p, or %lu and is %lf\n",
    ptr_sum, ptr_sum, *ptr_sum);

printf("Grades are at %lu to %lu\n", grades, grades+5);

不正确。您正在尝试使用转换说明符 lu 输出指针。您需要使用转换说明符 p.
例如

printf("Grades are at %p to %p\n", ( void * )grades, ( void * )( grades+SIZE ));

const 并不意味着“常数”。诚然,这有点令人困惑,因为关键字 const 显然源自英文单词“constant”,但它们意味着两个不同的东西。

在C中,const表示read-only。如果一个对象(变量)是用 const 关键字定义的,这意味着它的值在初始化后不能合法地改变。如果将 SIZE 用作表达式,则它不是 constant 表达式。所以

int grades[SIZE];

是一个variable-length数组。典型的编译器可能会生成与 fixed-length 数组等效的代码,但语言规则仍然适用,包括表示 variable-length 数组无法初始化的代码。

一个常量表达式是一个必须在编译时求值的表达式,例如422+2.

请注意,例如,此声明是完全合法的:

const int r = rand();

即使在程序执行之前无法确定 rand() 的值(假设 PRNG 已使用某些 run-time 值播种)。使初始化程序成为常量表达式不会改变行为。

(C++ 有一个 special-case 规则,即 const int SIZE = 5; 确实使 SIZE 成为一个常量表达式。这很方便,但在我看来它有点不幸。C++ 较新的 constexpr 是做同样事情的更好方法。但当然 C 不是 C++。)

不幸的是,C 没有很好的方法来定义任意类型的命名常量。最常见的方法是使用预处理器宏:

#define SIZE 5

但是宏名称的范围不像普通标识符那样。仅适用于类型 int 的解决方案是:

enum { SIZE = 5 };

其中任何一个都可以让您声明:

int grades[SIZE]={78, 80, 75, 82, 83};