Why does Kernighan pg 106 work?

Tom Beres tom at rlgvax.UUCP
Fri Aug 12 00:43:58 AEST 1983


Another small lesson in C for those who would like it.  Others may skip.

The problem mentioned is the result of a subroutine which is supposed to:
(a) return a pointer to something (in this case, a char array); and
(b) allocate the space for that something itself.  The trick, then, is
how the subroutine allocates the space. Take the simplified example:

main() {
	char *p;
	if (something) {
		p = foo();
		printf("%s", p);
	}
}

char *foo() {
	int i;
	char buf[80+1];
	for (i = 0; i < 80; i++) {
		if ((buf[i]=getchar()) == EOF || buf[i] == '\n')
			break;
	}
	buf[i] = 0;
	return(buf);
}

Note what happens because buf[] is declared to be an automatic variable.
foo() returns the address of buf[], which is assigned to p.  But upon
exit from foo(), buf[] is de-allocated (i.e. it is popped off the stack),
so p points to de-allocated space!  As long as the de-allocated space is
not re-allocated and re-used, p will point to the desired data.  As soon
as the de-allocated space is re-used (usually the result of another
subroutine call), the data p points to will be trashed.  So, the printf()
in main() should NOT work, but it might, just out of sheer luck and the
good graces of the O/S.  This is the setup in the question that was posed.
What was seen as the difference between an IF and a WHILE probably was
the difference between 1 and several subroutine calls.  The de-allocated
space survived after one call, but eventually got clobbered after several.

So, in foo() change the declaration to:

	static char buf[80+1];

Now, buf[] will not be de-allocated upon exit from foo(), and the above
example will work fine.  However, if main() is changed to:

	main() {
		char *p[10];
		int i;
		for (i = 0; i < 10; i++)
			p[i] = foo();
		for (i=0; i < 10; i++)
			printf("%s", p[i]);
	}

Because buf[] is a static, it will be re-used every time foo() is called,
so p[0], p[1], ..., p[9] end up ALL pointing to the same space (buf[]),
and what will be there will be the characters from the last call to foo().
Not what we want.

The resolution for this (and the most general solution) is what K & R did.
They had their equivalent to foo() read the line, then call alloc() to
allocate new space for the string, copy the string to the new space,
and return the address of the new space.  Therefore on every call, new space
was allocated and there was no problem of re-use and reallocation of space. 

- Tom Beres
{seismo, allegra, brl-bmd, mcnc, we13}!rlgvax!tom



More information about the Comp.lang.c mailing list