0

I have an unnamed structure called `FooStruct', and I wrote a function for it that initializes all its variables and also takes care of dynamic memory allocation.

I tried running this code, but it is not producing the results that I am expecting. For some reason, the variables are initialized correctly when inside the function, but then they change again once the block is exited.

A project that I'm working on requires that I use unnamed structs only. Why is this happening?

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

typedef struct {
    int fooPrimitive;
} FooStruct;

void FooStructInit(FooStruct * ptr, int num) {
    ptr = (FooStruct*)malloc(sizeof(FooStruct));
    ptr -> fooPrimitive = num;

    // Expected output: 5
    // Actual output: 5
    printf("ptr -> fooPrimitive: %d\n", (ptr -> fooPrimitive));
}

int main() {
    FooStruct * ptr;
    FooStructInit(ptr, 5);

    // Expected output: 5
    // Actual output: some random number
    printf("FooStruct -> fooPrimitive: %d\n", (ptr -> fooPrimitive));
}
zxgear
  • 1,168
  • 14
  • 30
  • 2
    You shouldn't use spaces around the arrow `->` or dot `.` operators. They bind very tightly; that should be shown visually by not including any spaces around them. (Yes, in terms of syntax, spaces are fine; spaces are allowed anywhere between tokens, so you could write each token on its own line. For readability, don't include spaces around arrow or dot.) – Jonathan Leffler Oct 18 '15 at 20:53
  • Thanks for telling me that, even though they don't really detract from the problem. – zxgear Oct 18 '15 at 20:58
  • YW: that's why it is a comment, not an answer. – Jonathan Leffler Oct 18 '15 at 20:58

2 Answers2

2

When you pass ptr to FooStructInit, it is copied and this copy is assigned the return value of malloc then. This is called pass-by-value. Instead, pass a pointer to ptr to actually write to ptr and not just the passed argument:

void FooStructInit(FooStruct** ptr, int num) {
    *ptr = malloc(sizeof(FooStruct));
    (*ptr)->fooPrimitive = num;

    // Expected output: 5
    // Actual output: 5
    printf("(*ptr)->fooPrimitive: %d\n", ((*ptr)->fooPrimitive));
}

int main() {
    FooStruct* ptr;
    FooStructInit(&ptr, 5);

    // Expected output: 5
    // Actual output: 5
    printf("FooStruct->fooPrimitive: %d\n", (ptr->fooPrimitive));
}

Notes:

Community
  • 1
  • 1
cadaniluk
  • 15,027
  • 2
  • 39
  • 67
2

C only has pass by value semantics, so when you pass a pointer to a function, the original value of the pointer is not changed, only the local copy is changed in the function.

The way to get around this is to pass a pointer to a pointer, so that you can change the value at the original memory location.

void FooStructInit(FooStruct **ptr, int num) {
    *ptr = malloc(sizeof(FooStruct));
    ...
}

int main(void) {
    FooStruct * ptr;
    FooStructInit(&ptr, 5); /* get the address of the pointer */
    ...
}
PC Luddite
  • 5,883
  • 6
  • 23
  • 39
  • Is this 100% impossible to do with only a `FooStruct * ptr` parameter in my function? – zxgear Oct 18 '15 at 20:53
  • @JohnH No, it's not possible. The function can only modify its local copy. – PC Luddite Oct 18 '15 at 20:56
  • 1
    @JohnH: Yes, it is 100% impossible with just a `FooStruct *ptr` parameter. You could do without the parameter and return the allocated structure; that would be legitimate. Or you can have the calling code do the memory allocation and simply have the constructor initialize the data in the previously allocated storage. This is how a C++ constructor works. The advantage is that you can then construct local variables and global variables of this type; you aren't forcing people to use dynamically allocated memory. – Jonathan Leffler Oct 18 '15 at 20:57