[prev] [up] [overview] [next]

Section 3. Memory Allocation

3.1: Why doesn't this fragment work?

	char *answer;
	printf("Type something:\n");
	gets(answer);
	printf("You typed \"%s\"\n", answer);

The pointer variable "answer," which is handed to the gets function as the location into which the response should be stored, has not been set to point to any valid storage.  That is, we cannot say where the pointer "answer" points.  (Since local variables are not initialized, and typically contain garbage, it is not even guaranteed that "answer" starts out as a null pointer.  See question 17.1.)

The simplest way to correct the question-asking program is to use a local array, instead of a pointer, and let the compiler worry about allocation:

	#include <string.h>

	char answer[100], *p;
	printf("Type something:\n");
        fgets(answer, sizeof(answer), stdin);
	if((p = strchr(answer, '\n')) != NULL)
		*p = '\0';
	printf("You typed \"%s\"\n", answer);

Note that this example also uses fgets() instead of gets() (always a good idea; see question 11.6), allowing the size of the array to be specified, so that the end of the array will not be overwritten if the user types an overly-long line. (Unfortunately for this example, fgets() does not automatically delete the trailing \n, as gets() would.) It would also be possible to use malloc() to allocate the answer buffer.

3.2: I can't get strcat to work.  I tried


	char *s1 = "Hello, ";
	char *s2 = "world!";
	char *s3 = strcat(s1, s2);

but I got strange results.

Again, the problem is that space for the concatenated result is not properly allocated.  C does not provide an automatically- managed string type.  C compilers only allocate memory for objects explicitly mentioned in the source code (in the case of "strings," this includes character arrays and string literals). The programmer must arrange (explicitly) for sufficient space for the results of run-time operations such as string concatenation, typically by declaring arrays, or by calling malloc (See also question 17.20.)

strcat performs no allocation; the second string is appended to the first one, in place.  Therefore, one fix would be to declare the first string as an array with sufficient space:

	char s1[20] = "Hello, ";

Since strcat returns the value of its first argument (s1, in this case), the s3 variable is superfluous.

References: CT&P Sec. 3.2 p. 32.

3.3: But the man page for strcat says that it takes two char *'s as arguments.  How am I supposed to know to allocate things?

In general, when using pointers you always have to consider memory allocation, at least to make sure that the compiler is doing it for you.  If a library routine's documentation does not explicitly mention allocation, it is usually the caller's problem.

The Synopsis section at the top of a Unix-style man page can be misleading.  The code fragments presented there are closer to the function definition used by the call's implementor than the invocation used by the caller.  In particular, many routines which accept pointers (e.g. to structs or strings), are usually called with the address of some object (a struct, or an array -- see questions 2.3 and 2.4.)  Another common example is stat().

3.4: I have a function that is supposed to return a string, but when it returns to its caller, the returned string is garbage.

Make sure that the memory to which the function returns a pointer is correctly allocated.  The returned pointer should be to a statically-allocated buffer, or to a buffer passed in by the caller, or to memory obtained with malloc(), but not to a local (auto) array.  In other words, never do something like

        char *f()
        {
                char buf[10];
                /* ... */
                return buf;
        }
One fix (which is imperfect, especially if f() is called recursively, or if several of its return values are needed simultaneously) would to to declare the buffer as
                static char buf[10];
See also question 17.5.

3.5: Why does some code carefully cast the values returned by malloc to the pointer type being allocated?

Before ANSI/ISO Standard C introduced the void * generic pointer type, these casts were typically required to silence warnings about assignment between incompatible pointer types.  (Under ANSI/ISO Standard C, these casts are not required.)

3.6: You can't use dynamically-allocated memory after you free it, can you?

No.  Some early documentation for malloc stated that the contents of freed memory was "left undisturbed;" this ill-advised guarantee was never universal and is not required by ANSI.

Few programmers would use the contents of freed memory deliberately, but it is easy to do so accidentally.  Consider the following (correct) code for freeing a singly-linked list:

	struct list *listp, *nextp;
	for(listp = base; listp != NULL; listp = nextp) {
		nextp = listp->next;
		free((char *)listp);
	}

and notice what would happen if the more-obvious loop iteration expression listp = listp->next were used, without the temporary nextp pointer.

References: ANSI Rationale Sec. 4.10.3.2 p. 102; CT&P Sec. 7.10 p. 95.

3.7: How does free() know how many bytes to free?

The malloc/free package remembers the size of each block it allocates and returns, so it is not necessary to remind it of the size when freeing.

3.8: So can I query the malloc package to find out how big an allocated block is?

Not portably.

3.9: I'm allocating structures which contain pointers to other dynamically-allocated objects.  When I free a structure, do I have to free each subsidiary pointer first?

Yes.  In general, you must arrange that each pointer returned from malloc be individually passed to free exactly once (if it is freed at all).

3.10: I have a program which mallocs but then frees a lot of memory, but memory usage (as reported by ps) doesn't seem to go back down.

Most implementations of malloc/free do not return freed memory to the operating system (if there is one), but merely make it available for future malloc calls within the same process.

3.11: Must I free allocated memory before the program exits?

You shouldn't have to.  A real operating system definitively reclaims all memory when a program exits.  Nevertheless, some personal computers are said not to reliably recover memory, and all that can be inferred from the ANSI/ISO C Standard is that it is a "quality of implementation issue."

References: ANSI Sec. 4.10.3.2 .

3.12: Is it legal to pass a null pointer as the first argument to realloc() Why would you want to?

ANSI C sanctions this usage (and the related realloc(..., 0), which frees), but several earlier implementations do not support it, so it is not widely portable.  Passing an initially-null pointer to realloc can make it easier to write a self-starting incremental allocation algorithm.

References: ANSI Sec. 4.10.3.4 .

3.13: What is the difference between calloc and malloc Is it safe to use calloc's zero-fill guarantee for pointer and floating-point values?  Does free work on memory allocated with calloc or do you need a cfree?

calloc(m, n) is essentially equivalent to

	p = malloc(m * n);
	memset(p, 0, m * n);

The zero fill is all-bits-zero, and does not therefore guarantee useful zero values for pointers (see section 1 of this list) or floating-point values.  free can (and should) be used to free the memory allocated by calloc.

References: ANSI Secs. 4.10.3 to 4.10.3.2 .

3.14: What is alloca and why is its use discouraged?

alloca allocates memory which is automatically freed when the function which called alloca returns.  That is, memory allocated with alloca is local to a particular function's "stack frame" or context.

alloca cannot be written portably, and is difficult to implement on machines without a stack.  Its use is problematical (and the obvious implementation on a stack-based machine fails) when its return value is passed directly to another function, as in fgets(alloca(100), 100, stdin).

For these reasons, alloca cannot be used in programs which must be widely portable, no matter how useful it might be.

References: ANSI Rationale Sec. 4.10.3 p. 102.


[prev] [up] [overview] [next]