Returning Memory from a Function

Another common use of dynamic memory involves returning pointers to memory blocks from a function. This is especially important when you're working with external libraries written by someone else.

Without this technique, the only things you can return from a function to the calling code are simple scalar values, such as an integer, a floating-point number, or a character. You cannot return more than one value or more complex data structures, like arrays.

This is why you need dynamic memory if you want to return something other than a simple value: you can allocate a block of memory in the function using malloc(), fill the block with something useful, and return the pointer to the block to the main part of the application, which can then use the data and free the memory as soon as it is no longer needed. The following example will demonstrate this concept:

To allocate and return memory from a function

1.
Create a new file or project in your text editor or IDE.

2.
Type the standard beginning lines of code (Script 10.5):

/* function.c - Script 10.5 */
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

Script 10.5. This function allocates a block of memory and returns it. The calling code must then free the block.


Note the inclusion of the assert.h header file, which defines the assert() macro introduced a bit later.

3.
Add a function prototype to declare thefunction you will call from the main() routine:

char *get_name(void);

This definition states that the get_name() function takes no arguments (indicated by the void) and that it returnsa char pointer.

4.
Begin the main function:

int main (void) {

5.
Define a char pointer variable and initialize it with the NULL pointer value:

char *name = NULL;

A char pointer variable is the usual type to store addresses of text strings, which is what the get_name() function returns. Initializing pointer variables to NULL is a good practice that can help you catch errors in your program. If you do not initialize a pointer variable to NULL, it can initially point to an unpredictable location in memory.

6.
Call the get_name() function and store the address it returns in the pointer variable:

name = get_name();

As you'll see, the get_name() function returns a character pointer, whose value will be assigned to name.

7.
Check the return value to make sure that you did get a valid address:

assert(name != NULL);

assert() is a macro that makes it convenient to embed short checks into your program code. If the condition in the parentheses is not true, then the program will abort immediately with an error message (Figure 10.10). In English, this line of code says Confirm that name does not equal NULL.

Figure 10.10. The assert() macro is an easy way to report upon errors, although it's not very user-friendly.


This is not the best and most user-friendly way to handle an error condition, but it works well in short programs and doesn't require you to type a lot of code. It is best used to check for failures that are not likely to occur.

8.
Print the string returned by the function:

printf("The entered name was: %s.
 
", name);

9.
Return the memory block to the pool using the free() function:

free(name);
name = NULL;

10.
Complete the main function:

    getchar();
    getchar();
    return 0;
} /* End of main() function. */

11.
Begin the get_name() function:

char *get_name(void) {

This definition matches the function prototype.

12.
Define a character pointer variable and immediately initialize it with the address returned by a call to malloc():

char *input = malloc(100);

This will allocate space in memory for 100 consecutive char items starting at the address returned by malloc().

13.
Check the return value to make sure you did get a valid address:

if (input == NULL) {
    printf("Unable to allocate
 memory!
");
    return NULL;
}

If the allocation fails, then the get_name() function cannot continue. In such a case, the function has to abort and should communicate its failure to the calling code. It does this by returning the NULL pointer value, just as malloc() returned the NULL pointer value to get_name(). This is a very common way to indicate failure for functions that return an address. You will see this pattern a lot in C programs.

14.
Prompt the user for input and collect the input using scanf():

printf("Enter your name: ");
scanf("%99s", input);

As usual with prompt strings, there is no and therefore no line break, so the user's input appearson the same line as the prompt (Figure 10.11).

Figure 10.11. The user is prompted to enter his or her name.


scanf()'s pattern (%99s) says that it should store up to 99 characters entered by the user. scanf() stores these characters plus the trailing null byte in the memory block pointed to by the input pointer variable.

15.
Return the value of input:

return input;

The memory block is now filled with the user's input. This should be returned by the calling function. Keep in mind that the function is actually returning the starting address where input is stored, not the actual string value itself.

Note that there is no free() statement in this function. Instead, it is the calling code's responsibility to free the memory when it is finished with the contents of the block, which is the whole point of this example.

16.
Save the file as function.c, compile, and debug as necessary.

17.
Run the application (Figure 10.12).

Figure 10.12. The memory for the name was allocated in a subroutine and passed back to the caller.


✓ Tip

  • In this scenario where the malloc() and the free() calls are not in the same function, it is especially important to make sure that the block is indeed freed when it is no longer used. If you use a third-party library function you didn't write yourself, read its documentation carefully to make sure all dynamic memory blocks are properly accounted for.


Why You Shouldn't Return Pointers to Local Variables

At the end of Chapter 7, we discussed the concept of variable scope: functions have their own variables, which are only available within that function. These variables are called local variables. In Chapter 9, you saw that you can use pointers to alter a local variable's value in another function, which is one way to circumvent scope.

It should be clarified, however, that a function should never return pointers to a its own local variables. The only kind of pointer you may safely return from a function is the base address of a block of dynamically allocated memory.

For example, the following function is declared as returning a pointer to a character array (a string):

char *get_name(void) {
    char name[100];
    // Put some data into name.
    return name; // Wrong!
}

When the return statement of this function is reached, the name array has been filled with some useful data. The intention here is to return the array's base address to the calling code (the main part of the application that called this function) so that the main application can use the data.

The problem with this is that the array holding the data is a local variable that ceases to exist at the end of the function, right after the return statement is executed. The space in memory occupied by the array during the execution of the function will be reused for other purposes after the function returns. The address returned does still point to a valid location in memory, but there is no longer an array at that location.

The same is true for any kind of local variable. Pointers to local variables are only valid until a function stops executing.


..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset