[comp.lang.c] An interesting behaviour in printf

Tim_CDC_Roberts@cup.portal.com (03/18/89)

In <960@Portia.Stanford.EDU>, joe@hanauma (Joe Dellinger) asks:

> What would you expect the following program to print out?
>
>
>	#include <stdio.h>
>	main()
>	{
>	char string[10];
>	string[0] = '*';
>	string[1] = '\0';
>	printf("%s\n", string[1]);
>	}
>
> Just "\n", right? On our system it prints out "(null)\n"!!!

No, I expect it to print out (null)\n.  The '%s' format item expects to
find a pointer_to_char on the stack.  By specifying string[1], you have
passed a _char_.  This _char_ happens to have the value 0.  When printf
goes to use this as a pointer, it finds that it is a null pointer.  To
do what you expected, replace the printf with:
        printf("%s\n", &string[1]);
                       ^

Trivia question: is the '(null)' output of printf standard or widespread?
I know that Microsoft C does this; do other compilers?

Tim_CDC_Roberts@cup.portal.com                | Control Data...
...!sun!portal!cup.portal.com!tim_cdc_roberts |   ...or it will control you.

ark@alice.UUCP (Andrew Koenig) (03/18/89)

In article <960@Portia.Stanford.EDU>, joe@hanauma (Joe Dellinger) writes:

> What would you expect the following program to print out?

> 	#include <stdio.h>
> 	main()
> 	{
> 	char string[10];
> 	string[0] = '*';
> 	string[1] = '\0';
> 	printf("%s\n", string[1]);
> 	}

The program is invalid; the implementation is allowed to
do as it pleases.

You said

	string[1] = '\0';

which is equivalent to

	string[1] = 0;

and then said

	printf("%s\n", string[1]);

which is, of course, equivalent to

	printf("%s\n", 0);

This asks printf to print characters starting at memory location 0;
not quite what you probably wanted.

Try this:

	printf("%s\n", &string[1]);

or, equivalently, this:

	printf("%s\n", string+1);
-- 
				--Andrew Koenig
				  ark@europa.att.com

bph@buengc.BU.EDU (Blair P. Houghton) (03/19/89)

In article <15938@cup.portal.com> Tim_CDC_Roberts@cup.portal.com writes:
>In <960@Portia.Stanford.EDU>, joe@hanauma (Joe Dellinger) asks:
>
>>	string[0] = '*';
>>	string[1] = '\0';
>>	printf("%s\n", string[1]);
>
>No, I expect it to print out (null)\n.  The '%s' format item expects to
>
>Trivia question: is the '(null)' output of printf standard or widespread?
>I know that Microsoft C does this; do other compilers?

The uVAX/Ultrix C compiler ignores it whole.  The only thing the program emits
is the \n.  (I checked it by "funkyprint | od -bc".  One char out.  \012.
More interesting than I expected.  Smells broken.)

The Encore/Umax C compiler emits \022 \012.  So, like, what's \022?  My
ASCII table says it's a "dc2".

				--Blair
				  "Gesundheit."

gwyn@smoke.BRL.MIL (Doug Gwyn ) (03/19/89)

In article <2343@buengc.BU.EDU> bph@buengc.bu.edu (Blair P. Houghton) writes:
>In article <15938@cup.portal.com> Tim_CDC_Roberts@cup.portal.com writes:
>>Trivia question: is the '(null)' output of printf standard or widespread?

AT&T UNIX System V Release 2.0 just printed out whatever was accidentally
pointed at.  In my version (BRL UNIX System V emulation for 4BSD), I
decided that the test for a null pointer was cheap insurance and I also
have *printf("%s",(char*)0) print "(null)" rather than behave randomly.
Such matters concern what X3J11 dubbed "quality of implementation" issues.

barmar@think.COM (Barry Margolin) (03/20/89)

In article <2343@buengc.BU.EDU> bph@buengc.bu.edu (Blair P. Houghton) writes:
>>In <960@Portia.Stanford.EDU>, joe@hanauma (Joe Dellinger) asks:
>>>	string[0] = '*';
>>>	string[1] = '\0';
>>>	printf("%s\n", string[1]);
>The Encore/Umax C compiler emits \022 \012.  So, like, what's \022?  My
>ASCII table says it's a "dc2".

In this case, the fact that it's DC2 is immaterial.  You passed a
pointer to location 0 to printf, so it printed the string that
happened to be at location 0 in memory, which appears to be
\022<Space>.

Barry Margolin
Thinking Machines Corp.

barmar@think.com
{uunet,harvard}!think!barmar

joe@hanauma.stanford.edu (Joe Dellinger) (03/20/89)

	Ooops. I realized my mistake about 1 minute after I
posted, but it seems our news server is hyperactive and had
already spread my errant article far and wide by then....

	My posting DOES raise an important question, though,
which I believe hasn't been addressed in any of the responses.
I ran my program through lint with no complaint. Why doesn't "lint"
catch this sort of error? And yes, I know, lint can't be expected
to know the internal details of weird varargs programs...

	BUT, printf is hardly "weird". It gets used ALL the time.
Why did the creators of lint not tackle error checking for all the
varargs programs in the standard libraries? Will "ANSI Lint" solve
this problem? If no, why not? It seems there is no better reason
than laziness...
\    /\    /\    /\/\/\/\/\/\/\.-.-.-.-.......___________
 \  /  \  /  \  /Dept of Geophysics, Stanford University \/\/\.-.-....___
  \/    \/    \/Joe Dellinger joe@hanauma.stanford.edu  apple!hanauma!joe\/\.-._

chris@mimsy.UUCP (Chris Torek) (03/21/89)

In article <1013@Portia.Stanford.EDU> joe@hanauma.stanford.edu
(Joe Dellinger) writes:
>I ran my [printf] program through lint with no complaint. Why doesn't
>"lint" catch this sort of error? And yes, I know, lint can't be expected
>to know the internal details of weird varargs programs...
>
>	BUT, printf is hardly "weird". It gets used ALL the time.
>Why did the creators of lint not tackle error checking for all the
>varargs programs in the standard libraries?

Because it was hard.

>Will "ANSI Lint" solve this problem? If no, why not? It seems there
>is no better reason than laziness...

Or disinterest.

Here is what the 4.3BSD-tahoe lint has to say:

	% cat foo.c
	main() { char foo[10]; printf("%s\n", foo[1]); }
	% lint -h foo.c
	foo.c(1): warning: printf: (char *) format, (char) arg (arg 2)

The change is due to Arthur Olson (elsie!ado).
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163)
Domain:	chris@mimsy.umd.edu	Path:	uunet!mimsy!chris

throopw@agarn.dg.com (Wayne A. Throop) (03/21/89)

> gwyn@smoke.BRL.MIL (Doug Gwyn )
> In my version (BRL UNIX System V emulation for 4BSD), I
> decided that the test for a null pointer was cheap insurance and I also
> have *printf("%s",(char*)0) print "(null)" rather than behave randomly.
> Such matters concern what X3J11 dubbed "quality of implementation" issues.

Uh... yeah.  They involve some taste issues also, so let me add a
tangential point.  I'd personally prefer not to overload an "in-band"
behavior to mean that something "out-of-band" happened.  The check for
the null pointer is cheap enough, I agree, but I'd druther not simply
print a string in that case... it's practically a liscense to write
non-portable code.  I'd druther do something close to

    kill( getpid(), SIGSEGV );

in such a case.
--
"Who would be fighting with the weather like this?"
        "Only a lunatic."
                "So you think D'Artagnian is involved?"
                        --- Porthos, Athos, and Aramis.
--
Wayne Throop      <the-known-world>!mcnc!rti!xyzzy!throopw

john@frog.UUCP (John Woods) (03/21/89)

In article <2343@buengc.BU.EDU>, bph@buengc.BU.EDU (Blair P. Houghton) writes:
> In article <15938@cup.portal.com> Tim_CDC_Roberts@cup.portal.com writes:
> >In <960@Portia.Stanford.EDU>, joe@hanauma (Joe Dellinger) asks:
> >>	string[0] = '*';
> >>	string[1] = '\0';
> >>	printf("%s\n", string[1]);
> >Trivia question: is the '(null)' output of printf standard or widespread?
> The uVAX/Ultrix C compiler ignores it whole.  The only thing the program emits
> is the \n.  (I checked it by "funkyprint | od -bc".  One char out.  \012.
> More interesting than I expected.  Smells broken.)
> The Encore/Umax C compiler emits \022 \012.  So, like, what's \022?  My
> ASCII table says it's a "dc2".

I'd bet dollars to holes in doughnuts that Ultrix's library (note: not the
compiler) behaves that way because there are four zero-bytes at address 0.
The first of them looks like an empty string ("").  Or maybe they check for
NULL and call it an empty string.

The program running on the Encore probably sees \022\040\000 at address 0.
A string with two characters, \022 and space.

Note that there are some systems that could potentially bus-error instead
of printing anything.  To quote Henry's Ten Commandments, "2.  Thou shalt
not follow the NULL pointer, for chaos and madness await thee at its end."
(a wonderful set of dicta for printing in Fraktur and posting on one's wall
:-)

UNOS' printf routine displays a NULL %s value as <NULL POINTER>, a bit more
verbose than (null) but equally useful.

-- 
John Woods, Charles River Data Systems, Framingham MA, (508) 626-1101
...!decvax!frog!john, john@frog.UUCP, ...!mit-eddie!jfw, jfw@eddie.mit.edu

"He should be put in stocks in Lafeyette Square across from the White House
 and pelted with dead cats."	- George F. Will

henry@utzoo.uucp (Henry Spencer) (03/22/89)

In article <1013@Portia.Stanford.EDU> joe@hanauma.stanford.edu (Joe Dellinger) writes:
>	... printf is hardly "weird". It gets used ALL the time.
>... Will "ANSI Lint" solve this problem? ...

Ask your compiler supplier.  ANSI C does not address details of the
implementation, such as what error messages are emitted by what program.
-- 
Welcome to Mars!  Your         |     Henry Spencer at U of Toronto Zoology
passport and visa, comrade?    | uunet!attcan!utzoo!henry henry@zoo.toronto.edu

jym@wheaties.ai.mit.edu (Jym Dyer) (03/22/89)

DEC's VAX C compiler (for VMS) crashes if you pass it a NULL for a string:

	printf("%s\n",NULL);

Alas, it also crashes on this:

	printf("%.*s\n",0,NULL);

Caveat emptor.
 <_Jym_>

gwyn@smoke.BRL.MIL (Doug Gwyn ) (03/22/89)

In article <4273@xyzzy.UUCP> throopw@agarn.dg.com (Wayne A. Throop) writes:
>I'd druther do something close to
>    kill( getpid(), SIGSEGV );
>in such a case.

I've had another similar suggestion mailed to me.

All I can say is, my experience with this has been that printing "(null)"
has not encouraged non-portable coding here (but then I try to stamp it
out anyway), and letting the application continue to run has usually been
more helpful than harmful.  Normally upon seeing "(null)" in output where
some meaningful string was expected, the software maintainer fixes the
null pointer bug and no damage was done.

rbutterworth@watmath.waterloo.edu (Ray Butterworth) (03/22/89)

In article <9891@smoke.BRL.MIL>, gwyn@smoke.BRL.MIL (Doug Gwyn ) writes:
> In article <4273@xyzzy.UUCP> throopw@agarn.dg.com (Wayne A. Throop) writes:
> >I'd druther do something close to
> >    kill( getpid(), SIGSEGV );
> >in such a case.
> 
> All I can say is, my experience with this has been that printing "(null)"
> has not encouraged non-portable coding here (but then I try to stamp it
> out anyway), and letting the application continue to run has usually been
> more helpful than harmful.  Normally upon seeing "(null)" in output where
> some meaningful string was expected, the software maintainer fixes the
> null pointer bug and no damage was done.

Unfortunately many people will rely on this feature and believe it
is actually part of the C language and library.  (Consider all the
postings to this group over the years that claim this fact.)

A better idea than "<null)" would be a somewhat more obnoxious string
such as: "a null character pointer was erroneously passed to printf".
It would achieve the effect that you want and the one that Wayne wants.

throopw@agarn.dg.com (Wayne A. Throop) (03/25/89)

> gwyn@smoke.BRL.MIL (Doug Gwyn )
>> throopw@dg-rtp.dg.com (Wayne A. Throop)
>> I'd druther do something close to
>>     kill( getpid(), SIGSEGV );
>> [..when a printf %s format is passed a null pointer..]
> All I can say is, my experience with this has been that printing "(null)"
> has not encouraged non-portable coding here (but then I try to stamp it
> out anyway), and letting the application continue to run has usually been
> more helpful than harmful.

Ah.  Good point: when a program is being developed, unexpected output
in one form is as good as that in another.  Adequate testing will
discover these kinds of thing.  But what I had in mind was that such
an implementation of the printf family shouldn't make it into
production code.  People tend to scan things superficially, and may
well miss such a thing as (null) in a column of entries of some sort
(especially if something like N/A or whatnot is a valid entry).  If
the users don't happen to note it, they won't ask to have it fixed.
Hence, I'd druther have a somewhat more attention-getting occurance
for this error.  Not that I claim Doug is "wrong"... these are largely
taste issues after all.  I just point out that his experience leading
to his decision to print (null) seems pointed towards the development
environment instead of the use environment.  But "I could be wrong,
you know!"

(Note that with adequate debugging support, there is nothing to
prevent continuing to the next error after raising the first...
raising it as an error rather than as an unexpected output just
"shouts louder" so to speak.)

--
If someone tells me I'm not really conscious, I don't marvel about
how clever he is to have figured that out... I say he's crazy.
          --- Searle (paraphrased) from an episode of PBS's "The Mind"
--
Wayne Throop      <the-known-world>!mcnc!rti!xyzzy!throopw