[net.micro] weird C behavior

nather@utastro.UUCP (Ed Nather) (03/21/86)

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.
The following was extracted directly from the display screen of my
IBM PC.  Try it on *your* 16-bit computer.  Then explain it to a friend.
-----------------------------------------------------------------------
C% print weird.c

/* weird.c - demonstrate weird C behavior */
/* Reference: Kernighan and Ritchie pp. 40-41 */

#include <stdio.h>

#define BIG 36864

main()
{
int i;

i = BIG;
if(i == BIG)
    printf("Equality found: i = %d, BIG = %d\n", i, BIG);
else
    printf("Equality NOT found: i = %d, BIG = %d\n", i, BIG);
}

C% weird

Equality NOT found: i = -28672, BIG = -28672

-----------------------------------------------------------------------
-- 
Ed Nather
Astronomy Dept, U of Texas @ Austin
{allegra,ihnp4}!{noao,ut-sally}!utastro!nather
nather@astro.UTEXAS.EDU

roy@gitpyr.UUCP (Roy Mongiovi) (03/22/86)

In article <557@utastro.UUCP>, nather@utastro.UUCP (Ed Nather) writes:
> Equality NOT found: i = -28672, BIG = -28672

Ok.  The #defined constant is greater than 32767, so it's a long on a
16 bit machine.  Top 16 bits are zero, bottom are 0x9000.  It gets
assigned into an SIGNED integer, so that gets the bottom 16 bits.
Then to perform the comparison, C promotes the int to a long, and
since it's signed that extends the sign bit.  One value now has
the top 16 bits zero, the other has 0xFFFF.  Unfortunately, the
program ignores the fact that the constant is a long, and printf's
it through a %d.  Since words and bytes are stored in least significant,
most significant order in memory, the address passed to printf for both
variables is that of the bottom 16 bits, which are equal.  Therefore
the printed output seems to contradict the comparison.

The real question is:  who made the mistake?  The compiler by assuming
the constant is a long, or the programmer by printing with a %d?  Maybe
you ought to have to say L to get a long constant....
-- 
Roy J. Mongiovi.	Office of Computing Services.		User Services.
Georgia Institute of Technology.	Atlanta GA  30332.	(404) 894-6163
 ...!{akgua, allegra, amd, hplabs, ihnp4, masscomp, ut-ngp}!gatech!gitpyr!roy

jsdy@hadron.UUCP (Joseph S. D. Yao) (03/22/86)

In article <557@utastro.UUCP> nather@utastro.UUCP (Ed Nather) writes:
>/* weird.c - demonstrate weird C behavior */
>/* Reference: Kernighan and Ritchie pp. 40-41 */
>#include <stdio.h>
>#define BIG 36864
>main()
>{
>int i;
>
>i = BIG;
>if(i == BIG)
>    printf("Equality found: i = %d, BIG = %d\n", i, BIG);
>else
>    printf("Equality NOT found: i = %d, BIG = %d\n", i, BIG);
>}
>Equality NOT found: i = -28672, BIG = -28672

Check the code produced by your compiler.  Could you be mixing
16- and 32-bit operations?  I.e., could the compiler be moving
the BIG word into 16-bit i (making it a negative number), then
(finding that BIG is > 32767) upgrading both to (long) before
the compare?  Check and see.  You don't say what machine or C
you used, so we have no way of telling.
-- 

	Joe Yao		hadron!jsdy@seismo.{CSS.GOV,ARPA,UUCP}

chris@umcp-cs.UUCP (Chris Torek) (03/22/86)

In article <557@utastro.UUCP> nather@utastro.UUCP (Ed Nather) writes:

>Here is a short C program which gives correct output according to K & R.
>[...]  Try it on *your* 16-bit computer.

The code contains the following [paraphrased]:

	printf("%d\n", 36864);

On a 16 bit machine, this should read

	printf("%ld\n", 36864);

One alternative is to change Ed's original program to read

	#define BIG ((int) 36864)

Too bad lint does not catch the printf() type bug.
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 1415)
UUCP:	seismo!umcp-cs!chris
CSNet:	chris@umcp-cs		ARPA:	chris@mimsy.umd.edu

jsdy@hadron.UUCP (Joseph S. D. Yao) (03/23/86)

In article <436@umcp-cs.UUCP> chris@umcp-cs.UUCP (Chris Torek) writes:
>In article <557@utastro.UUCP> nather@utastro.UUCP (Ed Nather) writes:
>>Here is a short C program which gives correct output according to K & R.
>>[...]  Try it on *your* 16-bit computer.
>The code contains the following [paraphrased]:
>	printf("%d\n", 36864);
>On a 16 bit machine, this should read
>	printf("%ld\n", 36864);
>One alternative is to change Ed's original program to read
>	#define BIG ((int) 36864)

Passed arguments should always be passed as an "int", I do believe.
Changing the printf specification will  n e v e r  change what the
C compiler does with the rest of the arguments!!  Nather's original
posting led me to believe he was using some kind of a 16/32 bit
machine, with a C compiler that had not quite been consistent.  I.e.,
on the comparison, all I could think was that a 16-bit int had sign-
extended to compare with an IMPLICIT long constant (look at it: it's
not an explicit long constant!).  This is inconsistent.  However, an
arg has to be explicitly declared; so the int default almost has to
be honoured.

Sorry to have to publicly disagree, Chris.
-- 

	Joe Yao		hadron!jsdy@seismo.{CSS.GOV,ARPA,UUCP}

broehl@watdcsu.UUCP (Bernie Roehl) (03/25/86)

In article <436@umcp-cs.UUCP> chris@umcp-cs.UUCP (Chris Torek) writes:
>
>The code contains the following [paraphrased]:
>
>	printf("%d\n", 36864);
>
>On a 16 bit machine, this should read
>
>	printf("%ld\n", 36864);
>

Both are wrong on a 16 bit machine, and both will produce garbage (though
probably different garbage).  What you want is

        printf("%ld\n", 36864L);

Note the 'L' suffix; this is how you tell C that you mean a Long constant.

chris@umcp-cs.UUCP (Chris Torek) (03/27/86)

In article <330@hadron.UUCP> jsdy@hadron.UUCP (Joseph S. D. Yao) writes:

>>On a 16 bit machine, this should read
>>	printf("%ld\n", 36864);		[me]

>Passed arguments should always be passed as an "int", I do believe.
>...  Sorry to have to publicly disagree, Chris.

Unfortunately for you, I checked my sources first.  The clue was
there in Ed's original posting.  According to K&R, a constant that
is too big to be an `int' is automatically considered a `long'; on
a 16 bit machine,

	printf("%ld\n", 36864);

and

	printf("%ld\n", 36864L);

build the exact same stack.

For you ANSI C buffs, I quote from a year-old draft (the latest I
could find), section C.1.3.2, `Integer constants':

	If the value of an unsuffixed decimal constant (base 10) is no
	greater than that of the largest signed int, the constant has
	type signed int; otherwise it has type signed long int.

Now, if you were using ANSI C and had written, say,

	void myfunc(int);

	myfunc(36864);

you would get something closer to what you expected. . . .
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 1415)
UUCP:	seismo!umcp-cs!chris
CSNet:	chris@umcp-cs		ARPA:	chris@mimsy.umd.edu

ark@alice.UucP (Andrew Koenig) (03/27/86)

>>The code contains the following [paraphrased]:
>>	printf("%d\n", 36864);
>>On a 16 bit machine, this should read
>>	printf("%ld\n", 36864);
>>One alternative is to change Ed's original program to read
>>	#define BIG ((int) 36864)

> Passed arguments should always be passed as an "int", I do believe.

Absolutely not!  Are you really suggesting there's no way to pass
a long integer to a function?  The following are correct:

	printf("%d\n", 1);
	printf("%ld\n", 1L);		/* long constant */
	printf("%ld\n", (long) 1);	/* long constant expression */

The following are incorrect:

	printf("%d\n", 1L);
	printf("%ld\n", 1);
	printf("%d\n", (long) 1);

This one is correct on some machines but not on others:

	printf("%d\n", 36864);

because 36864 is an int on some machines and a long on others.

mcewan@uiucdcs.CS.UIUC.EDU (03/27/86)

>> The code contains the following [paraphrased]:
>> 
>> 	printf("%d\n", 36864);
>> 
>> On a 16 bit machine, this should read
>> 
>> 	printf("%ld\n", 36864);
>> 
> 
> Both are wrong on a 16 bit machine, and both will produce garbage (though
> probably different garbage).  What you want is
> 
>         printf("%ld\n", 36864L);
> 
> Note the 'L' suffix; this is how you tell C that you mean a Long constant.

K & R, page 180:

"A decimal constant whose value exceeds the largest signed machine integer
is taken to be long."

			Scott McEwan
			{ihnp4,pur-ee}!uiucdcs!mcewan

Green s/m watchlizard seeks s/f/wl - object: companionship. Reply
Box 23, Cynosure.

jsdy@hadron.UUCP (Joseph S. D. Yao) (03/28/86)

In article <559@umcp-cs.UUCP> chris@umcp-cs.UUCP (Chris Torek) writes:
>In article <330@hadron.UUCP> jsdy@hadron.UUCP (Joseph S. D. Yao) writes:
>>>On a 16 bit machine, this should read
>>>	printf("%ld\n", 36864);		[me]
>>Passed arguments should always be passed as an "int", I do believe.
>                            ...  According to K&R, a constant that
>is too big to be an `int' is automatically considered a `long'; ...
>For you ANSI C buffs, I quote from a year-old draft (the latest I
>could find), section C.1.3.2, `Integer constants':

Moral:  check your sources (no pun intended?).  We analysed this
"weird behaviour" the same way; but I was wrong in calling it a
compiler error.

End of discussion.
-- 

	Joe Yao		hadron!jsdy@seismo.{CSS.GOV,ARPA,UUCP}

chris@umcp-cs.UUCP (Chris Torek) (03/30/86)

In article <2194@watdcsu.UUCP> broehl@watdcsu.UUCP (Bernie Roehl) writes:
>In article <436@umcp-cs.UUCP> chris@umcp-cs.UUCP (Chris Torek) writes:
>>On a 16 bit machine, this should read
>>
>>	printf("%ld\n", 36864);		[me]
>
>Both are wrong on a 16 bit machine, and both will produce garbage (though
>probably different garbage).

False.  I have already explained why, with references to K&R and an
old X3J11 draft.

>What you want is
>
>        printf("%ld\n", 36864L);	[Bernie]

I agree that this is considerably better in practice.  In fact, I
would not at all mind a compiler that gave warnings whenever an
unsuffixed constant did not fit in an integer and was promoted to
long (or unsigned long for `U' suffixed and hex and octal constants,
per X3J11).
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 1415)
UUCP:	seismo!umcp-cs!chris
CSNet:	chris@umcp-cs		ARPA:	chris@mimsy.umd.edu

mcewan@uiucdcs.CS.UIUC.EDU (03/30/86)

>> >Here is a short C program which gives correct output according to K & R.
>> >[...]  Try it on *your* 16-bit computer.
>> The code contains the following [paraphrased]:
>> 	printf("%d\n", 36864);
>> On a 16 bit machine, this should read
>> 	printf("%ld\n", 36864);
>> One alternative is to change Ed's original program to read
>> 	#define BIG ((int) 36864)
>
> Passed arguments should always be passed as an "int", I do believe.

Why? Are you saying that one should never have a function that accepts
long arguments (or short, char, float, pointer, etc., for that matter)?

> Changing the printf specification will  n e v e r  change what the
> C compiler does with the rest of the arguments!!

Never said it did. The constant "36864" is passed as a long no matter
what string is used in the "printf" - the only difference is that with
"%ld" printf correctly interprets and prints out the argument as a long.

>  Nather's original
> posting led me to believe he was using some kind of a 16/32 bit
> machine, with a C compiler that had not quite been consistent.  I.e.,
> on the comparison, all I could think was that a 16-bit int had sign-
> extended to compare with an IMPLICIT long constant (look at it: it's
> not an explicit long constant!).  This is inconsistent.

What is inconsistent about it? It is perfectly proper C.

>  However, an
> arg has to be explicitly declared; so the int default almost has to
> be honoured.

I'm afraid I don't understand what you mean.

			Scott McEwan
			{ihnp4,pur-ee}!uiucdcs!mcewan

Green s/m watchlizard seeks s/f/wl - object: companionship. Reply
Box 23, Cynosure.

jsdy@hadron.UUCP (03/31/86)

In article <5190@alice.uUCp> ark@alice.UucP (Andrew Koenig) writes:
>> Passed arguments should always be passed as an "int", I do believe.
>Absolutely not!

Folks, I'm not going to spend the rest of my life apologising for
those two stupid articles I wrote last week.  I hope.  Let's accept
that my brain was tied up with something else and get on with it.

What I  m e a n t  to say above was "Passed integer constants ... ".
As we all know, we can force a constant to be anything at all.  What
caught me was the surprise promotion of a constant to a long, with
no request from the user, if the constant is > the larget integer.
This, though a valid part of the language for years, I think is a
problem.  Then again, writing code that uses that kind of a constant
may be seen to be a problem also.  I have spent some effort in diverse
programs avoiding this.
-- 

	Joe Yao		hadron!jsdy@seismo.{CSS.GOV,ARPA,UUCP}

bc@cyb-eng.UUCP (Bill Crews) (04/03/86)

> /* weird.c - demonstrate weird C behavior */
> /* Reference: Kernighan and Ritchie pp. 40-41 */
. . .
> #define BIG 36864
> 
> main()
> {
> int i;
> 
> i = BIG;
> if(i == BIG)
>     printf("Equality found: i = %d, BIG = %d\n", i, BIG);
> else
>     printf("Equality NOT found: i = %d, BIG = %d\n", i, BIG);
> }
> 
> Equality NOT found: i = -28672, BIG = -28672
> 
> -----------------------------------------------------------------------
> Ed Nather
> Astronomy Dept, U of Texas @ Austin

It seems to me that your compiler has done the right thing.  Since 36864
cannot be represented as an int on your machine, it generated a positive
long rather than a negative int.  When i is extended to a long, the sign
bit is extended, so the comparison fails, as it should.  The only reason
you got -28672 for BIG instead of nulls is because your machine has backwards
byte order.
-- 
	- bc -

..!{seismo,topaz,gatech,nbires,ihnp4}!ut-sally!cyb-eng!bc  (512) 835-2266