syswerda@bbn.com (Gilbert Syswerda) (09/05/90)
I have been having some problems with memory allocation using Borland's C++. The problems seem to reside with the way sizes of objects are computed. An example follows: //************************************************ #include <stdio.h> class c { public: char a[65536]; } void main() { c b; printf ("Sizeof c: %ld\n", (long) (sizeof (c))); printf ("Sizeof b: %ld\n", (long) (sizeof (b))); printf ("Sizeof b.a: %ld\n", (long) (sizeof (b.a))); } **********OUTPUT************* Sizeof c: 2 Sizeof b: 2 Sizeof b.a: 65536 Doing a new() on an instance of class c results in only 2 bytes being allocated. I have tried using both the compact and huge memory models. I am new to both C and C++, so perhaps there is a simple misunderstanding on my part. Anyone have any suggestions?
grimlok@hubcap.clemson.edu (Mike Percy) (09/05/90)
syswerda@bbn.com (Gilbert Syswerda) writes: >I have been having some problems with memory allocation using Borland's C++. >The problems seem to reside with the way sizes of objects are computed. An >example follows: Sorry, the problems arise _mostly_ in the way you are breaking the rules. But there is one buggy thing in here. >//************************************************ >#include <stdio.h> >class c { > public: > char a[65536]; Point 1 -- 65536 is long, in this case it should be unsigned as well. To do less will generate a warning (if -w is set). Decimal constants are of type int, so 32767 is largest. This decimal constant is being promoted to long. Had this been 65535, we could have said 65535u and been within the range, and no warnings would be required. The line should be at least char a[65536l]; if not char a[65536ul]; However, all this is beside the point since the largest structure TurboC++ can properly allocate is 65534 bytes. I haven't figured out why not 65535 as one might think, but I think it has to do with pee-hole optimazations which would not be do-able if you allowed 65535 byte structures. Point 2 -- p331 [Programmer's Guide] With respect to ANSI Standard section 3.3.3.4 "The type of integer required to hold the maximum size of an array." For a normal array, the type is unsigned int, and for huge arrays the type is signed long. > } >void main() { >c b; > printf ("Sizeof c: %ld\n", (long) (sizeof (c))); > printf ("Sizeof b: %ld\n", (long) (sizeof (b))); > printf ("Sizeof b.a: %ld\n", (long) (sizeof (b.a))); > } Point 3 -- p334 [Programmer's Guide] With respect to ANSI Standard section 4.1.1 "The type of the sizeof operator, size_t." The type size_t is unsigned int. Casting an unsigned int to a signed long is dubious, it should be to unsigned long (however, the cast should work correctly since the smaller type was unsigned, the larger type will be zero filled rather than sign extended). A note here is that the %ld should be %lu if you change the cast to (unsigned long). But why make the cast? sizeof will _never_ return anything over 65535! >**********OUTPUT************* >Sizeof c: 2 >Sizeof b: 2 >Sizeof b.a: 65536 Point 4 -- garbage in, garbage out. You asked the compiler to do things it can't do, but it tried, and got the "wrong" results. >Doing a new() on an instance of class c results in only 2 bytes being >allocated. I have tried using both the compact and huge memory models. new() takes an argument that evaluates to a size_t number of bytes. This can be no larger than 65535 [note: for reasons having to do with TC++ heap management, this is really 65524 bytes in small data memory models and 65512 in large data memory models]. >I am new to both C and C++, so perhaps there is a simple misunderstanding >on my part. Anyone have any suggestions? Are you also new to the Intel chip structure -- this is the main root of these problems. An understanding of the languages invloved helps. An understanding of the machine lets you vent your anger at Intel when you see all the stupidities that have to be built into Intel compilers. Oh, yeah. I promised a bug. I feel that the bugs are in the fact that 1) the compiler didn't complain ferociously about the allocation of 65536 bytes. On second thought 65536 == 0 (in 16 bytes), so maybe it didn't have a problem exceeding the max. But it should still complian, and loudly! A hint to everyone who has these sort of problems: always compile with the -w switch enabled (eliminating all warnings eliminates virtually all of these sorts of problems, since you are forced to look up the cause in the manual). Another hint is to learn to read the -S output, which, by the way, is very, very, useful and readable from TC++. A look at the assembly code produced can provide great insights sometimes. "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
jimad@microsoft.UUCP (Jim ADCOCK) (09/07/90)
In article <10344@hubcap.clemson.edu> grimlok@hubcap.clemson.edu (Mike Percy) writes: [re a structure containing a hugh array] >Are you also new to the Intel chip structure -- this is the main root of >these problems. An understanding of the languages invloved helps. An >understanding of the machine lets you vent your anger at Intel when you >see all the stupidities that have to be built into Intel compilers. I disagree. Lots of compilers restrict structure size to 64K -- even on machines with 32-bit flat pointers. Many machines have instruction sets that make short offsets into structures desirable. So, if you write code expecting huge structures to work, Intel is the least of your problems. The "right" thing to do is to make the array a separate allocation, Intel chip, or no Intel chip. [Personally, I find 386/486 machines have fine support for OOPL. 286s I find frustrating. Consider that for a little over $1K I can buy a 386sx machine with 40Meg disk, 1Meg ram, 640x480 gray-scale display, etc. Or I can maybe buy a non-Intel machine with a tiny BW display and only a single floppy drive.... [standard disclaimer] ]
grimlok@hubcap.clemson.edu (Mike Percy) (09/08/90)
jimad@microsoft.UUCP (Jim ADCOCK) writes: >In article <10344@hubcap.clemson.edu> grimlok@hubcap.clemson.edu (Mike Percy) writes: >[re a structure containing a hugh array] >>Are you also new to the Intel chip structure -- this is the main root of >>these problems. An understanding of the languages invloved helps. An >>understanding of the machine lets you vent your anger at Intel when you >>see all the stupidities that have to be built into Intel compilers. >I disagree. Lots of compilers restrict structure size to 64K -- even >on machines with 32-bit flat pointers. Many machines have instruction >sets that make short offsets into structures desirable. So, if you write >code expecting huge structures to work, Intel is the least of your >problems. Hadn't thought of that; virtually all of the C/C++ work I do is on a PC, and I KNOW the kind of things that go on here, I made the mistake of assuming that more sensible architectures didn't have these problems. My beef here is that when people can't/don't/won't understand the limitations the machine can put on the compiler, or the limitations of the compilers (we can't have realistically have limitless, perfect compilers) the first words out of their mouth tend to be "compiler bug." >The "right" thing to do is to make the array a separate allocation, >Intel chip, or no Intel chip. A separate allocation might help sometimes, but TC/TC++ still uses size_t of unsigned int (16-bits), so no way can you get over 64K from malloc(). You could use farmalloc(unsigned long) (32-bit), but the standard conversions can cause huge headaches. farmalloc(1000 * 100); Let's see, 1000 is a decimal constant < 32767, as is 100; both are typed to be signed int. A signed int * signed int is a signed int. The result is converted to an unsigned long for the function call (we always have a prototype in scope, right?). So 1000 * 100 = 100000, but oops, we have to truncate it to fit 16 bits, we have 34465 as the intermediate result. But this is signed, so the value is really -34464. The conversion to long sign extends, but we treat the resultant long as unsigned. It has a value 0xFFFF86A0. That much space surely won't be available on the heap! The correct way do do this is farmalloc(1000ul * 100ul); Now everything is hunky-dory and assuming there is 100K or so of space on the heap, this will succeed. The above could have been worse though: What if we try farmalloc(1000 * 70); After the multiplication and truncation, we end up asking farmalloc for 4464 bytes, which it can surely get for us, but since the result is not NULL, the careless program[mer] will mistakenly assume the it got the 70000 bytes it thought it requested. Who knows what will happen when a write to the area after the "safe" 4464 byte zone occurs? When stuff like the above happens, the most frequent complaint is "Hey, I've found a compiler bug!" I have to look up and think "Forgive them, they know not what they do..." Maybe the above should go into the TurboC FAQ list, if it hasn't recently been added. >[Personally, I find 386/486 machines have fine support for OOPL. 286s I >find frustrating. Consider that for a little over $1K I can buy a 386sx >machine with 40Meg disk, 1Meg ram, 640x480 gray-scale display, etc. Or >I can maybe buy a non-Intel machine with a tiny BW display and only a >single floppy drive.... [standard disclaimer] ] I know EXACTLY what you mean... "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