9

在支持可变长度数组之前,我会像这样动态分配它们:

int foo(size_t n)
{
    int *arr = malloc(n * sizeof int);
    if (!arr) return ENOMEM; /* not enough memory */
    .
    . else do stuff with arr[]
    .
    free(arr);
    return 0;
}

使用可变长度数组,我现在可以让它看起来更干净:

int bar(size_t n)
{
    int arr[n];
    .
    . do stuff with arr[]
    .
    return 0;
}

现在我没有“内存不足”检查。事实上,如果 n 太大,程序就会崩溃。

如果 n 太大,我如何优雅地从 bar(n) 中退出?

4

3 回答 3

12

这种情况与任何其他局部变量完全一样 - 像这样的声明:

int baz(void)
{
    int arr[100000];
    .
    . do stuff with arr[]
    .
    return 0;
}

有完全相同的问题。“解决方案”与以往一样 - 不要递归太深,不要分配具有自动存储持续时间的非常大的数据结构(继续malloc()用于这些情况)。“非常大”的值很大程度上取决于您的环境。

换句话说,int array[n];除非您知道它n有界到一个合理的值,否则不要声明,这样您会很乐意将最大大小的数组声明为普通的、非可变修改类型的数组。

(是的,这意味着经过可变修改的类型数组并不像它们最初出现的那样有用,因为与仅将数组声明为所需的最大大小相比,您获得的收益很少)。

于 2010-11-11T03:41:56.863 回答
6

您可以通过不使用它们来防止它们崩溃。:)

严肃地说,几乎没有安全的方法可以使用可变长度数组来让你的生活更轻松,除非你对大小有很强的限制。另一方面,您可以通过以下方式有条件地使用它们:

char vla_buf[n < 1000 ? n : 1];
char *buf = sizeof vla_buf < n ? malloc(n) : vla_buf;
if (!buf) goto error;
/* ... Do stuff with buf ... */
if (buf != vla_buf) free(buf);

虽然这看起来像是无用的痛苦,但它可以产生巨大的性能差异,尤其是在线程应用程序中,许多调用malloc可能free会导致锁争用。(这个技巧的一个显着的好处是,您可以通过简单地替换[n < 1000 ? n : 1]1000,例如用宏来支持没有 VLA 的旧编译器。)

VLA 可能有用的另一个模糊情况是在递归算法中,您知道所有递归级别所需的数组条目总数为n,其中n足够小,您确信它不会溢出堆栈,但在那里可以达到n递归级别和使用最多n元素的单个级别。在 C99 之前,在不占用n^2堆栈空间的情况下处理这种情况的唯一方法是使用malloc. 使用 VLA,您可以完全在堆栈上解决问题。

请记住,这些 VLA 真正有益的情况非常罕见。通常,VLA 只是一种欺骗自己内存管理很容易的方法,直到您被您创建的(微不足道的)漏洞所困扰。:-)

编辑:为了更好地解决 OP 的原始问题:

#define MAX_VLA 10000
int bar(size_t n)
{
    int arr[n <= MAX_VLA ? n : 1];
    if (sizeof arr/sizeof *arr < n) return ENOMEM;
    /* ... */
    return 0;
}
于 2010-11-11T03:53:13.147 回答
0

实际上,到处检查内存不足的情况非常昂贵。处理海量数据的企业方法是通过在单个早期检查点定义大小硬上限来限制数据大小,并在达到上限时快速而优雅地失败。

我刚才的建议是简单而愚蠢的。但它是每一个普通(非科学或特殊)产品总是做的。这是客户通常所期望的。

于 2010-11-11T05:23:57.660 回答