Relay-Version: version B 2.10 5/3/83; site utzoo.UUCP
Path: utzoo!sq!msb
From: msb@sq.UUCP
Newsgroups: comp.lang.c
Subject: Re: printf and variable length string format (asterisk)
Message-ID: <1987Nov27.024339.12253@sq.uucp>
Date: Fri, 27-Nov-87 02:43:39 EST
Article-I.D.: sq.1987Nov27.024339.12253
Posted: Fri Nov 27 02:43:39 1987
Date-Received: Sun, 29-Nov-87 15:24:26 EST
References: <692@zycad.UUCP>
Reply-To: msb@sq.UUCP (Mark Brader)
Organization: SoftQuad Inc., Toronto
Lines: 56
Checksum: 15011

There have been several followups to this one, but they have all missed
a significant point.

> I want to print out a string whose length I will not know ahead, and which
> is not null terminated.
> [I tried] ... printf("%*s\n", i, a);

Most people pointed out that he meant "%.*s".  However, according to the
ANSI Draft Standard, that usage is NOT portable.  The reason is that the
Draft requires that the argument a "shall be a pointer to a string".
And a string is defined as "an array of characters *terminated by a null
character*" [my emphasis].

This means that an implementation may, on encountering the above, cheerfully
go on reading characters past the end of a until it runs off the end of
memory (or the segment of memory) and aborts... no matter what the value
of i is.

Fully portable ways to do this would be:

[1]		{ int j; for (j = 0; j < i; ++j) putchar (a[j]); }
		putchar ('\n');

[2]		{ char *tmp = malloc (j+1); if (!tmp) abort();
		  strncpy (tmp, a, j); tmp[j] = 0;
		  printf ("%s\n", tmp); free (tmp); }

If you know a maximum size for a, you can avoid the malloc().

If a is known not to be const, there is also:

[3]		if (j) { char last = a[j-1]; a[j-1] = '\0';
			 printf ("%s%c", a, last); a[j-1] = last; }
		putchar ('\n');

It seems to me that both [1] and [2] are likely to be significantly slower
than the "%.*s" method, which I expect will work on most or all existing
implementations; and [3] is ugly and not always usable.  I can't think
of another reasonable approach, anyway.  (You can't use fwrite(), for
instance, because the Draft allows an implementation to distinguish text
streams from binary streams.)


Doug Gwyn pointed out the "string" requirement to me when I asked him by
email about the acceptability of printf ("%.1s", &char_variable) to print
a char unless it was a null.  I agreed that this was a minor convenience
at best.  But thinking about the above example, and what you'd have to do,
does make me wonder whether the Committee really considered this point
before deciding to restrict all forms of %s to "strings".  They might have
decided that C was so strongly oriented to null-terminated strings that
anyone who wants to use data structures like a is doing so at their own
risk; or it might just have slipped by.  You there, Doug?


Mark Brader, SoftQuad Inc., Toronto	      "Suspicion breeds confidence."
utzoo!sq!msb, msb@sq.com					  -- BRAZIL