问题
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?
回答1:
"If I had a choice, I will use direct variable declaration all the time"
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?
- 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 whatrealloc
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);
Returning "arrays"
//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;
}
Having to resize the object:
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;
}
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
will have a signature along the lines of:
void call_some_lib_func(void *, void (*)(int, void *))
Or in a more readable format:
void call_some_lib_func(void *opaque, void (*callback)(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.
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;
}
回答2:
When are circumstances when dynamic memory allocation is more suitable?
When the allocation size is not known at compile time, we need to use dynamic memory allocation.
Other than the above case, there are some other scenarios, like
If we want to have a data-structure which is re-sizeable at runtime, we need to go for dynamic memory allocation.
The lifetime of dynamically allocated memory remains valid unless it is
free()
d. At times, it comes handy when returning some address of a variable from a function call, which , otherwise, with anauto
variable, would have been out of scope.Usually the stack size would be moderately limited. If you want to create and use an huge array, it is better to use dynamic memory allocation. This will allocate the memory from heap.
回答3:
Dynamic memory allocation with malloc places the memory on the heap, so it is not destroyed when leaving the function.
At a later point you would need to manually free the memory.
Direct declaration lands on the stack and is deleted on leaving the function. What happens on the return statement is that a copy of the variable is made before it is destroyed.
Consider this example:
On heap
void createPeople():
struct person *p = makePerson();
addToOffice(p);
addToFamily(p);
Vs. on stack
void createPeople():
struct person p = makePerson();
addToOffice(p);
addToFamily(p);
In the first case only one person is created and added to office and family. Now if the person is deleted, it is invalidated in both office and family and moreover, if his data is changed, it is changed in both, too.
In the second case a copy of the person is created for the office and family. Now it can happen that you change data of the copy in office and the copy in family remains the same.
So basically if you want to give several parties access to the same object, it should be on the stack.
回答4:
Dynamic memory allocation is needed when you intend to transport data out of a local scope (for example of a function).
Also, when you can not know in advance how much memory you need (for example user input).
And finally, when you do know the amount of memory needed but it overflows the stack.
Otherwise, you should not use dynamic memory allocation because of readability, runtime overhead and safety.
来源:https://stackoverflow.com/questions/31313893/when-should-one-use-dynamic-memory-allocation-function-versus-direct-variable-de