cpointerskernighan-and-ritchie

Why is it valid for the function to pass out a pointer in this K&R example?


I'm learning C by reading K&R (ANSI edition), supplemented with 21st Century C. I'd say I'm already pretty confident with most of the fundamentals of pointers. That means I know you have to be very careful passing pointers out of a function that weren't either passed into it in the first place or malloced. So this example has me stumped. It's from §5.6, page 109:

#define MAXLEN 1000 /* max length of any input line */
int getline(char *, int);
char *alloc(int);

/* readlines: readinputlines */
int readlines(char *lineptr[], int maxlines)
{
   int len, nlines;
   char *p, line[MAXLEN];

   nlines = 0;
   while ((len = getline(line, MAXLEN)) > 0)
      if (nlines >= maxlines || (p = alloc(len)) == NULL)
         return -1;
      else {
         line[len-1]='\0'; /* delete newline */
         strcpy(p, line);
         lineptr[nlines++] = p;
      }
   return nlines;
}

strcpy() was previously defined as:

/* strcpy: copy t to s; pointer version 3 */
void strcpy(char *s, char *t)
{
   while (*s++ = *t++)
      ;
}

I don't understand how the memory pointed to by p can remain in scope once the function returns. Here's what I do understand. The pointer p is declared within the function - so it's in automatic memory. As for what it's pointing to, it's not explicitly assigned any memory. Then when strcpy is called, values are copied from line to wherever p points to.

I've also reached the point in 21st Century C which discusses the dangers of pointers, and gives examples that attempting to pass an array declared in automatic memory out of a function is definitely not OK. (p. 109: "A pointer to a block of memory that has already been automatically freed is worse than useless.") So the only way I can understand the above code being valid is that by declaring p not as an array where a memory block is explicitly allocated in automatic memory, but rather as a pointer, its memory allocation is somehow implicitly handled in some other way. Is that correct? How does the memory allocation work?

Note: By reading 21st Century C I'm attempting to neutralise whatever bad practices K&R might introduce me to. I understand that it's not the best standard to go by, and probably I will never end up writing code in the way demonstrated in this example. But I would still like to understand it.


Solution

  • Indeed, the pointer p itself resides in automatic memory. However, it is assigned a memory block during the evaluation of the condition of the following if statement:

    if (nlines >= maxlines || (p = alloc(len)) == NULL)
    

    More specifically, if the first term of the condition is false (since the or logical operator is a short-circuit one), the second term is evaluated which performs a memory allocation and checks if that succeeded.

    As per my old knowledge of K&R, the alloc function is a simplified version of malloc, defined somewhere before you got to this example. However, the point of memory allocation functions, such as the standard malloc, exemplified here by alloc is to allow you to reserve memory on the heap. The heap is a memory region defined during the execution of the program which is not automatically managed, i.e. the compiler does not do any operations on it unless you explicitly tell it to do. Thus, you can return/pass by value the pointer p. The memory of the variable itself is not of much use; it's value is what matters. It points to an allocated memory region, which is going to stay there for as long as you want (until you explicitly free it). Just make sure you won't lose the memory address of this region before you free it.