是否可以在C函数调用中定义任何大小的数组?

问题描述

以下代码安全使用吗?我以某种方式认为这是不正确的方法。我知道更好的方法是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关键字声明,也不能是staticstruct类型的成员。


  1. “太大”的确切值取决于平台,编译器,源代码等。
,

以下代码安全使用吗?

否。

我以某种方式认为这是不正确的方式 去做。我知道更好的方法是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;
}