简体   繁体   中英

When writing a function that returns pointer, why should I allocate memory to the pointer I'm going to return?

I'm a bit weak when it comes to memory allocation and pointers. So, I want to understand why do I have to allocate memory to pointers in functions as follow:

char *cstring(char c, int n)
{
int i = 0;
char * res;
res = malloc ((n+1)*sizeof(char));
while (i<n)
{
    res[i]=c;
    i++;
}
res[i] ='\0';
return res;
}

and why is the following not valid?

char *cstring(char c, int n)
{
int i = 0;
char * res;
while (i<n)
{
    res[i]=c;
    i++;
}
res[i] ='\0';
return res;
}

I understand that I should allocate memory (generally) to pointers so that they have defined memory. However, I want to mainly understand how is it related to the concept of stack and heap memories! Thanks in advance!

Pointers need to point to a valid memory location before they can be dereferenced.

In your first example, res is made to point at a block of allocated memory which can subsequently be written to and read from.

In your second example, res remains uninitialized when you attempt to dereference it. This causes undefined behavior . The most likely outcome in this case is that whatever garbage value it happens to contain will not be a valid memory address, so when you attempt to dereference that invalid address your program will crash.

The second version of your code declares a pointer, but does not initialize it to point to a valid memory address.

Then, the code dereferences that pointer -- during the loop. So, your code would access uninitialized memory in this case. Remember, array indexing is just syntactic sugar for dereferencing -- so your code accesses memory its not supposed to.


The first version of your code initializes the pointer to actually point to something, and hence when you dereference it during the loop, it works.

Of course, in either case, you return the pointer from the function -- its just that in the first version it points to something valid, whereas in the second version it points anywhere .


The moral here is to always initialize your variables. Not doing so could result in undefined behavior in your code (even if it appears to work sometimes). The general advice here is to always compile your code using at least some compilation flags. For example in gcc / clang , consider -Wall -Werror -Wextra . Such options often pick up on simple cases of not initializing variables.

Also, valgrind is a brilliant tool for memory profiling. It can easily detect uses of uninitialized memory at runtime, and also memory leaks.

If you declare a variable like that,

int A = 5;

then that means the variable will be on the stack . When functions are called, their local variables are pushed to the stack. The main function is also an example of that. So you don't have to allocate memory manually, your compiler will do this for you in the background before it calls your main function. And that also means if you examine the stack during the execution of the function you can see the value 5.

With this,

int  A = 5;
int *PtrToA = &A;

The pointer will be on the stack again. But this time, the value on the stack just shows the memory address of the actual integer value we want. It points to the address of the memory block that holds the value 5. Since A is held in the stack here, pointer will show a memory address on the stack. Like the case in your question you can allocate memory dynamically. But you have to initialize it before you read it. Because when you request to allocate the memory, your operating system searches for a valid memory field in your programs heap and reserves that for you. Than it gives you back its adddress and gives you the read write permissions so you can use it. But the values in it won't contain what you want. When compiler allocates on stack, the initial values will be unset again. If you do this,

char *res;
res[1] = 3;

variable res will be on the stack and it will contain some random value. So accessing it is just like that,

(rand())[1] = 3;

You can get an access violation error because you may not have permission to write to that memory location.

An important note; after your function call returns, values of local variables on the stack are no more valid. So be careful with that. Do not dereference them after the function call ends.

In conclusion; if you want to use a pointer, be sure it points to a valid memory location. You can allocate it yourself or make it point another memory address.

Simple: because you do not have any allocated memory for the data you wite. In your example you define pointer, you do not initialize it so it will reference random (or rather not possible to predict) place in the memory, then you try to write to this random memory location.

You have 2 Undefined Behaviours here in 5 lines example. Your pointer is not initialized, and you did not allocate any valid memory this pointer to reference.

EDIT:

VALID

char *cstring(char c, int n)
{
    char * res;
    res = malloc ((n+1)*sizeof(char));
char *cstring(char c, int n)
{
    char * res;
    static char buff[somesize];
    res = buff;
char buff[somesize];
char *cstring(char c, int n)
{
    char * res;
    res = buff;

INVALID

char *cstring(char c, int n)
{
    char * res;
    char buff[somesize];
    res = buff;

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM