采用可变长度数组的 sizeof ——这样做有什么好处吗?

Gri*_*tov 3 c arrays sizeof c89 variable-length-array

我正在编写一段遗留代码(没有测试)。我偶然发现了隐藏在几个宏中的部分。如果使用 GCC 的-Wvla.

\n

有问题的代码相当于在这个小程序中可以看到的代码:

\n
typedef struct entry {\n    unsigned index;\n    unsigned reserved;\n    unsigned value;\n} entry_t;\n\nint main(int argc, char **argv) {\n    long pa = 0;\n    long res = pa + sizeof(entry_t[10 - argc]);\n    return res;\n}\n
Run Code Online (Sandbox Code Playgroud)\n

编译时,会发出警告:

\n
$ gcc -g -Wvla repro-vla.c\nrepro-vla.c: In function \xe2\x80\x98main\xe2\x80\x99:\nrepro-vla.c:9:5: warning: ISO C90 forbids variable length array [-Wvla]\n    9 |     long res = pa + sizeof(entry_t[10 - argc]);\n      |     ^~~~\n\n
Run Code Online (Sandbox Code Playgroud)\n

罪魁祸首当然是这个表达式:sizeof(entry_t[10 - argc])。这里的语法有点混乱。我相信会创建一个用于10 - argc类型条目的临时匿名数组entry_t,然后获取其大小,然后丢弃该数组。

\n

我的问题是:

\n
    \n
  1. 我对现在编写的代码的理解是否正确?
  2. \n
  3. 这个表达方式与 有何不同sizeof(entry_t) * (10-argc)?两者计算相同的值,并且两者都没有采取任何措施来防止下溢(当 时argc >= 10)。第二个表达式不使用可变长度数组,因此不会生成警告,而且在我看来它也更容易理解。
  4. \n
\n

tst*_*isl 5

没有创建临时数组对象。

有一个常见的误解,认为 VLA 是关于在运行时定义长度的堆栈分配数组,这是一种更安全的形式alloca()

不。VLA 是用于打字而不是存储。下面这行是“VLA-ness”的精髓:

typedef int T[n];
Run Code Online (Sandbox Code Playgroud)

不是:

int A[n];
Run Code Online (Sandbox Code Playgroud)

请注意,VLA 类型声明不分配任何存储空间,并且可以使用而不用担心堆栈溢出。VLA 类型对于处理多维数组和表达函数参数中的访问范围非常有用,即void foo(int n, int arr[n]);。VLA 类型在 C11 中是可选的,但由于其实用性,它们在 C23 中将是强制的。

该表达式sizeof(entry_t[10 - argc]本质上与以下相同:

typedef entry_t _unnamed_type[10 - argc];
sizeof(_unnamed_type)
Run Code Online (Sandbox Code Playgroud)

那里没有创建 VLA 数组对象。我认为问题在于-Wvla标志本身。它-Wvla警告任何VLA 类型(而不是 VLA 对象)的声明过于热心,因为它还捕获了 VLA 类型的良好用法。有人请求向 clang添加-Wvla-stack-allocation警告,以捕获 VLA 的危险用法。另一种方法是使用 gcc -Wvla-larger-than=0,但效果不太好。