什么时候在 C 中使用变长数组,但是什么时候动态分配呢? [英] When to use variable length array in C, but when a dynamic allocation?

查看:58
本文介绍了什么时候在 C 中使用变长数组,但是什么时候动态分配呢?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I find out about Variable Length Array in C99, but it looks like it behave almost the same as malloc + free.

我发现的实际差异:

  1. 处理太大的数组:

  1. Too big array handling:

unsigned size = 4000000000;
int* ptr = malloc(size); // ptr is 0, program doesn't crash
int array[size]; // segmentation fault, program crashes

  • 内存泄漏:仅在动态数组分配中可能发生:

  • Memory leaks: only possible in dynamic array allocation:

    int* ptr = malloc(size);
    ...
    if(...)
        return;
    ...
    free(ptr);
    

  • 对象的生命周期和从函数返回的可能性:动态分配的数组一直存在,直到内存被释放并且可以从分配内存的函数返回.

  • Life of object and possibility to return from function: dynamically allocated array lives until the memory is frees and can be returned from function which allocated the memory.

    调整大小:只能使用指向已分配内存的指针来调整大小.

    Resizing: resizing possible only with pointers to allocated memory.

    我的问题是:

    • 还有哪些不同之处(我对实用建议感兴趣)?
    • 程序员在使用两种变长数组方式时还会遇到什么问题?
    • 什么时候选择 VLA,什么时候选择动态数组分配?
    • 哪个更快:VLA 还是 malloc+free?

    推荐答案

    一些实用的建议:

    • VLA 实际上位于空间有限的堆栈上,而 malloc() 及其朋友在堆上分配,这可能允许更大的分配.此外,您对该过程有更多控制权,因为 malloc() 可能在失败时返回 NULL.换句话说,您必须小心使用 VLA,不要在 runtine 中炸毁您的堆栈.
    • 并非所有编译器都支持 VLA,例如视觉工作室.此外,C11 将它们标记为 为可选功能,并允许在 __STDC_NO_VLA__ 宏已定义.
    • VLAs are in practice located on the space-limited stack, while malloc() and its friends allocates on the heap, that is likely to allow bigger allocations. Moreveover you have more control on that process, as malloc() could return NULL if it fails. In other words you have to be careful with VLA not-to-blow your stack in runtine.
    • Not all compilers support VLA, e.g. Visual Studio. Moreover C11 marked them as optional feature and allows not to support them when __STDC_NO_VLA__ macro is defined.

    根据我的经验(数值程序,例如用试除法、Miller-Rabin 等求素数),我不会说 VLA 比 malloc() 快.malloc() 调用当然有一些开销,但似乎更重要的是数据访问效率.

    From my experience (numerical programs like finding prime numbers with trial division, Miller-Rabin etc.) I wouldn't say that VLAs are any faster than malloc(). There is some overhead of malloc() call of course, but what seems to be more important is data access efficiency.

    这里有一些快速&使用 GNU/Linux x86-64 和 GCC 编译器的脏比较.请注意,结果可能因平台而异,甚至因编译器的版本而异.您可能会使用一些基本的(虽然很远是完整的)数据访问 malloc() 与 VLA 基准.

    Here is some quick & dirty comparison using GNU/Linux x86-64 and GCC compiler. Note that results may vary from platform to another or even compiler's version. You might use as some basic (though very far of being complete) data-access malloc() vs VLA benchmark.

    #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;
    }
    

    编译&运行:

    $ gcc -std=c99 -pedantic -Wall -W prime-trial-gen.c
    $ ./a.out
    

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

    Then here is second program, that take use of generated "primes dictionary":

    #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;
    }
    

    编译&运行(malloc 版本):

    Compile & run (malloc version):

    $ 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
    

    编译&运行(VLA 版本):

    Compile & run (VLA version):

    $ 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
    

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

    As you might check π(10**7) is indeed 664,579. Notice that both execution times are almost the same.

    这篇关于什么时候在 C 中使用变长数组,但是什么时候动态分配呢?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

  • 查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆