4
#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.

13
  • 4
    Yes, you can do that. The issue is with returning pointers to things that don’t exist anymore, but what you malloc continues to exist until you free it. Another option is to take an output pointer as an argument so the memory management is left to the caller. Commented Aug 2, 2017 at 2:24
  • 2
    toupper(s[i]) has undefined behavior if s[i] is negative. You should cast it to unsigned char. Commented Aug 2, 2017 at 2:26
  • 6
    @cᴏʟᴅsᴘᴇᴇᴅ That seems like very bad advice. Commented Aug 2, 2017 at 2:28
  • 1
    @COLDSPEED. I'm confused. Are you saying "If a C program compiles then it can't have bugs or leaks?" Are you saying "You should always run your C program under a debugger"? Commented Aug 2, 2017 at 2:33
  • 1
    YunjinJang Curious: why (char*) cast in ret = (char*)malloc(sizeof(char) * length);? Why multiple by sizeof(char) which is 1? Who or what text suggested that? Commented Aug 2, 2017 at 2:39

2 Answers 2

6

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
Sign up to request clarification or add additional context in comments.

3 Comments

There is NO need to cast the return of malloc, it is unnecessary. See: Do I cast the result of malloc? and always validate the return of malloc (e.g. in main() (before printf and free) you need, at minimum, if (ret) {/* do stuff */} or if (!ret) { /* handle error, or exit */ }. Otherwise good answer and effort.
@DavidC.Rankin saying casting malloc is unnecessary is exactly what I meant. I'll edit to make it clearer. About the if(!ret), I mentioned that malloc can return NULL, but I didn't want to change the OP's code
Yes, it looked like a copy/paste issue from the original question, but it is always worth cleaning up so learning by the OP or many who may follow will occur ;)
4

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.

1 Comment

Thank you very much. I can understand some of the things I was not sure about. It has helped a lot.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.