“如果可以选择,我会一直使用直接变量声明”
你也应该这样做。除非需要,否则不要使用堆内存。这显然引出了一个问题:我什么时候需要动态内存?
- 堆栈空间有限,如果您需要更多空间,则必须自己分配(想想大数组,如
struct huge_struct array[10000])。了解堆栈有多大see this page。请注意,实际堆栈大小可能会有所不同。
- C 传递参数,并按值返回值。如果要返回一个衰减为指针的数组,最终将返回一个指向超出范围(无效)的数组的指针,从而导致 UB。像这样的函数应该分配内存并返回一个指向它的指针。
- 当您需要更改某些内容的大小时 (
realloc),或者您不知道需要多少内存来存储某些内容。您在堆栈上声明的数组大小固定,指向内存块的指针可以重新分配(malloc 新块 >= 当前块大小 + memcpy + free 原始指针基本上是realloc 做了什么)
- 当某块内存需要在各种函数调用中保持有效时。在某些情况下,全局变量不会这样做(想想线程)。此外:在几乎所有情况下,全局变量都被视为不好的做法。
- 共享库通常使用堆内存。这是因为他们的作者不能假设他们的代码将有大量可用的堆栈空间。如果你想写一个共享库,你可能会发现自己要写很多内存管理代码
所以,一些例子来澄清:
//perfectly fine
double sum(double a, double b)
{
return a + b;
}
//call:
double result = sum(double_a, double_b);
//or to reassign:
double_a = (double_a, double_b);
//valid, but silly
double *sum_into(double *target, double b)
{
if (target == NULL)
target = calloc(1, sizeof *target);
*target = b;
return target;
}
//call
sum_into(&double_a, double_b);//pass pointer to stack var
//or allocate new pointer, set to value double_b
double *double_a = sum_into(NULL, double_b);
//or pass double pointer (heap)
sum_into(ptr_a, double_b);
返回“数组”
//Illegal
double[] get_double_values(double *vals, double factor, size_t count)
{
double return_val[count];//VLA if C99
for (int i=0;i<count;++i)
return_val[i] = vals[i] * factor;
return return_val;
}
//valid
double *get_double_values(const double *vals, double factor, size_t count)
{
double *return_val = malloc(count * sizeof *return_val);
if (return_val == NULL)
exit( EXIT_FAILURE );
for (int i=0;i<count;++i)
return_val[i] = vals[i] * factor;
return return_val;
}
必须调整对象的大小:
double * double_vals = get_double_values(
my_array,
2,
sizeof my_array/ sizeof *my_array
);
//store the current size of double_vals here
size_t current_size = sizeof my_array/ sizeof *my_array;
//some code here
//then:
double_vals = realloc(
double_vals,
current_size + 1
);
if (double_vals == NULL)
exit( EXIT_FAILURE );
double_vals[current_size] = 0.0;
++current_size;
需要在范围内停留更长时间的变量:
struct callback_params * some_func( void )
{
struct callback_params *foo = malloc(sizeof *foo);//allocate memory
foo->lib_sum = 0;
call_some_lib_func(foo, callback_func);
}
void callback_func(int lib_param, void *opaque)
{
struct callback_params * foo = (struct callback_params *) opaque;
foo->lib_sum += lib_param;
}
在这种情况下,我们的代码正在调用一些异步处理某些内容的库函数。我们可以传递一个回调函数来处理库内容的结果。该库还为我们提供了一种通过void *opaque 将一些数据传递给该回调的方法。
call_some_lib_func 将有如下签名:
void call_some_lib_func(void *, void (*)(int, void *))
或者以更易读的格式:
void call_some_lib_func(void *opaque, void (*callback)(int, void *))
所以它是一个名为 call_some_lib_func 的函数,它接受 2 个参数:一个名为 opaque 的 void * 和一个指向返回 void 的函数的函数指针,并接受一个 int 和一个 void * 作为参数。
我们需要做的就是将void * 转换为正确的类型,然后我们就可以对其进行操作了。另请注意,some_func 返回一个指向不透明指针的指针,因此我们可以在需要的任何地方使用它:
int main ( void )
{
struct callback_params *params = some_func();
while (params->lib_sum < 100)
printf("Waiting for something: %d%%\r", params->lib_sum);
puts("Done!");
free(params);//free the memory, we're done with it
//do other stuff
return 0;
}