何时在C中使用可变长度数组,但是在动态分配时?

baz*_*rek 5 c arrays malloc free c99

我在C99中找到了可变长度数组,但看起来它的行为与malloc + free几乎相同.

我找到的实际差异:

  1. 太大的阵列处理:

    unsigned size = 4000000000;
    int* ptr = malloc(size); // ptr is 0, program doesn't crash
    int array[size]; // segmentation fault, program crashes
    
    Run Code Online (Sandbox Code Playgroud)
  2. 内存泄漏:只能在动态数组分配中使用:

    int* ptr = malloc(size);
    ...
    if(...)
        return;
    ...
    free(ptr);
    
    Run Code Online (Sandbox Code Playgroud)
  3. 对象的生命和从函数返回的可能性:动态分配的数组生命直到内存释放,并且可以从分配内存的函数返回.

  4. 调整大小:仅使用指向已分配内存的指针调整大小.

我的问题是:

  • 有什么更多的差异(我对实用建议感兴趣)?
  • 程序员可以使用两种具有可变长度的数组的方式有什么问题?
  • 何时选择VLA但是在动态阵列分配时?
  • 什么是更快:VLA或malloc +免费?

Grz*_*ski 6

一些实用的建议:

  • 实际上,VLA位于空间有限的堆栈上,而malloc()它的朋友在堆上分配,这可能允许更大的分配.此外,您可以更好地控制该流程,如果失败则malloc()可以返回NULL.换句话说,你必须要小心VLA不要在你的堆中打破你的堆.
  • 并非所有编译器都支持VLA,例如Visual Studio.此外,C11将它们标记为可选功能,并且在__STDC_NO_VLA__定义宏时不允许支持它们.

根据我的经验(数学计划,如试用分数,米勒 - 拉宾等寻找素数),我不会说VGA比任何更快malloc().malloc()当然有一些呼叫的开销,但似乎更重要的是数据访问效率.


下面是使用GNU/Linux x86-64和GCC编译器的一些快速和脏的比较.请注意,结果可能因平台或甚至编译器的版本而异.您可以使用数据访问与VLA基准测试作为一些基本(尽管非常完整)malloc().

prime-trial-gen.c:

#include <assert.h>
#include <stdbool.h>
#include <stdio.h>

bool isprime(int n);

int main(void)
{
    FILE *fp = fopen("primes.txt", "w");
    assert(fp);

    fprintf(fp, "%d\n", 2);
    for (int i = 3; i < 10000; i += 2)
        if (isprime(i))
            fprintf(fp, "%d\n", i);
    fclose(fp);
    return 0;
}

bool isprime(int n)
{
    if (n % 2 == 0)
        return false;
    for (int i = 3; i * i <= n; i += 2)
        if (n % i == 0)
            return false;
    return true;
}
Run Code Online (Sandbox Code Playgroud)

编译和运行:

$ gcc -std=c99 -pedantic -Wall -W prime-trial-gen.c
$ ./a.out
Run Code Online (Sandbox Code Playgroud)

然后这是第二个程序,它使用生成的"素数词典":

prime-trial-test.c:

#include <assert.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>

bool isprime(int n, int pre_prime[], int num_pre_primes);
int get_num_lines(FILE *fp);

int main(void)
{
    FILE *fp = fopen("primes.txt", "r");
    assert(fp);

    int num_lines = get_num_lines(fp);
    rewind(fp);

#if WANT_VLA
    int pre_prime[num_lines];
#else
    int *pre_prime = malloc(num_lines * sizeof *pre_prime);
    assert(pre_prime);
#endif

    for (int i = 0; i < num_lines; i++)
        assert(fscanf(fp, "%d", pre_prime + i));
    fclose(fp);

    /* NOTE: primes.txt holds primes <= 10 000 (10**4), thus we are safe upto 10**8 */
    int num_primes = 1; // 2
    for (int i = 3; i < 10 * 1000 * 1000; i += 2)
        if (isprime(i, pre_prime, num_lines))
            ++num_primes;
    printf("pi(10 000 000) = %d\n", num_primes);

#if !WANT_VLA
    free(pre_prime);
#endif
    return 0;
}

bool isprime(int n, int pre_prime[], int num_pre_primes)
{
    for (int i = 0; i < num_pre_primes && pre_prime[i] * pre_prime[i] <= n; ++i)
        if (n % pre_prime[i] == 0)
            return false;
    return true;
}

int get_num_lines(FILE *fp)
{
    int ch, c = 0;

    while ((ch = fgetc(fp)) != EOF)
        if (ch == '\n')
            ++c;
    return c;
}
Run Code Online (Sandbox Code Playgroud)

编译和运行(malloc版本):

$ gcc -O2 -std=c99 -pedantic -Wall -W prime-trial-test.c
$ time ./a.out
pi(10 000 000) = 664579

real    0m1.930s
user    0m1.903s
sys 0m0.013s
Run Code Online (Sandbox Code Playgroud)

编译和运行(VLA版):

$ gcc -DWANT_VLA=1 -O2 -std=c99 -pedantic -Wall -W prime-trial-test.c
ime ./a.out 
pi(10 000 000) = 664579

real    0m1.929s
user    0m1.907s
sys 0m0.007s
Run Code Online (Sandbox Code Playgroud)

你可能会检查 ?(10**7)确实664,579.请注意,两个执行时间几乎相同.