【问题标题】:Why can't the size of a static array be made variable?为什么静态数组的大小不能可变?
【发布时间】:2012-05-27 08:55:43
【问题描述】:

相关但不完全重复,因为它讨论了 C++:
can we give size of static array a variable

我在其中一个子文件中定义了一个数组,如下所示。

static int arr[siz];

这里siz 是子文件可用的全局变量。但是 gcc 编译器会产生以下错误:

<filename>: <line_num> : error : storage size of ‘arr’ isn’t constant

为什么我不能定义一个可变大小的static 数组?

编辑:这似乎只是static int 类型的问题。如果我将arr 的变量类型从static int 更改为int,即使数组的大小仍然依赖于变量siz,错误也会消失。

【问题讨论】:

    标签: c arrays gcc static


    【解决方案1】:

    您在编译时分配数组,因此编译器必须提前知道数组的大小。在声明 arr 之前,您必须将 siz 声明为常量表达式,例如:

    #define siz 5
    

    enum ESizes
    {
        siz = 5
    };
    

    或者,如果您需要在运行时确定它的大小,您可以使用malloc 在堆上分配它:

    static int* arr;
    arr = (int*)malloc(siz * sizeof(int))
    

    编辑:正如 eddieantonio 所说,我的回答对 C89 有效。在 C99 中,允许声明可变大小的数组。

    【讨论】:

    • @HeathRaftery 你是绝对正确的(我在写这篇文章时想到了 C++,忘记了问题是关于 C 的)。示例已删除。感谢您提请我注意。
    【解决方案2】:

    由于您声明的数组的大小不是恒定的,因此您拥有的是一个可变长度数组(VLA)。 C99 标准允许 VLA,但有一些限制。您不能拥有带有 staticextern 存储类说明符的可变长度数组。

    您的 VLA 具有 static 存储规范,但 C99 标准不允许这样做。

    参考:

    c99 标准:6.7.5.2/8

    示例 4 所有可变修改 (VM) 类型的声明必须在块范围内或 函数原型范围。 使用 static 或 extern 存储类说明符声明的数组对象不能具有可变长度数组 (VLA) 类型。但是,使用静态存储类说明符声明的对象可以具有 VM 类型(即,指向 VLA 类型的指针)。最后,使用 VM 类型声明的所有标识符都必须是普通标识符,因此不能是结构或联合的成员。

    因此,如果您想要一个带有static 存储说明符的动态大小数组,则必须使用在堆上分配的动态数组。

    #define MAX_SIZE 256
    static int* gArr;
    gArr = malloc(MAX_SIZE * sizeof(int));
    

    编辑:
    要回答您更新的问题:
    当您从声明中删除static 关键字时,声明数组的存储说明符从static 更改为全局,注意上面的标准引用,它明确提到了static 和@987654329 不允许使用VLA 的限制@ 存储规范。显然,您可以拥有具有全局存储规范的 VLA,这就是您删除 static 关键字后所拥有的。

    【讨论】:

    • 感谢您精心研究的答案。
    • 那么有效,如果我使用 gcc-4.3,VLA 不允许用于外部和静态类型变量?这也是用户 DCoder 在另一个回复的评论中回复的内容。
    • @Abhinav:是的,你是对的。 VLA 不允许用于具有 externstatic 类型存储规范的变量。您使用的是符合 c99 标准的 gcc-4.3。已更新答案以解决您编辑的查询。
    • “常量”的含义有些歧义,至少在我的编译器(gcc 6.3.0)中是这样。即使我执行const size_t siz = 3; 这显然不是变量并且可以在编译时进行评估,我仍然会收到static char arr[siz]; 的错误,这似乎相当有限。顺便说一句,就我而言,我试图在固定字符串上使用 sizeof() 以实现可维护性和自我记录。
    【解决方案3】:

    您不能声明可变大小的static 数组,因为它的空间分配在Data Segment(或bss segment,如果是未初始化的变量)。因此,编译器需要在编译时知道大小,如果大小不是常量,就会报错。

    其根本原因是数据段大小会影响生成的可执行文件的大小,这显然是在编译时创建的,因此必须固定。

    【讨论】:

      【解决方案4】:

      您不能定义任何可变大小的数组。那是因为arr[siz] 使编译器(!)为您的数组分配内存(好吧,编译器创建了一个程序,那个..,但我们不要误入歧途)。但是,变量可以在运行时更改(!),这意味着编译器没有机会知道要分配多少内存。

      你能做的是

      static int* arr;
      arr = (int*) calloc(siz,sizeof(int))
      

      这些行导致程序在运行时分配内存,因此它的确切大小也可以在运行时定义。

      【讨论】:

      • C99 permits Variable-Length Arrays,只要它们不是静态的,也不是外部的。 “所有可变修改 (VM) 类型的声明必须在块范围或函数原型范围内。使用 static 或 extern 存储类说明符声明的数组对象不能具有可变长度数组 (VLA) 类型。”
      • @DCoder 我正在使用 gcc 4.3。感谢您的建设性意见。
      • @paulsm4,你的语气只是冒犯,通常是一个友好的地方
      • @user1129682 抱歉,我不得不重新提出问题。请阅读编辑后的问题。
      • 基本问题始终是我在回答中提到的:在编译时不确定运行时的值将是什么以及这些值可能产生什么影响。就其本身而言,在一个简单、简单和天真的观点中,这就是为什么在编译时访问运行时定义的值会产生错误。简而言之:借助逻辑和编译器魔法,可以消除这种不确定性,编译器会使其工作。
      猜你喜欢
      • 2015-09-11
      • 1970-01-01
      • 1970-01-01
      • 2016-08-12
      • 2021-07-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多