【问题标题】:Defining the size of an array using a const int使用 const int 定义数组的大小
【发布时间】:2026-01-08 23:05:02
【问题描述】:

当我尝试运行它时,它给了我一个错误,说变量 a 中的值不是恒定的。这对我来说没有意义,因为我明确地将变量 a 设为常量。数组的大小是否必须比这更恒定?意思是,只有#define a 5,还是将其初始化为int arr[5],还是使用malloc?我做错了什么?

int main{

   const int a = 5;
   int i;
   int arr [a];

   for (i = 0; i < 5; i++) {
      arr[i] = i * 2;
   }

  printf("%d", arr[1]);
  return 0;
}

【问题讨论】:

  • 您认为可用的替代方案主要是#definemalloc 的假设是正确的——选择这两个选项之一来解决它。 l3x 的回答很好地解释了原因。

标签: c arrays size constants


【解决方案1】:

也许使用枚举来定义a 的值。

enum { a = 5 };
int arr [a];

也许这不是枚举的意图,但枚举的成员是 C 中最接近常量的东西。与使用 #define 定义所有内容的常见做法不同,a 的可见性受到范围和这里和arr一样。

【讨论】:

    【解决方案2】:

    在 C 中,const 应该被读作只读。它没有定义编译时间。

    const int a = 5;
    

    这里的a不是C standard要求的常量表达式:

    6.7.9 初始化
    4 具有静态或线程存储持续时间的对象的初始化程序中的所有表达式应为常量 表达式或字符串文字。

    所以错误表明您使用的是 C89/C90 编译器。您可以从用户那里读取a 的输入并声明variable length array,这是一个C99 功能,具有自动存储持续时间。

    使用#define 是另一种方式。但它只是一个文本替换,并定义了一个具有自动存储持续时间的数组。和自己定义int arr[5];一样。

    如果您想在动态存储(通常称为“堆”)上分配内存,则必须使用 malloc() 系列函数,它在程序执行期间将具有生命周期,直到您对其调用 free()

    (请注意,const 的这种行为仅在 C 中。C++ 在这方面有所不同,将按您的预期工作)。


    如果我在 C89 中编译代码,它会失败:

    #include <stdio.h>
    
    int main(){
    
       const int a = 5;
       int i;
       int arr [a];
    
       for (i = 0; i < 5; i++) {
          arr[i] = i * 2;
       }
    
      printf("%d", arr[1]);
      return 0;
    }
    
    $ gcc -Wall -Wextra -std=c89 -pedantic-errors test.c
    test.c: In function âmainâ:
    test.c:7:4: error: ISO C90 forbids variable length array âarrâ [-Wvla]
        int arr [a];
        ^
    

    因为 C89 不支持 VLA(尽管 gcc 即使在 C89/C90 中也支持 an extension)。因此,如果您使用的编译器不支持 C99,那么您就不能使用 VLA。 例如,Visual Studio 并不完全支持所有 C99 和 C11 功能。虽然,Visual studio 2015 support most C99 features,VLA 不是其中之一。

    但相同的代码在 C99 和 C11 中编译没有任何问题:

    $ gcc -Wall -Wextra -std=c99 -pedantic-errors t.c
    $ gcc -Wall -Wextra -std=c11 -pedantic-errors t.c
    

    这是因为 C99 中添加了可变长度数组 (VLA)。请注意,VLA 在 C11 标准中已成为可选。因此,实现可能不支持 C11 中的 VLA。 您需要针对 __STDC_NO_VLA__ 进行测试,以检查您的实现是否不支持 VLA。

    来自6.10.8.3 Conditional feature macros

    __STDC_NO_VLA__
    整数常量 1,意在表示实现不支持变长数组或变长 修改类型。

    我个人不使用 VLA,因为如果数组大小相当大,则无法便携地找到分配失败。例如

    size_t size = 8*1024;
    int arr[size];
    

    在上面的片段中,如果arr 分配失败,直到运行时你才会知道。内存分配取决于平台的“足够小”大小是多少。所以在一台机器上,1MB 分配可能会成功,而另一台机器可能会失败,更糟糕的是没有办法捕捉到这个失败。

    因此,VLA 的使用受到限制,并且只能用于您知道在给定平台上总是会成功的小型阵列。但我会简单地对数组大小进行硬编码并处理边界条件。

    【讨论】:

    • 或者,如果您真的想要硬编码数组大小,请使用名为 arraySize 或类似名称的 #define 指令。
    • 我不明白您为什么只对 VLA 做了一个简单的引用。这不是OP想要做的吗?如果她使用的是 MSVC,即使 2015 版本也不支持 VLA。她甚至在问题中提到了你的方法,所以很清楚。
    • @WeatherVane 你是对的,它需要更多解释。我做了更新。谢谢。
    【解决方案3】:

    const-qualified 变量与 常量表达式 不同;常量表达式的值在编译时已知,而 const-qualified 变量(通常)不知道(即使看起来应该如此)。

    请注意,在 C99 及更高版本中,可以声明 可变长度数组,其中数组大小直到运行时才知道。您必须使用 C99 或更高版本的编译器,并且鉴于该功能在 2011 标准中是可选的,您必须检查功能宏以查看 VLA 是否可用:

    static const int a = 10; // a is not a constant expression
    
    #if defined( __STDC__ ) && defined ( __STDC_VERSION__ ) && __STDC_VERSION__ >= 199901L && !defined( __STDC_NO_VLA__ ) 
    /**
     * VLAs are available in this environment
     */
    #define USE_VLA 1
    #endif
    
    #ifdef USE_VLA
       int arr[a];
    #else
      /**
       * VLAs are not available, either because it's a pre-1999 implementation,
       * or it's a post-2011 implementation that does not support optional
       * VLAs.  We'll have to use dynamic memory allocation here, meaning we'll
       * also need an explicit free call when we're done with arr 
       */
      int *arr = malloc( sizeof *arr * a );
    #endif
    ...
      do_something_interesting_with( a );
    ...
    #ifndef USE_VLA
      free( a );
    #endif
    

    至少直到最近,微软的 C 编译器还不支持 VLA。不过,他们一直在添加一些 C99 功能,例如混合声明和代码,因此最新版本可能支持 VLA。我不知道。

    【讨论】: