Koraxen
asked on
Dynamic/Double Mem Allocation
Hey guys!
I found an interesting snippet of C code, which once implemented, would aid one's understanding of C memory allocation. The requirement involves coming up with implementations for the function declarations below, followed by a test program. The implementations should validate: DEF my_def = CreateDEF(); my_def->abc->c = 5;
void FreeDEF(ABC *pabc);
DEF *CreateDEF(void);
typedef struct DEF {
ABC *abc;
int d;
int e;
int f;
};
typedef struct ABC {
int a;
int b;
int c;
};
I found an interesting snippet of C code, which once implemented, would aid one's understanding of C memory allocation. The requirement involves coming up with implementations for the function declarations below, followed by a test program. The implementations should validate: DEF my_def = CreateDEF(); my_def->abc->c = 5;
void FreeDEF(ABC *pabc);
DEF *CreateDEF(void);
typedef struct DEF {
ABC *abc;
int d;
int e;
int f;
};
typedef struct ABC {
int a;
int b;
int c;
};
allocate memory for a void*v with sizeof(ABC) + sizeof(DEF);
void *v = malloc(sizeof (ABC) + sizeof(DEF));
assign v's address + sizeof(ABC) to *oDEF
DEF *d = (DEF*) ((char*)v + sizeof(ABC));
then assign v's address to oDef->abc
d->abc = (ABC*)v ;
now, the whole memory is pointed by d->abc.
so when you pass this to FreeDEF, expected memory will get destroyed.
NOTE: i dont find any use of this!!? anyway its good for learning
void *v = malloc(sizeof (ABC) + sizeof(DEF));
assign v's address + sizeof(ABC) to *oDEF
DEF *d = (DEF*) ((char*)v + sizeof(ABC));
then assign v's address to oDef->abc
d->abc = (ABC*)v ;
now, the whole memory is pointed by d->abc.
so when you pass this to FreeDEF, expected memory will get destroyed.
NOTE: i dont find any use of this!!? anyway its good for learning
ASKER
The requirement is to use a single malloc. It has no practical use, except to get acquainted malloc, pointers, etc.
It looks like abith proposed a solution though, but I haven't understood it yet.
Abith, is this what you are suggesting? :
It looks like abith proposed a solution though, but I haven't understood it yet.
Abith, is this what you are suggesting? :
DEF *CreateDEF (void) {
void *v = malloc(sizeof (ABC) + sizeof(DEF));
DEF *d = (DEF*) ((char*)v + sizeof(ABC));
d->abc = (ABC*)v ;
}
void FreeDEF (ABC *pabc) {
}
you forgot the return value:
DEF *CreateDEF (void) {
void *v = malloc(sizeof (ABC) + sizeof(DEF));
DEF *d = (DEF*) ((char*)v + sizeof(ABC));
d->abc = (ABC*)v ;
return d; /* <---- this */
}
but will be a bit cleaner:
DEF *CreateDEF (void) {
ABC *abc = (ABC *)malloc(sizeof (ABC) + sizeof(DEF));
DEF *def = (DEF*)((char*)abc + sizeof(ABC));
def->a = abc;
return def;
}
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
>>Abith, is this what you are suggesting? :
Yes
Yes
With returning d
DEF *CreateDEF (void) {
.....
return d;
}
DEF *CreateDEF (void) {
.....
return d;
}
>> It has no practical use
Oh, it does. It avoids having to call malloc more than once. Allocating memory has a certain overhead ... If you can make one call instead of two, then you won some time. If you can do 1 call instead of 1000, then you've won quite a bit of time.
For performance critical applications that need to allocate a lot of small blocks of memory, this is an often used technique.
Oh, it does. It avoids having to call malloc more than once. Allocating memory has a certain overhead ... If you can make one call instead of two, then you won some time. If you can do 1 call instead of 1000, then you've won quite a bit of time.
For performance critical applications that need to allocate a lot of small blocks of memory, this is an often used technique.
SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
putting them in a struct ensures that any alignment requirements are satisfied.
the assignments and ops seem equivalent.
the check for def_p!=NULL properly avoids undefined behaviour in CreateDEF
but it doesn't help if it would then be used as
// DEF *pdef = CreateDEF ();
// pdef->abc->a = 10;
the assignments and ops seem equivalent.
the check for def_p!=NULL properly avoids undefined behaviour in CreateDEF
but it doesn't help if it would then be used as
// DEF *pdef = CreateDEF ();
// pdef->abc->a = 10;
>putting them in a struct ensures that any alignment requirements are satisfied.
If your compiler is aligning, then also the sizeof operator rounds up the size of structs to guarantee proper alignments, if it's not aligning then even putting them on a struct doesn't align them. So it shouldn't make any difference at all. Can you come up with an example where it actually makes a difference?
>the assignments and ops seem equivalent.
Agree
>the check for def_p!=NULL properly avoids undefined behaviour in CreateDEF
>but it doesn't help if it would then be used as
Agree on this too, but in general the idea is to write your functions so that they always work ok, and leave it to the user to decide whether to checks for exceptions or not (for example you may decide not to check for null pointers in sample or test code - as in the case of this post -, but of course you do check in production code)
If your compiler is aligning, then also the sizeof operator rounds up the size of structs to guarantee proper alignments, if it's not aligning then even putting them on a struct doesn't align them. So it shouldn't make any difference at all. Can you come up with an example where it actually makes a difference?
>the assignments and ops seem equivalent.
Agree
>the check for def_p!=NULL properly avoids undefined behaviour in CreateDEF
>but it doesn't help if it would then be used as
Agree on this too, but in general the idea is to write your functions so that they always work ok, and leave it to the user to decide whether to checks for exceptions or not (for example you may decide not to check for null pointers in sample or test code - as in the case of this post -, but of course you do check in production code)
sizeof(DEF) accounts for alignment requirements of DEF, but not alignment requirements of ABC
In this case, the requirements are probably at least as strict on most implementations, so this example shouldn't make an actual difference.
To be safe if the requirement is to be used as
// DEF *pdef = CreateDEF ();
// pdef->abc->a = 10;
the function should probably exit if the malloc fails.
(BTW, why is my comment marked as "Proposed Solution"?)
In this case, the requirements are probably at least as strict on most implementations, so this example shouldn't make an actual difference.
To be safe if the requirement is to be used as
// DEF *pdef = CreateDEF ();
// pdef->abc->a = 10;
the function should probably exit if the malloc fails.
(BTW, why is my comment marked as "Proposed Solution"?)
ASKER
Not sure why your post is marked as proposed solution ozo, I havent marked anything yet.
And would the free method just require doing what's in the snippet below?
Or should it be more complicated?
And would the free method just require doing what's in the snippet below?
Or should it be more complicated?
void FreeDEF (ABC *pabc) {
free(pabc);
}
Yes, since you allocated it with a single call to malloc, you just need to call "free" once.
However, since the function in named FreeDEF, it is probably supposed to be passed a pointer to DEF not to ABC. Also, be carefull to call free() passing it the pointer as returned by malloc.
That is, if you use my code, than you shoud call "free(pdef)", which is passing the same pointer returned by CreateDEF() as in:
void FreeDEF(DEF *pdef) {
free(pdef);
}
main() {
...
DEF *pdef = CreateDEF ();
pdef->abc->a = 10;
FreeDEF(pdef);
...
If you use Ozo's version, you should call "free(pdef->abc)" (more confusing?) as in
void FreeDEF(DEF *pdef) {
free(pdef->abc);
}
main() {
...
DEF *pdef = CreateDEF ();
pdef->abc->a = 10;
FreeDEF(pdef);
...
However, since the function in named FreeDEF, it is probably supposed to be passed a pointer to DEF not to ABC. Also, be carefull to call free() passing it the pointer as returned by malloc.
That is, if you use my code, than you shoud call "free(pdef)", which is passing the same pointer returned by CreateDEF() as in:
void FreeDEF(DEF *pdef) {
free(pdef);
}
main() {
...
DEF *pdef = CreateDEF ();
pdef->abc->a = 10;
FreeDEF(pdef);
...
If you use Ozo's version, you should call "free(pdef->abc)" (more confusing?) as in
void FreeDEF(DEF *pdef) {
free(pdef->abc);
}
main() {
...
DEF *pdef = CreateDEF ();
pdef->abc->a = 10;
FreeDEF(pdef);
...
ASKER
An extension to this question would now include running the following code:
What does the last line do? I'm unsure about what *(int*) means.
What does the last line do? I'm unsure about what *(int*) means.
DEF *pdef = CreateDEF();
int value;
value = (int) pdef;
value += 8;
*(int *) value = 10;
(int*) casts value into a pointer to an integer
and the * dereferences that pointer
and the * dereferences that pointer
>> What does the last line do?
It interpretes value as a pointer to int, then dereferences it, and stores 10 in there.
It is quite "dangerous", and not recommended to do this though. Especially because of this line :
value = (int) pdef;
It interpretes value as a pointer to int, then dereferences it, and stores 10 in there.
It is quite "dangerous", and not recommended to do this though. Especially because of this line :
value = (int) pdef;
maybe you meant to say value += offsetof(DEF,f)
Anyway the effect is that initially value is the memory address of pdef, and since "d" is the first member of pdef (so it has the same memory address of pdef) value is also the memory address of pdef->d.
Then you add 8 to value, and since each integer (on a 32bit machine) takes 4 bytes, adding 8 essentially getting the memory address of "f". In other words, member "d" is at the same memory address as the def struct, "e" is stored 4 bytes above that, and "f" is 4 bytes further above "e", so it is 8 bytes above the start of the def struct.
So now the value variable has the address of pdef->f.
Finally you cast value to a pointer to integer and dereference it, which means that you are storing value 10 at the address stored in value, and the overall net effect is the same as
pdef->f=10;
that is, you set the f member of def to 10.
Then you add 8 to value, and since each integer (on a 32bit machine) takes 4 bytes, adding 8 essentially getting the memory address of "f". In other words, member "d" is at the same memory address as the def struct, "e" is stored 4 bytes above that, and "f" is 4 bytes further above "e", so it is 8 bytes above the start of the def struct.
So now the value variable has the address of pdef->f.
Finally you cast value to a pointer to integer and dereference it, which means that you are storing value 10 at the address stored in value, and the overall net effect is the same as
pdef->f=10;
that is, you set the f member of def to 10.
SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
There are some imprecisions. a) I never said that sizeof rounds up to a multiple of 16. It rounds up to whatever your compiler is aligning. b) as you see, in ozo container struct, the abc struct is not aligned to a multiple of 16; indeed it's not aligning more than the sizeof operator rounds up. c) the address 0x12fde8 is not very meaningful as the fact that it's a multiple of something doesn't mean that the compiler placed it on that alignment on a purpose. d) as the def struct is a multiple of 8, you cannot use it to see if the compiler is aligning to a multiple of 8 or anything smaller than that as this struct would align naturally to 8 even if the compiler doesn't do anything.
>>>> It rounds up to whatever your compiler is aligning
Maybe I still misunderstand you but I can can set struct aligning to 16 bytes and nevertheless I get a size of 12 for a struct with 3 integers. There is no 'round up'; it simply is the size of the struct.
>>>> as you see, in ozo container struct, the abc struct is not aligned to a multiple of 16;
That was a mistake by me. I played wit 1 byte alignment when posting these addresses. With 16 byte alignment I got 0x12fddc for 'def' member and 0x12fdfc for 'abc' member and a size of 24 for the 'def'.
>>>> c) .... d) ....
I fear that these statements also were based on my wrong addresses.
Note, the results mean that your code is fully valid, as each member correctly was aligned within the structure it was defined in, regardless whether the start address of the struct is a multiple of the alignment or not.
Maybe I still misunderstand you but I can can set struct aligning to 16 bytes and nevertheless I get a size of 12 for a struct with 3 integers. There is no 'round up'; it simply is the size of the struct.
>>>> as you see, in ozo container struct, the abc struct is not aligned to a multiple of 16;
That was a mistake by me. I played wit 1 byte alignment when posting these addresses. With 16 byte alignment I got 0x12fddc for 'def' member and 0x12fdfc for 'abc' member and a size of 24 for the 'def'.
>>>> c) .... d) ....
I fear that these statements also were based on my wrong addresses.
Note, the results mean that your code is fully valid, as each member correctly was aligned within the structure it was defined in, regardless whether the start address of the struct is a multiple of the alignment or not.
ASKER
My end solution put together:
void FreeDEF (ABC *pabc) {
free(pabc); // Release resources
}
DEF *CreateDEF(void) {
void *my_mem = malloc(sizeof (ABC) + sizeof(DEF)); // Double struct mem allocation
DEF *my_def = (DEF)( sizeof(ABC) + (char*)my_mem );
my_def->abc = (ABC*)my_mem ; // Provide memory for ABC
return my_def; // Return DEF
}
void FreeDEF (ABC *pabc) {
free(pabc); // Release resources
}
DEF *CreateDEF(void) {
void *my_mem = malloc(sizeof (ABC) + sizeof(DEF)); // Double struct mem allocation
DEF *my_def = (DEF)( sizeof(ABC) + (char*)my_mem );
my_def->abc = (ABC*)my_mem ; // Provide memory for ABC
return my_def; // Return DEF
}
this will work perfectly.
do you get any issue?
do you get any issue?
>>>> My end solution put together:
That is pretty much the same as lbertacco posted above? And where most others refered to. What was the reason for reopening the thread?
That is pretty much the same as lbertacco posted above? And where most others refered to. What was the reason for reopening the thread?
DEF *my_def = (DEF)( sizeof(ABC) + (char*)my_mem );
Make that :
DEF *my_def = (DEF*)( sizeof(ABC) + (char*)my_mem );
Make that :
DEF *my_def = (DEF*)( sizeof(ABC) + (char*)my_mem );
ASKER
Was reopened because I was given additional requirements/constraints about the question, which I'm including in the final solution here:
DEF *CreateDEF(void) {
void *my_mem = (void*)malloc(sizeof(ABC) * sizeof(DEF)); // Double struct mem allocation
DEF *my_def = (DEF)( (int*)my_mem * 32 );
my_def->abc = (ABC*)my_mem ; // Provide memory for ABC
return my_def; // Return DEF
}
DEF *CreateDEF(void) {
void *my_mem = (void*)malloc(sizeof(ABC) * sizeof(DEF)); // Double struct mem allocation
DEF *my_def = (DEF)( (int*)my_mem * 32 );
my_def->abc = (ABC*)my_mem ; // Provide memory for ABC
return my_def; // Return DEF
}
>>>> DEF *my_def = (DEF)( (int*)my_mem * 32 );
The statement doesn't compile for three reasons:
- a int* cannot be multiplied with an int
- a int* cannot be casted to a struct DEF
- a struct DEF cannot be casted to a DEF*
You most likely you want
DEF *my_def = (DEF*)( (char*)my_mem + sizeof(ABC) );
But then you have to free with
free(m_def ->abc);
what works but is strange and error-prone.
>>>> (int*)my_mem * 32
if you multiply a pointer with 32 the resulting address is normally invalid. Can you explain?
The statement doesn't compile for three reasons:
- a int* cannot be multiplied with an int
- a int* cannot be casted to a struct DEF
- a struct DEF cannot be casted to a DEF*
You most likely you want
DEF *my_def = (DEF*)( (char*)my_mem + sizeof(ABC) );
But then you have to free with
free(m_def ->abc);
what works but is strange and error-prone.
>>>> (int*)my_mem * 32
if you multiply a pointer with 32 the resulting address is normally invalid. Can you explain?
I overlooked one more error:
>>>> malloc(sizeof(ABC) * sizeof(DEF));
Here you multiplied the sizes of both structs what never is a valid size for DEF + ABC (even for arrays of ABC).
Why not
DEF *CreateDEF(void) {
DEF *my_def = (DEF*)malloc(sizeof(DEF) + sizeof(ABC) * ); // Double struct mem allocation
my_def->abc = (ABC*)((char*)my_def + sizeof(DEF)); // Provide memory for ABC
return my_def; // Return DEF
}
as we already had it a few times above?
>>>> malloc(sizeof(ABC) * sizeof(DEF));
Here you multiplied the sizes of both structs what never is a valid size for DEF + ABC (even for arrays of ABC).
Why not
DEF *CreateDEF(void) {
DEF *my_def = (DEF*)malloc(sizeof(DEF) + sizeof(ABC) * ); // Double struct mem allocation
my_def->abc = (ABC*)((char*)my_def + sizeof(DEF)); // Provide memory for ABC
return my_def; // Return DEF
}
as we already had it a few times above?
Did you fix the error I pointed out in my previous post ?
It wont be an elegant solution.
The most common solution will be use 2 mallocs, first a malloc() to create the DEF structure, and then a second malloc to create the ABC structure, finally store the pointer to ABC into the DEF.
For this approach, FreeDEF() function should have two free() calls.
For this specific example, to store ABC as a pointer makes no much sense, would be simpler:
typedef struct {
int d;
int e;
int f;
ABC abc; /* without a pointer */
} DEF;
so you can create with a single malloc. Waiting for your feedback...