什么时候应该使用动态内存分配函数而不是直接变量声明? [英] When should one use dynamic memory allocation function versus direct variable declaration?

查看:75
本文介绍了什么时候应该使用动态内存分配函数而不是直接变量声明?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

下面是直接变量声明的示例.

Below is an example of direct variable declaration.

double multiplyByTwo (double input) {
  double twice = input * 2.0;
  return twice;
}

下面是动态内存分配的示例.

Below is an example of dynamic memory allocation.

double *multiplyByTwo (double *input) {
  double *twice = malloc(sizeof(double));
  *twice = *input * 2.0;
  return twice;
}

如果可以选择的话,我会一直使用直接变量声明,因为代码看起来更具可读性.什么时候应该使用动态内存分配?

If I had a choice, I will use direct variable declaration all the time because the code looks more readable. When are circumstances when dynamic memory allocation is more suitable?

推荐答案

如果有选择,我将一直使用直接变量声明"

同样应该.除非需要,否则不要使用堆内存.显然有一个问题在问:什么时候需要动态内存?

As well you should. You don't use heap memory unless you need to. Which obviously begs the question: When do I need dynamic memory?

  • 堆栈空间有限,如果需要更多空间,则必须自己分配(请考虑大数组,例如struct huge_struct array[10000]).要了解堆栈有多大,请请参阅本页 .请注意,实际的堆栈大小可能有所不同.
  • C传递参数,并按值返回值.如果要返回一个衰减为指针的数组,最终将返回一个指向超出范围(无效)的数组的指针,从而导致UB.这样的函数应该分配内存并返回指向它的指针.
  • 当您需要更改某些内容的大小(realloc)时,或者您不知道需要存储多少内存时.您在堆栈上声明的数组的大小是固定的,可以重新分配指向内存块的指针(malloc新块> =当前块大小+ memcpy + free原始指针基本上是realloc做什么)
  • 当某个内存需要通过各种函数调用保持有效时.在某些情况下,全局变量不会(考虑线程).此外:在几乎所有情况下,全局变量都被视为不当行为.
  • 共享库通常使用堆内存.这是因为他们的作者无法假设他们的代码将拥有大量可用的堆栈空间.如果要编写共享库,可能会发现自己编写了很多内存管理代码
  • The stack space is limited, if you need more space, you'll have to allocate it yourself (think big arrays, like struct huge_struct array[10000]). To get an idea of how big the stack is see this page. Note that the actual stack size may differ.
  • C passes arguments, and returns values by value. If you want to return an array, which decays into a pointer, you'll end up returning a pointer to an array that is out of scope (invalid), resulting in UB. Functions like these should allocate memory and return a pointer to it.
  • When you need to change the size of something (realloc), or you don't know how much memory you'll need to store something. An array that you've declared on the stack is fixed in size, a pointer to a block of memory can be re-allocated (malloc new block >= current block size + memcpy + free original pointer is basically what realloc does)
  • When a certain piece of memory needs to remain valid over various function calls. In certain cases globals won't do (think threading). Besides: globals are in almost all cases regarded as bad practice.
  • Shared libs generally use heap memory. This is because their authors can't assume that their code will have tons of stack space readily available. If you want to write a shared library, you'll probably find yourself writing a lot of memory management code

因此,有一些例子需要澄清:

So, some examples to clarify:

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

需要在范围内保留更长时间的变量:

Variables that need to stay in scope for longer:

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将数据传递到该回调的方法.

In this scenario, our code is calling some library function that processes something asynchronously. We can pass a callback function that handles the results of the library-stuff. The lib also provides us with a means of passing some data to that callback through a 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的函数,带有两个参数:一个称为opaquevoid *和一个指向返回void并以int和void *作为参数的函数的函数指针.

So it's a function, called call_some_lib_func, that takes 2 arguments: a void * called opaque, and a function pointer to a function that returns void, and takes an int and a void * as arguments.

我们要做的就是将void *强制转换为正确的类型,然后我们就可以对其进行操作.另外请注意,some_func返回一个指向不透明指针的指针,因此我们可以在需要的地方使用它:

All we need to do is cast the void * to the correct type, and we can manipulate it. Also note that the some_func returns a pointer to the opaque pointer, so we can use it wherever we need to:

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

这篇关于什么时候应该使用动态内存分配函数而不是直接变量声明?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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