printf

Chris Torek chris at mimsy.umd.edu
Sun Oct 22 00:59:30 AEST 1989


In article <543 at uwm.edu> zhao at csd4.csd.uwm.edu (T.C. Zhao) writes:
>I recently came across a piece of c code:(both a and b are integers)
>printf("%d"+(a),b);
>in passes compiler without any problem, what does this code mean ?

First, learn the language definition; then apply it.

This is a function call to the `printf' function; it passes two arguments.
The first argument (which is not necessarily evaluated first) is

	"%d" + (a)

which is composed of three parts.  The first part is a string constant.
A string constant is an anonymous array of type `array N of char',
where N is the number of characters in the string, plus one for a
terminating NUL character (code 0).  Here "%d" becomes the triple:

	<object, array 3 of char, `%d\0'>

The third part of this expression is the sub-expression (a).  In between
these two parts is the `+' operator, which performs either scalar
(integer or floating point) addition or pointer addition, depending on
its arguments (the other two parts of the expression).  These arguments
are evaluated, meaning they are in `rvalue contexts'.

Now we need to refer to the rule for handling array objects in rvalue
contexts.  The rule is:

   An object of type `array N of T' in an rvalue context is converted
   to a value of type `pointer to T' whose value is the address of the
   first element of that object.

Thus, the value of

	<object, array 3 of char, `%d\0'>

as seen by the `+' operator is

	<value, pointer to char, points to `%' in `%d\0'>

The value of `(a)' is simply the value of the variable `a', which we
were told above is an `integer' (presumably an |int|, not a |long|, nor
|unsigned int| nor |unsigned long| nor any variety of |short| or
|char|).  Hence the arguments to the `+' operator are a pointer type
and an integral type, and so `+' performs pointer addition.

Pointer addition is done by moving forward some number of objects,
the number being determined by the integral expression and the objects
being determined by the pointer expression.  Here the pointer points
to |char|s, and the integral expression is the value of `a'.  We do
not know the value of `a', so we cannot predict the result of the
pointer addition.  The first part of our answer, then, is that we have
no idea what value is being passed to printf() as its first argument.
All we know is that it has type `pointer to char'.

Returning to the printf() call, the second argument is the expression
`b'.  This is also in an rvalue context and so its value is the value
of `b'.  All we know about `b' is that it is an `integer', again presumably
really an |int|, not a |short| or an |unsigned long| or some other
type; but we do not really know that.  The second part of our answer,
then, is that we have no idea what value is being passed to printf()
as its second argument.  All we know is that it has one of the types
|int|, |long|, |unsigned int|, or |unsigned long|; probably it is
|int|.

Finally, we put these together with knowledge about printf().  Printf
expects its first argument to have type |pointer to char| (it does) and
its remaining arguments, if any, are variadic (can be any rvalue type),
but must match up with types that can be derived by examining the value
of the first argument.  In this case, we have no idea what that value
might be.  All we can conclude, then, is that printf() is being called
with at least one correct type, and possibly two correct types.

The answer to the question:
>what does this code mean ?
is thus `we cannot tell'.  We can, however, make a guess or two
at the intended meaning.

The value passed to printf must be a pointer that points to at least
one character.  If that character is not NUL, it must be followed by
at least one more character, and so on, until we reach a character
that *is* NUL.  The result of "%d"+(a) will not be a valid pointer
to such a sequence of characters unless (a) has one of the three
values 0, 1, or 2.  These value causes the pointer addition
`+' operator to evaluate to

	<value, pointer to char, points to `%' in `%d\0'>
	<value, pointer to char, points to `d' in `%d\0'>
and	<value, pointer to char, points to `\0' in `%d\0'>

respectively.  If `a' is 0, the expression reduces to

	printf("%d", b);

which is correct (and prints to stdout the value of `b') iff `b'
evaluates to an rvalue of type |int|.  If `a' is 1, the expression
reduces to

	printf("%d"+1, b);

which acts like

	printf("d", b);

which is correct regardless of the type of `b' (at least according
to my draft of the proposed ANSI standard: excess arguments to printf
are allowed and ignored).  This prints to stdout the letter `d'.
Finally, if `a' is 2, the expression reduces to

	printf("%d"+2, b);

which acts like

	printf("", b);

which is also always correct (as per above) and prints nothing at all.

We can also conclude that if `a' has a value other than 0, 1, or 2,
that the printf call is incorrect and the result is not predictable.
(The result of "%d"+3 is legal but is not a pointer to one or more
characters, hence the printf() can fail; and the result for other
values is undefined, such pointer addition being illegal.)

So our second answer to
>what does this code mean ?
is `either nothing at all, it being incorrect, if a is not 0, 1, or 2;
or print the value of b; or print the letter d; or print nothing at
all, if a is 0, 1, or 2 respectively.'

Both answers depend upon some hidden assumptions, such as `the program
contains the line ``#include <stdio.h>'' before the call to printf' and
`a and b have values stored in them before the call to printf'.  Without
more information it is impossible to verify these assumptions.

There are two lessons here:

   If you want to know what some code means, get a good book about C
   and apply it.

and

   If you want someone to explain what some code does, you must be
   very explicit.  Saying `a and b are integers' does not provide enough
   information---it does not even give the actual types of a and b.
-- 
`They were supposed to be green.'
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163)
Domain:	chris at cs.umd.edu	Path:	uunet!mimsy!chris



More information about the Comp.lang.c mailing list