Thread: structure pass by reference

  1. #1
    Registered User
    Join Date
    Dec 2015
    Posts
    16

    structure pass by reference

    Hello, I read this code is erroneous, which is related to the pass-by-reference issue. It seems to me each line of code is correct in syntax, and it compiles without any error/warning, but run with segmentation fault. I really do not know why.
    Can anybody elaborate the wrong part? Thanks a lot!
    Code:
    #include <stdio.h>
    #include <stdlib.h>
    
    struct node{
    int data;
    };
    
    void addnode(struct node *x)
    {
    x = (struct node* )malloc(sizeof(struct node));
    x->data = 9;
    }
    
    int main( )
    {
    struct node *N = NULL;
    addnode(N);
    printf("The value inside the struct = %d\n", N->data);
    return 0;
    }

  2. #2
    Registered User
    Join Date
    Jun 2015
    Posts
    1,640
    addnode is not modifying N in main. To do that you need to pass N's address, receive it as a pointer to a pointer in addnode, and dereference it there to set it in main.

  3. #3
    Registered User
    Join Date
    Jun 2011
    Posts
    4,513
    Also check that malloc has succeeded before attempting to use the memory, don't cast malloc, and be sure you free any memory you allocate.

    Your code should have indentation, as well.

  4. #4
    Registered User
    Join Date
    Dec 2015
    Posts
    16
    Thanks!
    OK, so I did:
    Code:
    // To do that you need to pass N's address, 
    addnode(&N);
    but still segmentation fault!
    Code:
    struct13_ptr_b.c:20:1: warning: passing argument 1 of ‘addnode’ from incompatible pointer type [enabled by default]
     addnode(&N);
    struct13_ptr_b.c:11:6: note: expected ‘struct node *’ but argument is of type ‘struct node **’
     void addnode(struct node *x)
    Could you be more specific with this part and elaborate the reason? I was warned that this is a common mistake, but I want to clarify the rudimentals of this.
    // receive it as a pointer to a pointer in addnode,
    Thanks!
    Last edited by Yifangt; 04-18-2016 at 04:14 PM.

  5. #5
    Registered User
    Join Date
    Jun 2015
    Posts
    1,640
    Code:
    #include <stdio.h>
    #include <stdlib.h>
     
    struct node{
        int data;
    };
     
    void addnode(struct node **x) {
        *x = malloc(sizeof(struct node));
        if (*x == NULL) { /* handle malloc error */ }
        (*x)->data = 9;
    }
     
    int main( ) {
        struct node *N = NULL;
        addnode(&N);
        printf("The value inside the struct = %d\n", N->data);
        return 0;
    }

  6. #6
    Registered User
    Join Date
    Dec 2015
    Posts
    16

    hard to understand "pointer to pointer for structure"

    Code:
    void addnode(struct node **x)        //This one is really hard to me!!
    Could you please break it down in more details? I only know it is "pointer to pointer" on reference stuff very vaguely, but why must it be like that with two layers of pointer? Thanks!
    Last edited by Yifangt; 04-18-2016 at 04:55 PM.

  7. #7
    Registered User
    Join Date
    Jun 2015
    Posts
    1,640
    The address of a variable is a pointer to whatever type that variable is. If the variable itself is already a pointer, then it's address will be a pointer to a pointer.
    Code:
    // Given
    struct node **x;
    //   x is a struct node **    i.e., it's a pointer to a pointer to a struct node
    //  *x is a struct node *     i,e., it's a pointer to a struct node
    // **x is a struct node       i.e., it's a struct node

  8. #8
    Registered User
    Join Date
    Dec 2015
    Posts
    16
    Thanks for your explanation on reading the expression "struct node **x".
    Something important is missing in my understanding, and I am not sure how to ask my question....
    Can I ask in another way: What is the memory manipulation for the correct code when addnote(struct node **x) is called with addnote(&N)?
    Code:
    addnote(struct node **x); // prototype, the parameter is a pointer-to-pointer of struct node;
    addnode(&N); //calling-by-reference, &N is the address of a struct node N;
    What I can't understand is: how *x (not **x ) matches &N from memory point of view.
    Thanks a lot!

  9. #9
    Registered User
    Join Date
    Apr 2016
    Posts
    21
    Quote Originally Posted by Yifangt View Post
    Code:
    addnode(&N); //calling-by-reference, &N is the address of a struct node N;
    Thanks a lot!
    It isn't, though. You declared N as a pointer,

    Code:
    struct node *N = NULL;
    so that &N is the address of the address of an unnamed struct node.

  10. #10
    Registered User
    Join Date
    Dec 2015
    Posts
    16

    struggle with pointer-to-pointer and pass-by-reference

    It isn't, though. You declared N as a pointer,
    struct node *N = NULL;
    so that &N is the address of the address of an unnamed struct node
    Oh?!
    Maybe that's the part I missed! Could you please break it down in more detail? I am struggling with pointer-to-pointer. Thanks!

  11. #11
    Registered User
    Join Date
    Jun 2015
    Posts
    1,640
    A variable is a named location in memory. That location is where it's value is stored.

    A pointer is simply a variable whose value is an address, i.e., it's value is the location of other data.

    Dereferencing a pointer causes the pointer's value, an address, to be used to access another location.

    A "double pointer" is nothing special at all. It's simply a pointer whose value is the address of another pointer.

    Run and study the output of this program. (The (void*) casts are necessary for the proper use of the %p format spec, which prints an address.)
    Code:
    #include <stdio.h>
    
    void g(int **ppx) {
        printf("    g: ppx (%p) = %p   *ppx = %p   **ppx = %d\n",
               (void*)&ppx, (void*)ppx, (void*)*ppx, **ppx);
        ++**ppx;
        printf("    g: ppx (%p) = %p   *ppx = %p   **ppx = %d\n",
               (void*)&ppx, (void*)ppx, (void*)*ppx, **ppx);
    }
    
    void f(int *px) {
        printf("  f:    px (%p) = %p   *px = %d\n",
               (void*)&px, (void*)px, *px);
        ++*px;
        g(&px);
        ++*px;
        printf("  f:    px (%p) = %p   *px = %d\n",
               (void*)&px, (void*)px, *px);
    }
    
    int main(void) {
        int x = 1;
        printf("main:    x (%p) = %d\n", (void*)&x, x);
        ++x;
        f(&x);
        ++x;
        printf("main:    x (%p) = %d\n", (void*)&x, x);
        return 0;
    }
    Possible output:
    Code:
    main:    x (0x7fff4f8f407c) = 1
      f:    px (0x7fff4f8f4058) = 0x7fff4f8f407c   *px = 2
        g: ppx (0x7fff4f8f4038) = 0x7fff4f8f4058   *ppx = 0x7fff4f8f407c   **ppx = 3
        g: ppx (0x7fff4f8f4038) = 0x7fff4f8f4058   *ppx = 0x7fff4f8f407c   **ppx = 4
      f:    px (0x7fff4f8f4058) = 0x7fff4f8f407c   *px = 5
    main:    x (0x7fff4f8f407c) = 6

  12. #12
    Registered User
    Join Date
    Apr 2016
    Posts
    21
    That helps a lot.
    Incidentally, the compiler warnings, telling me the type expected by the escape and the type of what I pass to printf() when I don't use the (void*) casts, also helped shed some light on the subject.

  13. #13
    Registered User
    Join Date
    Dec 2015
    Posts
    16
    Thanks algorism and ShungTzu!
    I believe I understand pointer/address of variable and pointer to pointer mostly.
    Your code definitely helped me, and it is closely related to the problem with mine in the function using struct as parameter. Here it deviated my problem a little bit, so I rewrote my code to show you my struggle:
    Code:
    #include <stdio.h>
    #include <assert.h>         //needed for assert()
    #include <stdlib.h>
    
    struct node{
        int data;
    };
    
    /*Common wrong version 1! */
    void addnode_e1(struct node *x)            //I use this way but it is a common mistake!
    {
        x = malloc(sizeof(struct node)); //This line seems causing trouble!
        assert(x);
        x->data = 91;
    }
    
    /*Wrong version 2?? As it compiled without warning or error, print out correctly! */
    void addnode_e2(struct node *x)        
    {
    //    x = malloc(sizeof(struct node));        //This line seems causing trouble!
        assert(x);
        x->data = 92;
    }
    
    void addnode(struct node **x)        //I am lost with **x when compared with the caller addnode(&N)
    {
        *x = malloc(sizeof(struct node));
        assert(*x);
        (*x)->data = 99;
    }
    
    int main( )
    {
        struct node *N = NULL;
        N = malloc(sizeof(struct node *));
        assert(N);
    /* &N is NOT the address of a struct node N, though. 
       You declared N as a pointer, so that 
       &N is the address of the address of an unnamed struct node 
    */    
        printf("The value of &N = %p\n", &N);
        printf("The value of N = %p\n\n", N);
    
        addnode_e1(N);        //    addnode_e1(&N); --would be wrong. Why?    
        printf("After calling addnode_e1(&N)\n");
        printf("The value of &N = %p\n", &N);
        printf("The value of N = %p\n", N);
        printf("The value inside the struct = %d\n\n", N->data);
    
        addnode_e2(N);        // addnode_e2(&N); --would be wrong, too. Why?
        printf("After calling addnode_e2(&N)\n");
        printf("The value of &N = %p\n", &N);
        printf("The value of N = %p\n", N);
        printf("The value inside the struct = %d\n\n", N->data);
    
        addnode(&N);
        printf("After calling addnode(&N)\n");
        printf("The value of &N = %p\n", &N);
        printf("The value of N = %p\n", N);
        printf("The value inside the struct = %d\n\n", N->data);
    
    return 0;
    }
    And the output is:
    Code:
    The value of &N = 0x7ffc35b3bb98
    The value of N = 0x1ec9010
    
    After calling addnode_e1(&N)
    The value of &N = 0x7ffc35b3bb98
    The value of N = 0x1ec9010
    The value inside the struct = 0
    
    After calling addnode_e2(&N)
    The value of &N = 0x7ffc35b3bb98
    The value of N = 0x1ec9010
    The value inside the struct = 92
    
    After calling addnode(&N)
    The value of &N = 0x7ffc35b3bb98
    The value of N = 0x1ec9050
    The value inside the struct = 99
    All &N give the same address in the main() scope, of course, but all N in the erroneous versions are different from the correct version of addnode(), which is what I am trying to understand. So leave addnode() aside.
    1) &N gave the same address after calling addnode_e1() and addnode_e2(), which I had thought they should give different addresses, because the function prototypes are both using addnode_e1/2(struct node *x), which seem both are "pass-by-reference". The results turn out they are both "pass-by-value". What did I understand wrong?
    2) The only difference between addnode_e1() and addnode_e2() is the the line
    Code:
    //    x = malloc(sizeof(struct node));        //This line seems causing trouble!
    which was commented out in the latter. This gave me some clue that my problem is related to the memory allocation. And after calling addnode_e1(), N->data did not get the value as expected.
    I believe there are some important parts missing with my understanding. I read "structure variable is unlike the case for array in C, so the structure name is not the address of the structure, so & operator is needed", but I am not sure how to apply this information to addnode(struct node **x) and addnode(&N).
    Please correct my understanding and elaborate any that I missed!
    Thanks again!
    Last edited by Yifangt; 04-19-2016 at 03:06 PM. Reason: typo

  14. #14
    Registered User
    Join Date
    Dec 2015
    Posts
    16
    Thanks algorism!
    Here is what I understand your code: In the main(), function f(), in which a pointer is the parameter, is called:
    Code:
    int main() {
        int x = 1;
        ...
        f(&x);
        ...
    }
    so that the prototype for function f() with a pointer as parameter, is:
    Code:
    void f(int *px) { 
        ...
        g(&px);
        ...
    }
    where a second function g(), in which a pointer is the parameter, is called. Because the parameter of g() is a pointer of the parameter for function f(), the prototype for function g() must use pointer to pointer as parameter:
    Code:
    void g(int **ppx) {
        ...
    }
    Am I getting closer?
    How to compare this scenario with my addnode() function, which only has one layer of function calling?
    Last edited by Yifangt; 04-19-2016 at 03:05 PM. Reason: edit the indentation

  15. #15
    Registered User
    Join Date
    Jun 2015
    Posts
    1,640
    This is becoming way too much work, so this is my last post.

    Your code is confused. In main you malloc something for N (although you do it incorrectly, for some reason, but let's pretend you wrote sizeof(struct node)) and then you call addnode_e1 with N, essentially passing the new node you allocated to that function. Then that function mallocs a different chunk of memory to its x parameter, throwing away the passed-in address, sticks some data into that node and then returns, leaking that new chunk of memory. addnode_e2 does better in that it doesn't throw the passed-in address away or leak memory but instead assigns the node malloc'ed in main some data, but why is main malloc'ing the struct in the first place?

    Instead of asking these abstract questions, you should say what you're actually trying to do so someone can help you in a more constructive way. For instance, I might write your function like this (if I'm interpreting your purpose correctly).
    Code:
    struct node *newnode(int data) {  // better name
        struct node *x = malloc(sizeof(*x));
        assert(x);  // the poor man's error handler; only for debugging
        x->data = data;
        return x;
    }
    
    /// and call it like this
    struct node *N = newnode(91);

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Replies: 4
    Last Post: 02-14-2012, 07:45 PM
  2. is it possible to pass a structure element by reference?
    By Cathalo in forum C++ Programming
    Replies: 6
    Last Post: 03-04-2009, 10:01 AM
  3. Pass structure by reference(?)
    By azerej in forum C Programming
    Replies: 4
    Last Post: 05-22-2008, 02:10 AM
  4. Structure pass-by-reference - help?
    By dxfoo in forum C Programming
    Replies: 28
    Last Post: 08-20-2005, 10:16 PM
  5. pass be reference versus pass by value
    By Unregistered in forum C++ Programming
    Replies: 2
    Last Post: 08-01-2002, 01:03 PM

Tags for this Thread