我在C99中找到了可变长度数组,但看起来它的行为与malloc + free几乎相同。
我发现的实际差异:
太大的数组处理:
unsigned size = 4000000000;
int* ptr = malloc(size); // ptr is 0, program doesn't crash
int array[size]; // segmentation fault, program crashes
内存泄漏:只能在动态数组分配中使用:
int* ptr = malloc(size);
...
if(...)
return;
...
free(ptr);
对象的生命和从函数返回的可能性:动态分配数组直到内存释放,并且可以从分配内存的函数返回。
调整大小:仅使用指向已分配内存的指针调整大小。
我的问题是:
答案 0 :(得分:6)
一些实用的建议:
malloc()
及其朋友在堆上分配,这可能允许更大的分配。此外,您可以更好地控制该流程,因为如果malloc()
失败,NULL
可能会返回__STDC_NO_VLA__
。换句话说,你必须小心使用VLA,而不是用runtine来炸你的堆栈。malloc()
宏时不允许支持它们。根据我的经验(数学计划,如通过试验分部查找素数,Miller-Rabin等)我不会说VLA比malloc()
快。当然,malloc()
呼叫有一些开销,但更重要的是数据访问效率。
这是一些快速&使用GNU / Linux x86-64和GCC编译器进行脏比较。请注意,结果可能因平台或甚至编译器的版本而异。您可以使用数据访问prime-trial-gen.c
与VLA基准进行基本(虽然非常完成)。
#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
编译&amp;运行:
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;
}
$ 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
编译&amp; run(malloc版本):
$ 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
编译&amp;跑(VLA版):
π(10**7)
您可能check 664,579
确实是{{1}}。请注意,两个执行时间几乎相同。
答案 1 :(得分:2)
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时,要做的簿记工作较少。但是如果你需要处理大小不合适的数组,那么malloc()
仍会得分。如果您需要小心,可以将VLA与malloc()
等人一起使用 - 请参阅calloc()
for an array of array with negative index in C以获取示例。