我正在处理一段遗留代码(没有测试)。我偶然发现了一个隐藏在几个宏中的部分。如果用GCC的-Wvla
编译,它会生成一个警告。
所讨论的代码与下面这个小程序中的代码相当:
typedef struct entry {
unsigned index;
unsigned reserved;
unsigned value;
} entry_t;
int main(int argc, char **argv) {
long pa = 0;
long res = pa + sizeof(entry_t[10 - argc]);
return res;
}
编译时,它会发出警告:
$ gcc -g -Wvla repro-vla.c
repro-vla.c: In function ‘main’:
repro-vla.c:9:5: warning: ISO C90 forbids variable length array [-Wvla]
9 | long res = pa + sizeof(entry_t[10 - argc]);
| ^~~~
罪魁祸首当然是这个表达式:sizeof(entry_t[10 - argc])
.这里的语法有点混乱,我相信为entry_t
类型的10 - argc
条目创建了一个临时匿名数组,然后取其大小,并丢弃该数组。
我的问题是:
1.我对现在编写的代码的理解是否正确?
1.这个表达式与sizeof(entry_t) * (10-argc)
有什么不同?两者计算相同的值,并且都没有做任何事情来防止下溢(当argc >= 10
时)。第二个表达式不使用可变长度数组,因此不会生成警告,在我看来,它也更容易理解。
3条答案
按热度按时间zc0qhyus1#
不创建临时数组对象。
有一种常见的误解,认为VLA是关于运行时定义的长度的堆栈分配数组,这是一种更安全的
alloca()
形式。不。VLA是关于键入而不是存储的。下面一行是“VLA特性”的本质:
非:
注意,VLA type 声明不分配任何存储空间,并且可以使用而不用担心堆栈溢出。VLA类型对于处理多维数组和在函数参数中表示访问范围(例如
void foo(int n, int arr[n]);
)非常有用。VLA类型在C11中是可选的,但由于其有用性,在C23中将是强制性的。表达式
sizeof(entry_t[10 - argc]
基本上与下式相同:这里没有创建VLA数组对象。我认为问题出在
-Wvla
标志本身。-Wvla
警告任何 *VLA类型 * 的声明(不是VLA对象)什么是过分的,因为它也捕捉VLA类型的良好用法。有一个request添加-Wvla-stack-allocation
警告到clang以捕捉VLA的危险用法。或者,可以使用gcc的-Wvla-larger-than=0
,但是它工作得不是很好。am46iovg2#
使用可变长度数组的sizeof-这样做有什么好处吗?
当
argc >= 10
时,sizeof(entry_t) * (10-argc)
具有定义的乘积 * 1。在这种情况下,sizeof(entry_t[10 - argc])
是 * 未定义的行为 *(UB),因为它不能满足"如果大小是不是整数常量表达式的表达式:......每次求值时,它的值都应该大于零。"C17dr § www.example.com 5(数组声明符)6.7.6.2 5 (Array declarators)接下来的代码步骤(转换为
long
,然后转换为int
)在sizeof(entry_t) * (10-argc)
大于LONG_MAX, INT_MAX
时具有其实现定义的行为。给定UB为
argc >= 10
,优化编译器可以假设argc < 10
,并且发射代码利用返回值将仅在窄范围内的优势(例如:[3*4 ... 3*4*9
])-因此只需要使用狭义的整数数学。没有太大的优势-但它就在那里。size_t
。htrmnn0y3#
在这个表达式
sizeof(entry_t[10 - argc])
中没有创建数组。它对表达式10 - argc
求值,并根据entry_t
的类型计算这样一个数组的大小。entry_t[10 - argc]
是一个类型说明符,而不是表达式。因此,例如,您可以不写这些表达式
sizeof(entry_t[10 - argc])
和sizeof(entry_t) * (10-argc)
产生相同的值,因为数组的大小等于数组中元素的数量乘以其元素的大小,假定10
大于argc
。关于VLA中的元素数量,C标准中有一个限制,即 “每次评估时,它的值都应大于零。"
请注意,在表达式
sizeof(entry_t) * (10-argc)
中,当argc
大于10
时,由于通常的算术转换,您可以获得size_t
类型的非常大的值。还有这条线
提出了为什么将无符号整数类型
size_t
的值赋给有符号整数类型long
的变量的问题。