Thursday, July 1, 2010

Memory Allocation Questions

1. Why doesn't this fragment work?
char *answer;
printf("Type something:\n");
gets(answer);
printf("You typed \"%s\"\n", answer);

A: 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.

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
char answer[100], *p;
printf("Type something:\n");
fgets(answer, 100, 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), so that the size of the array can be specified, so that fgets will not overwrite the end of the array 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, and/or to parameterize its size (#define ANSWERSIZE 100).

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.

A: 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.

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.


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?

A: 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 18 and 19.) Another common example is stat().

4. You can't use dynamically-allocated memory after you free it, can you?

A: No. Some early man pages 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.


5. How does free() know how many bytes to free?

A: 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.

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

A: 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.


7. 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?

A: 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 questions 1-14) or floating- point values. free can (and should) be used to free the memory allocated by calloc.


8. What is alloca and why is its use discouraged?

A: 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.

No comments:

Post a Comment