[comp.lang.c] Found a TC 2.0 bug... ?

ajayshah@alhena.usc.edu (Ajay Shah) (04/28/91)

I think I have identified a bug in TC 2.0.

My machine has 645k of free memory.  I bring up the TC environment 
(not using tcc here).  I've set the memory model to be Huge.
I run this program:
---------------------------------------------------------------------------
1	#include <stdio.h>
2	#include <malloc.h>
3	void
4	main()
5	{
6	    int i;
7	    char *ptr;
8	    ptr = (char *)malloc((unsigned) 0);
9	    for (i=1;i<=1024;i+=i){
10		ptr = (char *)realloc(ptr,(unsigned)i*1024);
11		if (ptr == (char *)NULL) printf("realloc bombed when ");
12		printf("%d Kbytes realloced\n",i);
13	    }
14	}
15
---------------------------------------------------------------------------

I get this output:
---------------------------------------------------------------------------
1 Kbytes realloced
2 Kbytes realloced
4 Kbytes realloced
8 Kbytes realloced
16 Kbytes realloced
32 Kbytes realloced
realloc bombed when 64 Kbytes realloced
realloc bombed when 128 Kbytes realloced
realloc bombed when 256 Kbytes realloced
realloc bombed when 512 Kbytes realloced
realloc bombed when 1024 Kbytes realloced
---------------------------------------------------------------------------

Question: why does he have problems allocating > 64k???  Is the
culprit Intel or Borland?

Now watch.  Instead of taking RAM in steps of 1024 bytes, I use
steps of 1000 bytes.  Ie., I change line 10 to be

ptr = (char *)realloc(ptr,(unsigned)i*1000);

Now it works!!  It goes ahead and reallocs me 1Meg without complaining!
There's no way I can have this kind of free RAM on a 8086!!
-- 
_______________________________________________________________________________
Ajay Shah, (213)734-3930, ajayshah@usc.edu
                             The more things change, the more they stay insane.
_______________________________________________________________________________

grimlok@hubcap.clemson.edu (Mike Percy) (04/28/91)

ajayshah@alhena.usc.edu (Ajay Shah) writes:

>I think I have identified a bug in TC 2.0.
 
How I wish I had a buck for each time someone says that so casually and
was dead wrong.  Just a few weeks ago, I had to write a scathing letter
to the editors of CUJ for allowing such statements to be made without a
shred of evidence -- in fact all could have been easily dis-proven with
a simple trip to the manuals.

>My machine has 645k of free memory.  I bring up the TC environment 
>(not using tcc here).  I've set the memory model to be Huge.
>I run this program:
>---------------------------------------------------------------------------
>1	#include <stdio.h>
>2	#include <malloc.h>
>3	void
>4	main()
>5	{
>6	    int i;
>7	    char *ptr;
>8	    ptr = (char *)malloc((unsigned) 0);
>9	    for (i=1;i<=1024;i+=i){
>10		ptr = (char *)realloc(ptr,(unsigned)i*1024);
>11		if (ptr == (char *)NULL) printf("realloc bombed when ");
>12		printf("%d Kbytes realloced\n",i);
>13	    }
>14	}
>15
>---------------------------------------------------------------------------

>I get this output:
>---------------------------------------------------------------------------
>1 Kbytes realloced
>2 Kbytes realloced
>4 Kbytes realloced
>8 Kbytes realloced
>16 Kbytes realloced
>32 Kbytes realloced
>realloc bombed when 64 Kbytes realloced
>realloc bombed when 128 Kbytes realloced
>realloc bombed when 256 Kbytes realloced
>realloc bombed when 512 Kbytes realloced
>realloc bombed when 1024 Kbytes realloced
>---------------------------------------------------------------------------

>Question: why does he have problems allocating > 64k???  Is the
>culprit Intel or Borland?

>Now watch.  Instead of taking RAM in steps of 1024 bytes, I use
>steps of 1000 bytes.  Ie., I change line 10 to be

>ptr = (char *)realloc(ptr,(unsigned)i*1000);

>Now it works!!  It goes ahead and reallocs me 1Meg without complaining!
>There's no way I can have this kind of free RAM on a 8086!!
 
When are people going to learn how to read a manual for crissakes??????

Let's go through this one more time, slowly.  But first, Ajay, no
offense to you personally.  There must be about 50 or so postings montly
which ask virtually the exact same question.

The first point of order is to open a manual (or check out the
prototypes for *alloc in malloc.h).  Notice that the *alloc functions
take a parameter of type size_t.  Now, everyone should realize that
size_t represents the upper limits of allocation amounts.  In TC, size_t
is typedef'd thusly: typedef unsigned int size_t;

The largest value allowable in an unsigned int (in TC) is 65535 (64K),
therefore the largest block of memory which can be *alloc()'d is 64K.
This turns out not to be the case.  Depending on the memory model in
use, each *alloc()'d block uses either 8 or 16 bytes of header
information.  There is also some space in a 64K byte block which cannot
be used because *alloc() functions work in 8-byte chunks.  The real
upper limit of *alloc() is closer to 655310 useable bytes.  This 64K
limit has to to with the limits of the segmented Intel architecture and
efficiency.  Borland provides a way around this with the non-standard
pointer type huge, and the far*alloc functions.  The far*alloc functions
take a long int as a size request.i  Huge pointers are a significant CPU
user, and aren't recommended unless you absolutely must have >64K
structures.

As for the program not working with 1024, but working fine with 1000,
consider what the standard conversions being done here. 1024 is of type
int, multiplied by an unsinged int.  The rules say that if either
operand is of type unsigned, the other is converted to unsigned.  So now
we have <unsigned int, 64> * <unsigned int, 1024>, which is 65536.  But
65536 cannot be represented as an unsigned int, so the compiler forces
it in by truncating any high order bits.  The result is, according to
this, now 0!  Of course realloc() returns NULL if you ask it to resize a
size 0.  Notice that the rest of your numbers truncate to exactly 0, so
they all fail -- not because you don't have the memory or anything like
that.  They fail because of the _definition_ of realloc()!

Changing 1024 to 1000 changes this a bit, but you are a far cry from
allocating 1 meg.
1 * 1000 = 1000, fits in unsigned int -- no problem 
2 * 1000 = 2000, fits in unsigned int -- no problem
4 * 1000 = 4000, fits in unsigned int -- no problem  
8 * 1000 = 8000, fits in unsigned int -- no problem
16 * 1000 = 16000, fits in unsigned int -- no problem 
32 * 1000 = 32000, fits in unsigned int -- no problem
64 * 1000 = 64000, fits in unsigned int! -- realloc() works ok
128 * 1000 = 128000, problem, must truncate, realloc() gives 62465 byte chunk 
256 * 1000 = 256000, after truncation, realloc() gives 59392 block
512 * 1000 = 512000, after truncation, we get a 53248 byte chunk from realloc()
1024 * 1000 = 1024000, we ask realloc() for a 40960 byte block 

So, you can see that we never allocated >64K in one call.  Nor is this a
bug of any sort.  It is simply a phenomena of compiler limits, which are
documented quite reasonably. I suppose Borland could come out and
explicitly state in the docs exactly what the biggest size block which
can be allocated by *alloc(), but maybe they feel that they already did
that in the prototype description.

C is not a language for those unconcerned with "trivial" details like
compiler-based limitations, nor for those who cannot be bothered to
spend a little time with a manual to determine what these limits are.
Hell, browse through stddefs.h, stdlib.h, and limits.h at least once to
see how things look!

"I don't know about your brain, but mine is really...bossy."
Mike Percy                    grimlok@hubcap.clemson.edu
ISD, Clemson University       mspercy@clemson.BITNET
(803)656-3780                 mspercy@clemson.clemson.edu

aduncan@rhea.trl.oz (Allan Duncan) (04/30/91)

From article <32383@usc>, by ajayshah@alhena.usc.edu (Ajay Shah):
> I think I have identified a bug in TC 2.0.
[sample program and output deleted]

> Question: why does he have problems allocating > 64k???  Is the
> culprit Intel or Borland?

Just Intel and the program - because of Intel's abysmal addressing
scheme with its unconnected segment and paragraph registers (I think
that's the terminology) it is difficult to handle addressing in lumps
bigger than 64K.  Borland has chosen to go for speed and compactness by
limiting you to a 16 bit int (+/-32K, or 64K unsigned), probably in part
because of this.  On the 68000 CPU there are compilers that will do
either 16 or 32 bit ints at the flick of a compiler switch.

Allan Duncan	ACSnet	 a.duncan@trl.oz
(+613) 541 6708	Internet a.duncan@trl.oz.au
		UUCP	 {uunet,hplabs,ukc}!munnari!trl.oz.au!a.duncan
Telecom Research Labs, PO Box 249, Clayton, Victoria, 3168, Australia.