Question

On pg. 109 of K&R, we see:

void writelines(char *lineptr[], int nlines)
{
  while (nlines -- > 0) printf("%s\n", *lineptr++);
}

I'm confused about what *lineptr++ does exactly. From my understanding, printf requires a char pointer, so we provide that with *lineptr. Then we increment lineptr up to the next char pointer in the array? Isn't this illegal?

On page 99, K&R writes that "an array name is not a variable; constructions like a=pa [where a is an array, pa is a pointer to an array] and a++ are illegal."

Was it helpful?

Solution

The selected answer of Adam Rosenfield is wrong. The answer of coobird too. For that reason I down voted both answers.

Adam Markowitz interpretation of *lineptr++ is right, but he doesn't answer the main question whether this is legal C99 code. Only Tom Future does; unfortunately he doesn't explain *lineptr++. I granted them a point each.

So for short, lineptr is a variable and can be manipulated as a pointer. It is thus legal to increment the pointer.

lineptr is a pointer into a sequence of pointers to sequences of chars. In other words it is a pointer to the first string of a string array. According to the code we can assume that the strings are null ('\0') terminated char sequences. nlines is the number of strings in the array.

The while test expression is nlines-- > 0. nlines-- is a post decrement (because -- is on the right of the variable). It is thus executed after the test has been performed and regardless of the test result, so in any case.

So, if the nlines value given as argument was 0, the test is performed first and returns false; the instructions in the loop are not executed. Note that since nlines is decremented anyway, the value of nlines after the while loop will be -1.

If nlines == 1, the test will return true and nlines will be decremented; the instructions in the loop will be executed once. Note that while these instructions are executed the value of nlines is 0. When the test is performed again, we are back to the case when nlines == 0.

The printf instruction uses the *lineptr++ expression. It is a post increment of the pointer (++ is on the right of the variable). This means the expression is evaluated first and the increment is performed after its use. So on the first execution printf receives a copy of the first element of the string array, which is a pointer to the first char of the strings. The lineptr is incremented only after that. The next time the printf is to be executed, lineptr points on the second element and will be move to the third when the second string has been printed. This makes sense because we obviously want to print the first string. If Adam Rosenfield was right, the first string would have been skipped and at the end we would try to print the string beyond the last one which is obviously a bad thing to do.

So, the printf instruction is a concise form of the two following instructions

printf("%s\n", *lineptr);
++lineptr; // or lineptr++, which is equivalent but not as good. lineptr += 1; is ok too.

Note, as a rule of thumb, that when pre- and post-increment are equivalent in their action, the pre-increment is preferable for performance reasons. The compilers will take care to switch it for you. Well, most of the time. It is better to the the pre- operators yourself, whenever possible, so it is used always. The reason becomes more explicit once you implement a post- and pre- increment yourself in C++.

OTHER TIPS

Keep reading! At the very bottom of p. 99

As formal parameters in a function definition,

 char s[];

and

 char *s;

are equivalent; we prefer the latter because it says more explicitly that the parameter is a pointer.

I.e. you can never pass an array (which is not a variable) to a function. If you declare a function that looks like it takes an array, it really takes a pointer (which is a variable). This makes sense. It would be odd for a function argument not to be a variable -- it can have a different value every time you call the function so it sure ain't a constant!

lineptr is not really an array; it's a pointer to a pointer. Note that the postincrement operator ++ has higher precedence than the dereference operator *, so what happens in lineptr++ is this:

  1. lineptr gets incremented to point to the next element in the array
  2. The result of lineptr++ is the old value of lineptr, namely a pointer to the current element in the array
  3. *lineptr++ dereferenced that, so it is the value of the current element in the array

Thus, the while loop iterates over all of the elements in the array pointed to by lineptr (each element is a char*) and prints them out.

It might be easier to imagine the *lineptr++ as the following:

*(lineptr++)

There, the pointer to the array of char*s is moved to the next element of the array, that is, from lineptr[0], we move to lineptr[1]. (The increment of the pointer occurs after the dereferencing due to the postfix increment of the pointer.)

So basically, the following things happen:

  1. lineptr[0] (of type char*) is retrieved by deferencing.
  2. The pointer is incremented to the next element of the array. (By the postfix increment on pointer lineptr.)
  3. The pointer now points to lineptr[1], repeat the process from step 1 again.
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top