Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can I use "malloc" for local variables to return local variables?

Tags:

c

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>

char * upperCase(const char* s)
{
    char * ret = NULL;
    size_t length = strlen(s) + 1;
    ret = (char*)malloc(sizeof(char) * length);
    for (size_t i = 0; i < length; i++) {
        ret[i] = toupper(s[i]);
    }
    return ret;
}

int main()
{
    char* ret = NULL;
    char* input = "HelloWorld";
    ret = upperCase(input);
    printf("value = %s", ret);
    free(ret);
}

The above code takes a string as a parameter and copies the string. It then converts each element of the copied string to uppercase and returns it.

Compiler = GCC 6.2

If you use the malloc function, is it possible to return a local variable to terminate the function and release the memory? I do not have enough understanding of the scope that still has memory.

like image 497
YunjinJang Avatar asked Sep 13 '25 07:09

YunjinJang


2 Answers

Short answer: Yes, it is possible

Long answer: malloc(some_size) allocates some_size space and returns a pointer to the address of the start of the allocated chunk (or NULL upon failure). When doing ret = (char*)malloc(sizeof(char) * length); ret is assigned with said pointer that points to memory chunk of length chars (note that sizeof(char) == 1 so you can remove it).

The memory is yours until you free it, even after the function had returned, so, after the end of upperCase(...) execution, that memory still belongs to you. The only problem is, the pointer ret was allocated on the stack with (auto) local storage, meaning it will "die" when it's scope (in your case - the upperCase(...) function's scope), and thus, you will not know where that memory is, BUT since you return ret from the function, the value it holds (which is the address to your allocated memory) will pass on to main's ret, which basically means you're good to go.

One last thing I think I should emphasize is, returning local variables is done all the time. For example int increment_by_one(int x){int y = x + 1; return y;}. This is a very simple function that returns the value of local int y. Since the value returned is all we need (i.e. the value of x+1) it's ok that it (the value) was stored in a local variable. The same (sort of) goes for your case - since ret inside upperCase holds the address that was allocated ,it's ok for it to "die" after upperCase ends, since the value it holds (the address) is passed on.

char * upperCase(const char* s)
{
    char * ret = NULL;                             // local variable -> will die after upperCase ends
    size_t length = strlen(s) + 1;                 // local variable -> will die after upperCase ends
    ret = (char*)malloc(sizeof(char) * length);    // ret assigned with address to memory
    for (size_t i = 0; i < length; i++) {          // local variable -> will die after it's scope (the for loop) ends
        ret[i] = toupper(s[i]);
    }
    return ret;                                     // said address is returned to main (local variable ret now dies peacefully after fulfilling its duty) 
}

int main()
{
    char* ret = NULL;               // local variable -> will die after main ends
    char* input = "HelloWorld";     // local variable -> will die after main ends
    ret = upperCase(input);         // ret gets the address allocated in upperCase
    printf("value = %s", ret);
    free(ret);                      // address is freed
}

2 notes:

  1. There is no need casting malloc return value, meaning ret = (char*)malloc(sizeof(char) * length); should be ret = malloc(sizeof(char) * length);
  2. no need for sizeof(char) since it's 1, meaning you can shorten it further to ret = malloc(length);
  3. malloc can fail. In that case it will return NULL so after every ret = malloc(...); you should check the value like if(!ret){// handle error} or if(ret != NULL){// do something} and such
like image 87
CIsForCookies Avatar answered Sep 15 '25 22:09

CIsForCookies


Yes, you can.

The malloc memory is on the heap, so it won't release when upperCase end.

But variable char *ret is on the stack, it will be popped out when upperCase end. But the value of ret in upperCase() is copied via ret = upperCase(input) in main(). That is to say, you still obtain the address of the allocated memory, which is still on the heap. You can call free(ret) when finished the use of this memory.


Check What and where are the stack and heap? for details about stack & heap.

like image 20
delta Avatar answered Sep 15 '25 22:09

delta