Yet another free() question.

L

lawtrevor

I have a question regarding the use of free() based on some code I need
to decipher:

{
struct A {<A fields>};
struct B {<A fields> + <Bfields>};

typedef struct A Aobj;
typedef struct B Bobj;

Aobj *ptrToA = (struct A*) calloc(1,sizeof(struct B));

free(ptrToA);
}

Will this free the entire memory block of size sizeof(struct B), or
only a block starting at ptrToA of size sizeof(struct A)? The posts I
have read strongly suggest this will, as probably intended, free the
entire block produced by calloc(), but in those posts it seems
sizeof(Aobj) == N*sizeof(Bobj), i.e. the size of the allocated block is
an integer multiple of the dereferenced type of the pointer to which
the allocated block's void pointer is cast (Don't know how to express
this well). Such would be the case when allocating a single instance
of an object or an array of the same objects on the heap. Just want to
make sure what is done above is safe (assuming the type interpretation
of the allocated block elsewhere is correct.)

Thank you for your time.
 
S

Spiros Bousbouras

I have a question regarding the use of free() based on some code I need
to decipher:

{
struct A {<A fields>};
struct B {<A fields> + <Bfields>};

typedef struct A Aobj;
typedef struct B Bobj;

Aobj *ptrToA = (struct A*) calloc(1,sizeof(struct B));

free(ptrToA);
}

Will this free the entire memory block of size sizeof(struct B), or
only a block starting at ptrToA of size sizeof(struct A)? The posts I
have read strongly suggest this will, as probably intended, free the
entire block produced by calloc(),

Yes , that's what it will do. It is worth noting that if the
calloc succeeded then it allocated memory which is *at
least* as large as sizeof(struct B) , it doesn't have to be
exactly sizeof(struct B).
but in those posts it seems
sizeof(Aobj) == N*sizeof(Bobj), i.e. the size of the allocated block is
an integer multiple of the dereferenced type of the pointer to which
the allocated block's void pointer is cast (Don't know how to express
this well).

I will guess that by "dereferenced type of the pointer" you
mean the type of object the pointer points to. In any case
whether sizeof(Aobj) == N*sizeof(Bobj) holds or whether
sizeof(*Aobj) == N*sizeof(*Bobj) holds is irrelevant , the
free() will free all the memory (if any) which was allocated
by calloc.
Such would be the case when allocating a single instance
of an object or an array of the same objects on the heap.

Since the presence of a heap is up to an implementation
it's best not use the term. If you mean memory obtained
through the malloc family of functions then that's what
you should say.

Having said that , which case are you referring to ? The
formula sizeof(Aobj) == N*sizeof(Bobj) you gave mentions
2 different object types.
Just want to
make sure what is done above is safe (assuming the type interpretation
of the allocated block elsewhere is correct.)

I don't know what "the type interpretation of the
allocated block" is. mallocing memory and freeing
it right away is safe but obviously pointless. Whether
your code is safe depends on what's happening between
the calloc() and the free().
 
K

Keith Thompson

I have a question regarding the use of free() based on some code I need
to decipher:

{
struct A {<A fields>};
struct B {<A fields> + <Bfields>};

typedef struct A Aobj;
typedef struct B Bobj;

Aobj *ptrToA = (struct A*) calloc(1,sizeof(struct B));

free(ptrToA);
}

Will this free the entire memory block of size sizeof(struct B), or
only a block starting at ptrToA of size sizeof(struct A)? The posts I
have read strongly suggest this will, as probably intended, free the
entire block produced by calloc(), but in those posts it seems
sizeof(Aobj) == N*sizeof(Bobj), i.e. the size of the allocated block is
an integer multiple of the dereferenced type of the pointer to which
the allocated block's void pointer is cast (Don't know how to express
this well). Such would be the case when allocating a single instance
of an object or an array of the same objects on the heap. Just want to
make sure what is done above is safe (assuming the type interpretation
of the allocated block elsewhere is correct.)

Yes.

The malloc/calloc/realloc/free subsystem does not, and cannot,
keep track of what you do with the newly allocated pointer after
its value is returned to you. Suppose sizeof(struct A) == 10,
and sizeof(struct b) == 20. In your code, calloc (if successful)
simply returns a void* pointer to 20 bytes of newly allocated memory;
it has no idea that the value 20 was computed as "sizeof(struct B),
or that you then converted the result from void* to struct A*.

When you call free(PtrToA), free() sees only an argument of type
void*, which happens to have the same value as what was returned by
calloc(), so it frees 20 bytes.

The sizeof, the cast, and the assignment are entirely invisible to
calloc() and free(), and cannot affect their behavior. So as far as
calloc() and free() are concerned, your code is equivalent to:

void *ptr = calloc(1, 20);
free(ptr);

Incidentally, the standard doesn't *quite* guarantee that the initial
fields of your struct A and struct B will be laid out consistently,
unless you happen to declare a union of the two types, though it's
difficult to imagine an implementation in which they wouldn't match.
You might consider making the first member of struct B be of type
struct A, rather than duplicating all the fields explicitly; this
would also avoid any errors that might be introduced by writing the
same sequence of member declarations twice,
 
F

Fred Kleinschmidt

I have a question regarding the use of free() based on some code I need
to decipher:

{
struct A {<A fields>};
struct B {<A fields> + <Bfields>};

typedef struct A Aobj;
typedef struct B Bobj;

Aobj *ptrToA = (struct A*) calloc(1,sizeof(struct B));

free(ptrToA);
}

Will this free the entire memory block of size sizeof(struct B), or
only a block starting at ptrToA of size sizeof(struct A)?
It will free the entire block that was allocated.
The signature of free is
void free( void *ptr )
It knows only that it was passed a pointer. It will free the memory
that was allocated by the malloc/calloc/realloc call, regardless of what
that pointer may have been cast to.

Note that
Aobj *ptrToA = (struct A*) calloc(1,sizeof(struct B));

is equivalent to
Aobj *ptrTob = calloc(1,sizeof(struct B));
Aobj *ptrToA = (struct A*) ptrTob;
 
S

Spiros Bousbouras

Keith said:
Incidentally, the standard doesn't *quite* guarantee that the initial
fields of your struct A and struct B will be laid out consistently,
unless you happen to declare a union of the two types, though it's
difficult to imagine an implementation in which they wouldn't match.
You might consider making the first member of struct B be of type
struct A, rather than duplicating all the fields explicitly; this
would also avoid any errors that might be introduced by writing the
same sequence of member declarations twice,

Since what is returned by calloc() is guaranteed to
be properly alligned for any type I don't see what
might go wrong even in theory. Unless you're saying
that sizeof(struct A) may be larger than sizeof(struct B).
 
D

David T. Ashley

I have a question regarding the use of free() based on some code I need
to decipher:

{
struct A {<A fields>};
struct B {<A fields> + <Bfields>};

typedef struct A Aobj;
typedef struct B Bobj;

Aobj *ptrToA = (struct A*) calloc(1,sizeof(struct B));

free(ptrToA);
}

Will this free the entire memory block of size sizeof(struct B), or
only a block starting at ptrToA of size sizeof(struct A)?

malloc(), calloc(), and free() are block-oriented only. They operate only
in terms of a starting address and a length. There is no type information
involved.

The code in calloc() has no way to determine what calculations went into the
parameters it was passed. They are just integers.

The whole block gets free()'d.
 
R

Richard

David T. Ashley said:
malloc(), calloc(), and free() are block-oriented only. They operate only
in terms of a starting address and a length. There is no type information
involved.

Can someone explain again why people keep saying (in this NG) that ints or pointers
to chars etc might be stored in different memory blocks? And if they
are, how would one possibly use malloc?
 
S

Spiros Bousbouras

Richard said:
Can someone explain again why people keep saying (in this NG) that ints or pointers
to chars etc might be stored in different memory blocks? And if they
are, how would one possibly use malloc?

I don't know what "stored in different memory blocks"
means or whether it has been said in some thread but
noone has said it in this thread.
 
K

Keith Thompson

Spiros Bousbouras said:
Since what is returned by calloc() is guaranteed to
be properly alligned for any type I don't see what
might go wrong even in theory. Unless you're saying
that sizeof(struct A) may be larger than sizeof(struct B).

Yes, sizeof(struct A) may theoretically, given a sufficiently insane
implementation, be larger than sizeof(struct B), but that's not what I
had in mind.

The code in question was:
| {
| struct A {<A fields>};
| struct B {<A fields> + <Bfields>};
|
| typedef struct A Aobj;
| typedef struct B Bobj;
|
| Aobj *ptrToA = (struct A*) calloc(1,sizeof(struct B));
|
| free(ptrToA);
| }

I assumed that the reason for declaring struct B that way was to allow
the initial part of a struct B object to be treated as a struct A.
That's the only reason I can think of to allocate sizeof(struct B)
bytes.

There's one other thing I should mention here. calloc() initializes
the allocated memory to all-bits-zero. If struct A or struct B
contains members of some pointer or floating-point type, there's no
guarantee that this will set them to null pointers or 0.0,
respectively. It may be safer to use malloc() and then initialize the
members explicitly.

For example (ignoring the struct A vs. struct B business):

struct foo { /* ... */ };
const struct foo foo_zero = { 0 };
struct foo *ptr_to_foo = malloc(sizeof *ptr_to_foo);
if (ptr_to_foo == NULL) { /* OOPS! */ }
*ptr_to_foo = foo_zero;
 
K

Keith Thompson

Richard said:
Can someone explain again why people keep saying (in this NG) that
ints or pointers to chars etc might be stored in different memory
blocks? And if they are, how would one possibly use malloc?

I don't think anyone has said so in this thread, particularly in the
article to which you replied. I think David was referring to the
"block" of memory allocated by each call to calloc or malloc.
 
S

Spiros Bousbouras

Keith said:
Yes, sizeof(struct A) may theoretically, given a sufficiently insane
implementation, be larger than sizeof(struct B), but that's not what I
had in mind.

The code in question was:
| {
| struct A {<A fields>};
| struct B {<A fields> + <Bfields>};
|
| typedef struct A Aobj;
| typedef struct B Bobj;
|
| Aobj *ptrToA = (struct A*) calloc(1,sizeof(struct B));
|
| free(ptrToA);
| }

I assumed that the reason for declaring struct B that way was to allow
the initial part of a struct B object to be treated as a struct A.

So how would an insane (but conforming) implementation
prevent you from doing this ?
 
D

David T. Ashley

Keith Thompson said:
I don't think anyone has said so in this thread, particularly in the
article to which you replied. I think David was referring to the
"block" of memory allocated by each call to calloc or malloc.

Yep. What I meant in particular is that when you call free() with an
address, it has no idea why you originally wanted the memory, what you did
with the memory while you had it, what you believed that you might one day
be capable of doing with the memory, etc.

The memory is just a ____block____ with start address and size.

Whatever the size of _____block_____ allocated via malloc() or calloc() or
realloc() ... that is the size that gets free()'d. There are no partial
free()s. Soup can in ... soup can out. Bushel basket in ... bushel basket
out.
 
K

Keith Thompson

Spiros Bousbouras said:
So how would an insane (but conforming) implementation
prevent you from doing this ?

By laying out the members of struct A differently than the
corresponding members of struct B.

For example:

struct A {
int a0;
int a1;
};

struct B {
int a0;
int a1;
int b2;
};

a0 in both types must be at an offset of 0, but a compiler *could*
insert different amounts of padding between a0 and a1 for the two
types. The guarantee that the initial common subsequence of two
struct types has the same layout applies only when there's a union
with members of the two types.

Any sane implementation will lay them out the same way anyway.
There's no good reason to have different amounts of padding between
the members, and if you keep it the same, you don't have to keep track
of whether there's a union declared somewhere else.
 
S

Spiros Bousbouras

Keith said:
By laying out the members of struct A differently than the
corresponding members of struct B.

For example:

struct A {
int a0;
int a1;
};

struct B {
int a0;
int a1;
int b2;
};

a0 in both types must be at an offset of 0, but a compiler *could*
insert different amounts of padding between a0 and a1 for the two
types.

As long as sizeof(struct A) <= sizeof(struct B) I don't see
why that would be a problem. If you write p->a1 where
p is a pointer to A, the compiler would add the correct
padding for A, so as long as calloc supplied sufficient bytes
it would work.
 
K

Keith Thompson

Spiros Bousbouras said:
As long as sizeof(struct A) <= sizeof(struct B) I don't see
why that would be a problem. If you write p->a1 where
p is a pointer to A, the compiler would add the correct
padding for A, so as long as calloc supplied sufficient bytes
it would work.

As long as you use the allocated memory for only one type or the
other, and as long as it's big enough, there's no problem. The
problem shows up if you do type punning. For example (untested code):

void *p = malloc(ENOUGH);
((struct A*)p)->a1 = 42;
if (((struct B*)p)->a1 == 42) {
puts("ok");
}
else {
puts("oops");
}

Under a sane implementation, a1 will have the same offset in both
struct A and struct B, and the above will print "ok". Under an insane
but conforming implementation, a1 could have different offsets in the
two different types, and the code could print "oops" or invoke
undefined behavior.
 
M

Mark L Pappin

I don't believe it would be allowed to, within the /common initial
sequence/ between the two structures.
As long as you use the allocated memory for only one type or the
other, and as long as it's big enough, there's no problem. The
problem shows up if you do type punning. For example (untested code):

void *p = malloc(ENOUGH);
((struct A*)p)->a1 = 42;
if (((struct B*)p)->a1 == 42) {
puts("ok");
}
else {
puts("oops");
}

Under a sane implementation, a1 will have the same offset in both
struct A and struct B, and the above will print "ok". Under an insane
but conforming implementation, a1 could have different offsets in the
two different types, and the code could print "oops" or invoke
undefined behavior.

It is my understanding that 6.5.2.3 paragraph 5 forbids this
implicitly since, while it discusses only structures containing a
common initial sequence *as members of a union*, the compiler needs to
keep these offsets the same in case the next definition along is

union {
struct A a;
struct B b;
} foo;

at which point 'foo.a' and 'foo.b' must have the same layouts as any
other 'struct A' and 'struct B' respectively throughout the program
(so they could be 'memcpy()'ed, for instance). The only way an insane
but conforming implementation (IBCI? ISAGN) could have different
offsets for those otherwise-common initial sequences would be if it
*knew* that such a union was not used anywhere in anything the current
translation unit might be linked with, which is a pretty tall order.
I suppose that an IBCI might leave structure-member-offset allocation
until link time, though ...

mlp edant
 
L

lawtrevor

Thank you for the confirmation. The code that uses the struct-casting
construct is from a group's (fairly old) source, and since I am not
experienced in C, I don't want to modify anything I don't have to. In
the code, <A fields> is a preprocessor macro used to implement
inheritance.

Thanks again for your time!
 
K

Keith Thompson

Mark L Pappin said:
I don't believe it would be allowed to, within the /common initial
sequence/ between the two structures.


It is my understanding that 6.5.2.3 paragraph 5 forbids this
implicitly since, while it discusses only structures containing a
common initial sequence *as members of a union*, the compiler needs to
keep these offsets the same in case the next definition along is

union {
struct A a;
struct B b;
} foo;

at which point 'foo.a' and 'foo.b' must have the same layouts as any
other 'struct A' and 'struct B' respectively throughout the program
(so they could be 'memcpy()'ed, for instance). The only way an insane
but conforming implementation (IBCI? ISAGN) could have different
offsets for those otherwise-common initial sequences would be if it
*knew* that such a union was not used anywhere in anything the current
translation unit might be linked with, which is a pretty tall order.
I suppose that an IBCI might leave structure-member-offset allocation
until link time, though ...

Right, that's the kind of thing I had in mind. The possibility I was
thinking of was having both struct declarations in a block scope, so
other translation units wouldn't be an issue, but postponing layout
determination until link time (or run time!) would let you do this
kind of thing for global structs.

(Anyone who takes this too seriously should re-read the words "insane
(but conforming)" above.)

Incidentally, a few months ago somebody here (sorry, I don't remember
who) posted an account of some serious problems he ran into doing type
punning between two different structure types with common initial
subsequences. It *wasn't* the hypothetical layout problem we've been
discussing. As I recall, it was something like this:

struct foo {
int i;
char c0;
};
struct bar {
int i;
char c0;
char c1;
};
struct foo foo_obj;
struct bar bar_obj;

Accessing the members i and c0 works just fine, but something like
this:

*(struct foo*)&bar_obj = foo_obj;

could clobber bar_obj.c1, overwriting it with a padding byte from
foo_obj.
 
R

Richard Tobin

Keith Thompson said:
Incidentally, the standard doesn't *quite* guarantee that the initial
fields of your struct A and struct B will be laid out consistently,
unless you happen to declare a union of the two types, though it's
difficult to imagine an implementation in which they wouldn't match.

It's particularly difficult to imagine in the (usual) case of separate
compilation: a union of them might be declared in some other file.

-- Richard
 
K

Keith Thompson

It's particularly difficult to imagine in the (usual) case of separate
compilation: a union of them might be declared in some other file.

Agreed. It would be nice if the standard made the guarantee more
general.
 

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

Forum statistics

Threads
473,770
Messages
2,569,583
Members
45,073
Latest member
DarinCeden

Latest Threads

Top