When to use a variable length array in C, but when is dynamic allocation?

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

Practical differences I found:

  • Array processing too large:

    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 when allocating dynamic arrays:

    int* ptr = malloc(size);
    ...
    if(...)
        return;
    ...
    free(ptr);
    
  • Object life and the ability to return from a function: a dynamically allocated array lives until memory is freed and can be returned from a function that allocates memory.

  • Resizing: resizing is possible only with pointers to the allocated memory.

My questions:

  • The more differences (I’m interested in practical tips)?
  • What other problems can programmers with both ways of variable length arrays have?
  • VLA, ?
  • : VLA malloc + ?
+4
2

:

  • VLA , malloc() , , , . , malloc() NULL, . , VLA, runtine.
  • VLA, . Visual Studio. , C11 __STDC_NO_VLA__.

( , , - ..), , VLA , malloc(). , malloc(), , -, .


GNU/Linux x86-64 GCC. , . ( ) malloc() vs VLA.

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

:

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

, :

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

( 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

( 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

check π(10**7) 664,579. , .

+5

VLA , , ( ), :

int n = 4;
int m = 5;
int matrix[n][m];
// …code to initialize matrix…
another_func(n, m, matrix);
// No call to free()

:

void another_func(int n, int m, int matrix[n][m])
{
    int sum = 0;
    for (int i = 0; i < n; i++)
    {
        for (int j = 0; j < m; j++)
        {
            // …use matrix just like normal…
            sum += matrix[i][j];
        }
    }
    // …do something with sum…
}

, , malloc() VLA, , , .

int n = 4;
int m = 5;
int *matrix = malloc(sizeof(*matrix) * n * m);
// …code to initialize matrix…
another_func2(n, m, matrix);
free(matrix);

void another_func2(int n, int m, int *matrix)
{
    int sum = 0;
    for (int i = 0; i < n; i++)
    {
        for (int j = 0; j < m; j++)
        {
            // …do manual subscripting…
            sum += matrix[i * m + j];
        }
    }
    // …do something with sum…
}

int n = 4;
int m = 5;
int **matrix = malloc(sizeof(*matrix) * n);
for (int i = 0; i < n; i++)
    matrix[i] = malloc(sizeof(matrix[i] * m);
// …code to initialize matrix…
another_func2(n, m, matrix);
for (int i = 0; i < n; i++)
    free(matrix[i]);
free(matrix);

void another_func3(int n, int m, int **matrix)
{
    int sum = 0;
    for (int i = 0; i < n; i++)
    {
        for (int j = 0; j < m; j++)
        {
            // …use matrix 'just' like normal…
            // …but there is an extra pointer indirection hidden in this notation…
            sum += matrix[i][j];
        }
    }
    // …do something with sum…
}

:

int n = 4;
int m = 5;
int **matrix = malloc(sizeof(*matrix) * n);
int *values = malloc(sizeof(*values) * n * m);
for (int i = 0; i < n; i++)
    matrix[i] = &values[i * m];
// …code to initialize matrix…
another_func2(n, m, matrix);
free(values);
free(matrix);

VLA

, VLA. , malloc() - . VLA malloc() et al, - . calloc() C .

+2

All Articles