CERT's Secure Coding Standard包含一个项目(API05-C),它鼓励使用一致的数组参数,这是我在很多代码中实现的建议(隐藏在a macro后面不支持它们的编译器。)
对于那些不知道的人,一致的数组参数类似于:
void foo(int length, char data[length]);
API05-C提供了更多信息。
许多编译器不喜欢可变长度数组(有充分理由)。 C11将它们从必需(因为它们在C99中)降级为可选(如果它们没有实现,编译器应该定义__STDC_NO_VLA__
)。 MSVC扁平化不支持它们。 IAR将它们隐藏在一个开关(--vla
)后面。如果您(-Wvla
或-Werror=vla
询问错误,GCC和clang会向您发出警告)。
符合数组参数不会遇到与“普通”可变长度数组相同的问题;它们不会导致堆栈使用变量。他们只是告诉编译器现有数组(可能在堆栈或堆上)有多大。
我的问题是我所知道的每个编译器都将符合数组的参数视为VLA。这对于像MSVC这样的编译器来说并不是什么大问题,因为我可以将我的宏定义为空,但对于像GCC和clang这样的编译器我想使用一致的数组参数但不想触发-Wvla
诊断。
根据API05-C(重点补充):
因此,用作函数参数的数组声明可以具有作为变量或表达式的索引。 数组参数降级为指针,因此不是可变长度数组(VLA)。开发人员可以使用符合数组参数来指示数组的预期范围。编译器可以使用此信息,也可以忽略它。但是,这样的声明对开发人员很有用,因为它们用于记录数组大小和指针之间的关系。静态分析工具也可以使用此信息来诊断潜在缺陷。
我迫切希望这是真的,但我似乎无法找到C99或C11标准的相关部分。
那么,严格依据C99 / C11标准,是符合数组参数的VLA?或者,换句话说,将数组作为参数传递真的将它降级为指针吗?
显然,请引用规范的相关部分。
答案 0 :(得分:1)
声明为数组类型的所有参数都将转换为指针类型。 VLA也不例外。
N1256(C99 + TC1 + TC2 + TC3):
6.7.5.3函数声明符(包括原型)
7参数声明为“数组类型”应调整为“限定指向类型的指针”,其中类型限定符(如果有)是
[
和]
中指定的类型限定符数组类型推导。如果关键字static
也出现在数组类型派生的[
和]
内,那么对于每次调用该函数,相应实际参数的值应提供对第一个的访问权限。数组的元素,其元素至少与size表达式指定的元素一样多。
声明为void f(int a[10]);
的函数需要int *
。声明为void f(int length, int array[length]);
的函数需要int
和int *
。
但是,你写道:
我的问题是我所知道的每个编译器都将符合数组的参数视为VLA。这对于像MSVC这样的编译器来说并不是什么大问题,因为我可以将我的宏定义为空,但对于像GCC和clang这样的编译器我想使用一致的数组参数但不想触发
-Wvla
诊断。
嗯,这很棘手。它在转换为指针之前是一个VLA,而-Wvla
的点是警告无法在不支持VLA的编译器上编译的代码。如您所见,MSVC不喜欢该代码。
符合数组参数不会遇到与“普通”可变长度数组相同的问题;它们不会导致堆栈使用变量。他们只是告诉编译器现有数组(可能在堆栈或堆上)有多大。
不,他们不这样做。给定void f(int a[10]);
,使用空指针调用f
,使用指向长度为1的数组的指针等完全有效。编译器必须支持它。它们只是人类读者的暗示。这同样适用于转换的VLA。
答案 1 :(得分:0)
它们不是VLA,而只是指针。如果将该规则扩展到多个维度,则会获得可变修改类型VM,但仍不是VLA。
如果要指示指针不应为0,则
中有static
void Toto(size_t n, double vec [static n]);
表示vec
必须至少有n
个元素,特别是不能为空。