0

I wonder why after my malloc all modifications aren't working. Here is the code I used to illustrate this :

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

struct Age {
   unsigned int age : 16;
   unsigned int two : 2;
   unsigned int notToBeInitialed: 2;
};

int init(struct Age * p){
 p = (struct Age *) malloc( sizeof(struct Age) );
 p->age = 5;
 return 0;
}

int change(struct Age * p){
   p->age = 99; 
}

int getValue(struct Age * p){
  return p->age;    
}

int main(void) {
    struct Age test;
    init(&test);
    printf( "Age.age : %d\n", getValue(&test) ); // gives me 0 , expected 5
    change(&test);
    printf( "Age.age : %d\n", getValue(&test) ); // gives me 99 
    return 0;
}

What have I done wrong ?

Thanks for your help.

Source : http://www.tutorialspoint.com/cprogramming/c_bit_fields.htm Ideone : https://ideone.com/O59tqZ

jy95
  • 773
  • 13
  • 36
  • thanks everyone : you all got your +1 because all your answers are true. I think Daniel Trugman's answer is more clear . I probably mix up this concept with another language ^^ – jy95 Sep 30 '17 at 21:37

4 Answers4

2

Your call to malloc overwrites the argument that was received. So the initialization goes to that new object, which you then leak when the function returns.

It seems that you are confusing initialization and allocation.

Remove the malloc from your init function.

Then you can simply allocate and init in two steps. A good idiom would be

struct Age* init(struct Age * p){
 if (p) {
   p->age = 5;
 }
 return p;
}

....

struct Age* testp = init(malloc(sizeof *testp));

BTW, the return of malloc must not and should not be cast in C.

Jens Gustedt
  • 76,821
  • 6
  • 102
  • 177
  • thanks for the answer : How can I do my malloc and then set up a field ? – jy95 Sep 30 '17 at 21:13
  • thanks for the edit. The problem is that I must do it in a single function :( – jy95 Sep 30 '17 at 21:21
  • So you really don't have an idea how to put that into a single function? That should not be too difficult, but I wouldn't do that last step of your homework for you. And, a function that does an allocation should never be called `init`. – Jens Gustedt Sep 30 '17 at 21:23
2

You clearly misunderstand the difference between automatic storage and dynamic memory allocation.

Local variables

struct Age test

Defines an Age object using automatic storage. You don't need to do anything else, this object exists and is now usable. The only downside to declaring such objects is that they exists throughout their declaration scope. You should not call init (at least not the malloc part) on this object, since it is already initialized, just set it's value using test.age.

The following will work as expected:

int main(void) {
    struct Age test;
    test.age = 5;
    printf( "Age.age : %d\n", getValue(&test) );
    change(&test);
    printf( "Age.age : %d\n", getValue(&test) );
    return 0;
}

Dynamic memory allocation

On the other hand, the following:

p = (struct Age *) malloc( sizeof(struct Age) );

Uses dynamic memory allocation. New memory is allocated and then a pointer is returned to you. In this case you decide to use this memory for holding an Age object.

int init(struct Age ** p){
    *p = (struct Age *) malloc( sizeof(struct Age) );
    (*p)->age = 5;
}

int main(void) {
    struct Age * test;
    init(&test)
    printf( "Age.age : %d\n", getValue(test) );
    change(test);
    printf( "Age.age : %d\n", getValue(test) );
    return 0;
}

Summary

These two methods are mutualy exclusive. When you are creating a local variable there is not point in allocating additional memory for it.

Daniel Trugman
  • 8,186
  • 20
  • 41
  • Thanks! I really don't need a compiler for this kind of code... but on the other hand, I did miss that :) – Daniel Trugman Sep 30 '17 at 21:29
  • I've learned the hard way to be cautious. I sometimes post an answer before compiling it. I usually compile it anyway just as soon as I can, and I'm disappointed by how often I have to go back and amend the answer. – Jonathan Leffler Sep 30 '17 at 21:30
1

There seem to be a few misunderstandings in this code. First, you allocate an Age struct on the stack inside your main function. You then pass a pointer to this into your init function. So far so good. But you then allocate memory for a new Age struct inside the init function. You modify the contents of this new version, but the pointer to it is only stored inside the local 'p' variable inside the 'init' function, it is not passed back in any way. When you print the contents, you are printing the stack-allocated Age struct, not the one allocated with malloc that you made the changes to.

If you want to pass a reference to your stack-allocate struct into init, and have init modify that, then you should remove the malloc line completely.

If you wish to have your init function allocate a new Age struct, then it will need to take 'struct Age** p' as its argument, to allow it to pass out a 'struct Age* p', in the following manner:



    void init(struct Age** p){
        *p = (struct Age *)malloc( sizeof(struct Age) );
        (*p)->age = 5;
    }

    int main(void) {
        struct Age* test;
        init(&test);
        printf( "Age.age : %d\n", test->age );
    }

I also changed the return type of the init function to void, since the return value seems to serve no purpose. You should probably also change the return type of the 'change' function to void since it doesn't seem to return any value.

Sean Burton
  • 907
  • 7
  • 15
  • 1
    The allocation of the `test` object in the original example doesn't allocate an Age struct on the heap (in the main function), it is a local variable... – Daniel Trugman Sep 30 '17 at 21:31
  • Sorry I meant to say the stack not the heap – Sean Burton Sep 30 '17 at 21:41
  • This is not accurate as well. It defines the variable using automatic storage, which might be implemented using a stack-typed memory, but it is implementation defined. See my [answer here](https://stackoverflow.com/questions/6159968/declaring-array-of-int/46329105#46329105) to see an exact explanation. – Daniel Trugman Sep 30 '17 at 21:46
1

You are passing pointer by value to function init. This means that function operates on the copy of that pointer. In this case you are assigning a new address to it but original pointer doesn't get changed. If you want to assign pointer a new value which would remain after function return, you have to pass to the function the address of the pointer (pointer to pointer).

But in your example, in main, you are instantiating struct Age on the stack. You can't change its address (location) by allocating memory on the heap (with malloc). All you can do is to create completely a new object on the heap (and that's what you do in init). But you have memory leak in that function as malloc-ed memory is never freed.

Bojan Komazec
  • 9,216
  • 2
  • 41
  • 51