问题描述
以下代码安全使用吗?我以某种方式认为这是不正确的方法。我知道更好的方法是malloc(),realloc()。但是我想了解这段代码在哪里带来最大的风险。由于编译器不知道分配大小,因此如何定义堆栈大小?
int array_call(int k)
{
int temp[k];
int = 0;
int i = 0;
for (i = 0;i<k;i++)
{
temp[i]=i;
}
return k;
}
int main()
{
int n;
scanf("%d",&n);
n=array_call(n);
}
解决方法
这是一个可变长度数组(VLA)的示例,其中数组大小直到运行时才知道。该功能在该语言的1999年修订版中引入,但在2011年修订版中变为可选。
因此,第一个风险是VLA不受普遍支持-此代码可能无法在某些平台上编译。在C2011及更高版本上,您将需要在尝试使用{@ 1}}功能宏之前先进行检查,例如
__STDC_NO_VLA__
第二个主要风险是VLA不能任意大-许多(如果不是大多数)实现尝试从堆栈中分配VLA空间,并且堆栈空间往往受到限制。如果#if defined( __STDC_NO_VLA__ )
int *temp = malloc( sizeof *temp * k );
#else
int temp[k];
#endif
/** do stuff with temp **/
#if defined( __STDC_NO_VLA__ )
free( temp );
太大 1 ,您可能会遇到某种不可恢复的运行时错误。
VLA不能在文件范围内或用k
关键字声明,也不能是static
或struct
类型的成员。
- “太大”的确切值取决于平台,编译器,源代码等。
以下代码安全使用吗?
否。
我以某种方式认为这是不正确的方式 去做。我知道更好的方法是malloc(),realloc()。但是我要 了解该代码在何处带来最大风险。
风险#1:您依赖的功能(可变长度数组)在C11和更高版本中是可选的,而在C90中不存在(至少不是标准化的)。只有在C99中,它才是标准和强制性的。您将遇到的许多C实现都将其实现,但是有一些值得注意的不是。您的代码将完全无法在后者上编译。
风险2:C没有指定自动VLA的实现方式,但是通常在堆栈上分配它们。在这样的实现中,通过提供太大的n
值很容易导致程序由于堆栈溢出而失败。通常,malloc()
&co。有(很多)更多的空间可用于动态分配,但是即使没有,您也至少可以检测到malloc()
故障并从中恢复。类似的情况也适用于n
被赋予负数的可能性。
风险3:您正在以敏感方式使用未经验证的用户输入。这是安全编程协议的主要缺陷。
因为编译器不知道 分配大小,如何定义堆栈大小?
您假设有一个堆栈,这就是VLA的去向。都不保证。在这两个假设都成立的实现中,也不需要任何特定的行为。但是,实际上,编译器很可能会在非VLA成员之上的堆栈顶部分配VLA成员,并简单地针对n
的值适当地调整堆栈指针。如果有多个VLA,则有些棘手,但完全可行。这是涉及的最少问题之一。
如果您使用的编译器不支持VLA(Variable Length Array s),则可以让该函数分配并返回给定大小的数组。
int *NewInitedArrayWithCount( unsigned int arrayCount )
{
int *array = (int *) malloc( arrayCount * sizeof( int ));
if ( array != NULL )
{
for ( int i = 0; i<arrayCount; i++ )
{
array[i] = (int) i;
}
}
return array;
}
int main()
{
unsigned int arrayCount;
scanf( "%u",&arrayCount );
int *array = NewInitedArrayWithCount( arrayCount );
// Do stuff
if ( array != NULL )
free( array );
return 0;
}