Link to home
Start Free TrialLog in
Avatar of Koraxen
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;
};
Avatar of Jaime Olivares
Jaime Olivares
Flag of Peru image

is this a homework? the single malloc requirement is mandatory?
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...
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
Avatar of Koraxen
Koraxen

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? :

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) {
 
}

Open in new window

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 */
}

Open in new window

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;
}

Open in new window

ASKER CERTIFIED SOLUTION
Avatar of abith
abith
Flag of India image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
>>Abith, is this what you are suggesting? :
Yes
With returning  d

DEF *CreateDEF (void) {
.....
return d;
}
Avatar of Infinity08
>> 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.
SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
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;
>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)
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"?)
Avatar of Koraxen

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?
void FreeDEF (ABC *pabc) {
    free(pabc);
}

Open in new window

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);
...

Avatar of Koraxen

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.
DEF *pdef = CreateDEF();
int value;
 
value = (int) pdef;
value += 8;
 
*(int *) value =  10;

Open in new window

(int*) casts value into a pointer to an integer
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;
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.
SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
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.
Avatar of Koraxen

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
}

this will work perfectly.

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?
     DEF *my_def = (DEF)( sizeof(ABC) + (char*)my_mem );

Make that :

      DEF *my_def = (DEF*)( sizeof(ABC) + (char*)my_mem );
Avatar of Koraxen

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 *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?


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?

Did you fix the error I pointed out in my previous post ?