[英]When should one use dynamic memory allocation function versus direct variable declaration?
以下是直接变量声明的示例。
double multiplyByTwo (double input) {
double twice = input * 2.0;
return twice;
}
以下是动态内存分配的示例。
double *multiplyByTwo (double *input) {
double *twice = malloc(sizeof(double));
*twice = *input * 2.0;
return twice;
}
如果可以选择的话,我会一直使用直接变量声明,因为代码看起来更具可读性。 什么时候动态内存分配更合适?
什么时候动态内存分配更合适?
如果在编译时不知道分配大小,则需要使用动态内存分配。
除上述情况外,还有其他一些情况,例如
如果我们想要一个在运行时可调整大小的数据结构,则需要进行动态内存分配。
除非free()
d,否则动态分配的内存的生存期将保持有效。 有时,从函数调用返回某个变量的地址时会很方便,否则,使用auto
变量会超出范围。
通常,堆栈大小将受到适当限制。 如果要创建和使用大型数组,最好使用动态内存分配。 这将从堆中分配内存。
使用malloc进行动态内存分配会将内存放置在堆上,因此在离开函数时不会破坏内存。
稍后,您将需要手动释放内存。
直接声明落在堆栈上,并在离开函数时被删除。 在return语句上发生的事情是,在销毁变量之前先对其进行复制。
考虑以下示例:
在堆上
void createPeople():
struct person *p = makePerson();
addToOffice(p);
addToFamily(p);
VS. 在堆栈上
void createPeople():
struct person p = makePerson();
addToOffice(p);
addToFamily(p);
在第一种情况下,只会创建一个人并将其添加到办公室和家庭中。 现在,如果此人被删除,则在办公室和家庭中均无效,此外,如果更改了他的数据,则两者也都被更改了。
在第二种情况下,将为办公室和家庭创建此人的副本。 现在,您可能会在办公室中更改副本的数据,而家庭副本仍保持不变。
因此,基本上,如果您想让多方访问同一对象,则该对象应该在堆栈中。
当您打算将数据传输到本地范围之外(例如某个函数)时,需要动态分配内存。
另外,当您无法事先知道需要多少内存时(例如用户输入)。
最后,当您知道所需的内存量但它会使堆栈溢出时。 否则,由于可读性,运行时开销和安全性,您不应使用动态内存分配。
“如果有选择,我将一直使用直接变量声明”
您也应该。 除非需要,否则不要使用堆内存。 哪个显然引出了问题:什么时候需要动态内存?
struct huge_struct array[10000]
)。 要了解堆栈有多大, 请参阅本页 。 请注意,实际的堆栈大小可能有所不同。 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;
}
在这种情况下,我们的代码正在调用某些库函数,该函数异步处理某些内容。 我们可以传递一个回调函数来处理库文件的结果。 lib还为我们提供了一种通过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;
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.