weird C behavior

Gregory Smith greg at utcsri.UUCP
Wed Mar 26 04:36:26 AEST 1986


In article <3090013 at csd2.UUCP> dube at csd2.UUCP (Tom Dube) writes:
>>Here is a short C program which gives correct output according to K & R.
>>While I can't argue it's wrong, it is not transparently right, either.
>>#include <stdio.h>
>>#define BIG 36864
>>int i;
>>i = BIG;
>>if (i != BIG)
>>   printf("Equality NOT found: i = %d, BIG = %d\n", i, BIG);
>
> Sure it's correct. i is not 36864 because it is forced into a 16
>bit integer.  The numbers look the same when you print them because
>you are also forcing BIG into an integer with the format %d.  Try
>printing both numbers with %ld.
>                                    Tom Dube

Lots of people noticed that 36864 is a (long) while i is an (int).
( we must be dealing with 16-bit ints, of course ). A much more
serious problem exists in the printf call, which reduces to

	printf("..%d..%d..", i, 36864 );

Since 36864 is a long (32 bits ) and assuming 16-bit addresses (e.g. PDP-11)
the parameter list in the stack for printf looks like this:

	< address of string >
	< value of i	    >
	< low word of 36864 >
	< hi word of 36864 >

[ Note: I am assuming that the low word of a long is stored first. ]
So printf sees that two (2) integers are required
(_by_looking_at_the_string_) and prints i and the lo word of the constant.
You see the problem?

	printf("...%d...%d...%s", i, 36864, "holy lint, batman!");

The stack is now as above, with the addition of one more address at the
end of the list. printf sees that two integers and one string are required,
so it prints the same 2 numbers as before, and _then_tries_to_use_the_high_
_word_of_36864_as_a_string_address_! Gotcha!

The arguments to printf MUST AGREE with the types specified in the format
string. There is no way for printf to tell the types _or_the_data_sizes_
by looking at the stack. Thus simply changing the "%d"'s in the string to
"%ld"'s, as suggested by Mr. Dube, will most probably result in strangeness.
We need to convert 'i' to a long before that will work.
What is needed is:

 printf("Equality NOT found: i = %ld, BIG = %ld\n", (long) i, (long) BIG);

Now printf expects two longs, and is handed two longs. Note that the
second (long) [ before BIG ] is redundant in this case, but it is what we
call a Very Good Idea :-).

Moral of the story: Be *sure* that the types of parameters to printf are
compatible with the format specifiers given in the format string.
Or: 'Caveat Printor'.( groan....). Us vaxers can be more careless because
our ints and longs are both 32 bits!

Note: I am 97.23% certain that LINT will not catch any error of this type.

-- 
"No eternal reward will forgive us now for wasting the dawn" -J. Morrison
----------------------------------------------------------------------
Greg Smith     University of Toronto       ..!decvax!utzoo!utcsri!greg



More information about the Comp.lang.c mailing list