Weird malloc behaviour

L

leptone

Dear all,

I am programming a PLC with an 80188 processor, hence I am using an
old version of Borland C++ (3.10). While doing the job, I've
encountered strange behaviours and I've isolated the problem that
seems to be related to a malloc function. I wrote a little piece of
code to reproduce the situation:

#include <stdlib.h>
#include <stdio.h>

typedef struct {
int a;
int b;
} BEAM_PROFILE;


int main(void){

BEAM_PROFILE * bpp;
int i;

bpp=(BEAM_PROFILE *)malloc(sizeof(BEAM_PROFILE));
printf("Memory was allocated at address %.8X\n", bpp);

bpp=(BEAM_PROFILE *)malloc(sizeof(BEAM_PROFILE));
printf("Memory was allocated at address %.8X\n", bpp);
}

When I use the small memory model, everything runs smoothly. The
problem is when I use the Large memory model. In this case the program
output is the following:

C:\BCTEST>tstmall
Memory was allocated at address 00000004
Memory was allocated at address 00000004

Every call to malloc returns exactly the same address (not null). I've
verified that the sizeof(BEAM_PROFILE * ) is 4 bytes with the large
memory model and 2 bytes with the small memory model, so I really
cannot figure out where the problem is...

Hope you have some clue about it...

Regards

Nicola
 
S

santosh

Dear all,

I am programming a PLC with an 80188 processor, hence I am using an
old version of Borland C++ (3.10). While doing the job, I've
encountered strange behaviours and I've isolated the problem that
seems to be related to a malloc function. I wrote a little piece of
code to reproduce the situation:

#include <stdlib.h>
#include <stdio.h>

typedef struct {
int a;
int b;
} BEAM_PROFILE;


int main(void){

BEAM_PROFILE * bpp;
int i;

bpp=(BEAM_PROFILE *)malloc(sizeof(BEAM_PROFILE));

No need to cast the return value from malloc.
printf("Memory was allocated at address %.8X\n", bpp);

Change the format specifier from X to p and cast bpp to void*

printf("Memory was allocated at address %p\n", (void*)bpp);
bpp=(BEAM_PROFILE *)malloc(sizeof(BEAM_PROFILE));
printf("Memory was allocated at address %.8X\n", bpp);

As above.
}

When I use the small memory model, everything runs smoothly. The
problem is when I use the Large memory model. In this case the program
output is the following:

C:\BCTEST>tstmall
Memory was allocated at address 00000004
Memory was allocated at address 00000004

Do you still get the same output after the suggested changes?

<snip>
 
J

Jens Thoms Toerring

I am programming a PLC with an 80188 processor, hence I am using an
old version of Borland C++ (3.10). While doing the job, I've
encountered strange behaviours and I've isolated the problem that
seems to be related to a malloc function. I wrote a little piece of
code to reproduce the situation:
#include <stdlib.h>
#include <stdio.h>
typedef struct {
int a;
int b;
} BEAM_PROFILE;
int main(void){
BEAM_PROFILE * bpp;
int i;
bpp=(BEAM_PROFILE *)malloc(sizeof(BEAM_PROFILE));

The cast of the return value is not needed if you didn't forgot
printf("Memory was allocated at address %.8X\n", bpp);

The "%X" format specifier expects an unsigned int argument.
You pass it a pointer instead. Did you try instead

printf("Memory was allocated at address %p\n", (void) bpp);

which is the correct format specifier for printing out addresses?
Or, if you insist on "%X", did you at least try to cast the
address to an unsigned int? Not that this is something I would
recommend for a portable program or that there is any guarantee
that the resulting value makes any sense, but at least it would
make sure that printf() would receive an unsigned int where it
expects one.
bpp=(BEAM_PROFILE *)malloc(sizeof(BEAM_PROFILE));
printf("Memory was allocated at address %.8X\n", bpp);
}
When I use the small memory model, everything runs smoothly. The
problem is when I use the Large memory model. In this case the program
output is the following:
C:\BCTEST>tstmall
Memory was allocated at address 00000004
Memory was allocated at address 00000004
Every call to malloc returns exactly the same address (not null). I've
verified that the sizeof(BEAM_PROFILE * ) is 4 bytes with the large
memory model and 2 bytes with the small memory model, so I really
cannot figure out where the problem is...

It could very well be that an unsigned int has only two bytes
on your machine, whatever the "memory model" (a term the C
standard doesn't use, so it's an extension of C) is. If for
the "small memory model" the address also has 2 bytes using
the "%X" format specifier might produce something that looks
like a reasonable address while, with the "large memory model",
obvioulsy only junk gets printed out. printf() and other vari-
adic function don't like it if they get lied to - the argu-
ments you pass them better fit the format specifier.

Regards, Jens
 
K

Kenneth Brody

Jens said:
The cast of the return value is not needed if you didn't forgot
to include <stdlib.h>. If you forgot it may keep the compiler
from flagging what might be a serious bug.

Especially if compiled under "large model", where ints are 2
bytes and pointers are 4 bytes. If the prototype isn't in
scope, then the compiler will assume that an int is returned,
and discard the upper 16 bits of the return value. In this
case, it would discard the segment and keep the offset.

[...][...]

It is perfectly reasonable in "real mode, large model" on an
x86 processor for malloc() to return a value with an offset
of 4 every time.

--
+-------------------------+--------------------+-----------------------+
| Kenneth J. Brody | www.hvcomputer.com | #include |
| kenbrody/at\spamcop.net | www.fptech.com | <std_disclaimer.h> |
+-------------------------+--------------------+-----------------------+
Don't e-mail me at: <mailto:[email protected]>
 
L

leptone

Ok, I followed your suggestions and edited the code like this:

///////////////////////////////////////////////////////////////

#include <stdlib.h>
#include <stdio.h>



typedef struct {
int a;
int b;
int c;
} BEAM_PROFILE;


void main(void){

BEAM_PROFILE * bpp;
unsigned long lpp;


printf("sizeof(*BEAM_PROFILE) is %d\n", sizeof(BEAM_PROFILE*));
printf("sizeof(lpp) is %d\n", sizeof(lpp));

bpp=malloc(sizeof(BEAM_PROFILE));
lpp=(unsigned long)bpp;

printf("Memory was allocated at address(X mod.) %.8X\n", bpp);
printf("Memory was allocated at address(p mod.) %p\n", bpp);

bpp=malloc(sizeof(BEAM_PROFILE));
printf("Size of the pointer is %d\n", sizeof(bpp));
lpp=(unsigned long)bpp;

printf("Memory was allocated at address %.8X\n", bpp);
printf("Memory was allocated at address(p mod.) %p\n", bpp);

}

///////////////////////////////////////////////////////////////////////////////////

The result, using the Large memory model, is the following:

C:\PACIFICO\SRC>tstmall
sizeof(*BEAM_PROFILE) is 4
sizeof(lpp) is 4
Memory was allocated at address(X mod.) 00000004
Memory was allocated at address(p mod.) 1DD5:0004
Size of the pointer is 4
Memory was allocated at address 00000004
Memory was allocated at address(p mod.) 1DD6:0004

With the small one, instead, I have the following

C:\PACIFICO\SRC>tstmall
sizeof(*BEAM_PROFILE) is 2
sizeof(lpp) is 4
Memory was allocated at address(X mod.) 00000672
Memory was allocated at address(p mod.) 0672
Size of the pointer is 2
Memory was allocated at address 0000067C
Memory was allocated at address(p mod.) 067C


So now things seem to work fine for the both of them. Now I am just
wondering why it takes to different segments to allocate a structure
which is only four bytes long, but i guess this has something to do
with the memory management model of the compiler...

By the way, thanks to everyone for your precious help!!!

Regards
Nicola
 
S

santosh

Ok, I followed your suggestions and edited the code like this:

///////////////////////////////////////////////////////////////

#include <stdlib.h>
#include <stdio.h>

typedef struct {
int a;
int b;
int c;
} BEAM_PROFILE;


void main(void){

This is not portable. Use int main(void) instead.
BEAM_PROFILE * bpp;
unsigned long lpp;

printf("sizeof(*BEAM_PROFILE) is %d\n", sizeof(BEAM_PROFILE*));

Or

printf("sizeof(BEAM_PROFILE*) is %lu\n", (unsigned long)sizeof bpp);
printf("sizeof(lpp) is %d\n", sizeof(lpp));

Be aware that the result of an application of the sizeof operator is a
value of type size_t, which is an implementation defined unsigned
integer type. Under C90 it must be a typedef for one of unsigned char,
unsigned short, unsigned int, or unsigned long, the only unsigned
integer types defined. However it's not easily possible at runtime to
know which of these types is actually typedef'ed to size_t. That is why
it is safer to print the value as an unsigned long and cast the
corresponding argument appropriately.

Under C99 a new printf/scanf specifier 'z' has been added specifically
for size_t arguments, but unfortunately, C99 itself is not very
portable.

In any case you are supplying mismatched arguments for conversion
specifiers in the above calls, which invokes undefined behaviour. A
cast to int would resolve the UB, but it's better to cast size_t
arguments to unsigned int or unsigned long.
bpp=malloc(sizeof(BEAM_PROFILE));

If you do:

bpp = malloc(sizeof *bpp);

then the code remains correct even if later the base type of bpp is
changed.
lpp=(unsigned long)bpp;

printf("Memory was allocated at address(X mod.) %.8X\n", bpp);

You mean to supply lpp here, I'm sure.
printf("Memory was allocated at address(p mod.) %p\n", bpp);

The 'p' conversion specifier needs the corresponding argument to be a
void*. So a cast is, strictly speaking, necessary.
bpp=malloc(sizeof(BEAM_PROFILE));
printf("Size of the pointer is %d\n", sizeof(bpp));
lpp=(unsigned long)bpp;

What is the intent behind assigning to lpp if you never use it further?
printf("Memory was allocated at address %.8X\n", bpp);
printf("Memory was allocated at address(p mod.) %p\n", bpp);

}

///////////////////////////////////////////////////////////////////////////////////

The result, using the Large memory model, is the following:

C:\PACIFICO\SRC>tstmall
sizeof(*BEAM_PROFILE) is 4
sizeof(lpp) is 4
Memory was allocated at address(X mod.) 00000004
Memory was allocated at address(p mod.) 1DD5:0004
Size of the pointer is 4
Memory was allocated at address 00000004
Memory was allocated at address(p mod.) 1DD6:0004

This is the danger of treating pointers as compatible with integers. For
most modern flat model machines a pointer is just an integer, but such
a relation is not guaranteed. As you have found, under segmented memory
architectures, a pointer is usually of two components and merely
casting one to an integer does not yield the full address, but the
offset part alone.
With the small one, instead, I have the following

C:\PACIFICO\SRC>tstmall
sizeof(*BEAM_PROFILE) is 2
sizeof(lpp) is 4
Memory was allocated at address(X mod.) 00000672
Memory was allocated at address(p mod.) 0672
Size of the pointer is 2
Memory was allocated at address 0000067C
Memory was allocated at address(p mod.) 067C


So now things seem to work fine for the both of them. Now I am just
wondering why it takes to different segments to allocate a structure
which is only four bytes long, but i guess this has something to do
with the memory management model of the compiler...

That's an implementation decision. Under the x86 segmented memory model,
a segment can begin at any "paragraph boundary", which is any address
that is a multiple of 16. Segments do not have a minimum size and they
can perfectly overlap. Your compiler has merely chosen to place the
second BEAM_PROFILE object on the next higher segment address, instead
of at a higher offset within the same segment.

<snip>
 
L

leptone

Thank you for the suggestions, I will apply them in the "real
code" (this was just a test routine btw written in haste, although I
must admit that I still get quite brain damaged by type casting ;))...

Regards
Nicola
 
J

Jens Thoms Toerring

Thank you for the suggestions, I will apply them in the "real
code" (this was just a test routine btw written in haste, although I
must admit that I still get quite brain damaged by type casting ;))...

Keep staying wary about casts. Much too often they are used without
good reason (or for bad reasons, i.e. to keep the compiler from com-
plaining about something suspicious). Only use them if you exactly
know why you have to do it and only if there's no better way. In most
situations the compiler should be able to figure things out. Only if
you made sure that it can't a cast should be used and then it will
serve as a flag to the reader of the code that something special is
going on at that place.
Regards, Jens
 
C

CBFalconer

Thank you for the suggestions, I will apply them in the "real code"
(this was just a test routine btw written in haste, although I must
admit that I still get quite brain damaged by type casting ;)).

Please do not top-post. Your answer belongs after (or intermixed
with) the quoted material to which you reply, after snipping all
irrelevant material. See the following links:

<http://www.catb.org/~esr/faqs/smart-questions.html>
<http://www.caliburn.nl/topposting.html>
<http://www.netmeister.org/news/learn2quote.html>
<http://cfaj.freeshell.org/google/> (taming google)
<http://members.fortunecity.com/nnqweb/> (newusers)
 
B

Barry Schwarz

Thank you for the suggestions, I will apply them in the "real
code" (this was just a test routine btw written in haste, although I
must admit that I still get quite brain damaged by type casting ;))...

The phrase "type casting" is like the phrase "over and out". Both
contain unnecessary words and indicate amateurish use. The operator
"(some_valid_C_type)" is a cast operator. No "type" required.

Casts are frequently overused and abused. The language is
sufficiently rich and flexible that they are not needed as often as
some think. But they are there for a reason and at times even
required. A few of the common correct uses of a cast are:

A printf argument corresponding to a %p format specification must
have type void*

A printf argument involving sizeof must be cast to the correct
type for the corresponding format specification. (In C99, the z
format modifier eliminates the need.)

To silence the diagnostic some compilers issue when
**deliberately** assigning an integer value to a variable of lower
rank or when **deliberately** demoting a floating point value.

When dereferencing the parameter of a callback function, such as
the compare function used by qsort. (In this case, some advocate
defining and initializing a local pointer of the correct type and
taking advantage of the implicit conversion between void* and other
object pointers.)
 
B

Barry Schwarz

I'd add: passing a T ** (or a T *const *) to a function that wants a
const T *const * parameter.

Is there not a one way implicit conversion adding as many const as
needed? A T* will be converted to const T* with no problem (e.g.,
strcpy). Is a T** different?
 
K

Kenneth Brody

santosh said:
(e-mail address removed) wrote: [...]
Memory was allocated at address(X mod.) 00000004
Memory was allocated at address(p mod.) 1DD5:0004

Note that, by treating the pointer as an int, you lost the top 16
bits of the value. (This is why the cast on malloc was a bad idea,
since it hid the int-to-pointer warning due to the missing include.)

[...]
That's an implementation decision. Under the x86 segmented memory model,
a segment can begin at any "paragraph boundary", which is any address
that is a multiple of 16. Segments do not have a minimum size and they
can perfectly overlap. Your compiler has merely chosen to place the
second BEAM_PROFILE object on the next higher segment address, instead
of at a higher offset within the same segment.
[...]

This also allows the most memory to be available to allocate for
the object, since 64K-4 bytes is available in any segment (that is,
if the memory is available at all). It also simplifies the runtime
library, since there's no overhead in figuring out what to do with
allocations in the middle of a segment, merging freed blocks, and
so on. (Plus, I suppose, the library chould check that the offset
to anything passed to free() is 4, because anything else is invalid.)

--
+-------------------------+--------------------+-----------------------+
| Kenneth J. Brody | www.hvcomputer.com | #include |
| kenbrody/at\spamcop.net | www.fptech.com | <std_disclaimer.h> |
+-------------------------+--------------------+-----------------------+
Don't e-mail me at: <mailto:[email protected]>
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,755
Messages
2,569,537
Members
45,023
Latest member
websitedesig25

Latest Threads

Top