1

I am new to C and having trouble understanding why my_struct_ptr (main) is nil in the following example. How would I assign the address of a struct in the my_structs array to the my_struct_ptr pointer within the get_my_struct_by_name function?

struct my_struct {
    char *name;
    char *descr;
    char *value;
} my_structs[3] = {
    {"a", "a description", "value 1"},
    {"b", "b description", "value 2"},
    {"c", "c description", "value 3"}
};

int get_my_struct_by_name(char *name, struct my_struct *my_struct_ptr) {

    int i;
    for (i=0; i < (sizeof(my_structs)/sizeof(struct my_struct)); i++) {
            if (strcmp(name, my_structs[i].name) == 0) {

                    my_struct_ptr = &my_structs[i];

                    printf("works: %s,%s,%s\n", my_struct_ptr->name, my_struct_ptr->descr, my_struct_ptr->value);
                    return 0;
            }
    }

    return -1;
}


int main() {
    int res = 0;
    struct my_struct *my_struct_ptr;

    if (res = get_my_struct_by_name("b", my_struct_ptr))
            return res;

    printf( "nil: %p\n", my_struct_ptr);

    printf("seg fault: %s,%s,%s\n", my_struct_ptr->name, my_struct_ptr->descr, my_struct_ptr->value);

    return res;
}

Edit: Adding example output to hopefully help others. Thank you to everyone who responded. This is exactly the help I was looking for!

Output:

[prompt ~]$ ./test
works: b,b description,value 2
nil: (nil)
Segmentation fault
TenaciousC
  • 13
  • 4

3 Answers3

4

You have to pass the pointer as a pointer (i.e. pass its address):

int get_my_struct_by_name(char *name, struct my_struct **my_struct_ptr)
{                                                   // ^^^
    // ...
    *my_struct_ptr = /* ... */
}

int main()
{
    struct my_struct * my_struct_ptr;
    get_my_struct_by_name(name, &my_struct_ptr);
    // ...
}

If you just pass the pointer by value, as you did, you only modify a local copy of the pointer, not the original pointer.

Moral: If you want a function to change a variable in the calling scope, you have to say & somewhere.

Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084
1

The solution is to use pointer to pointer in your function get_my_struct_by_name. Notice addition * (asterisk) in the declaration, then in assignment (inside get_my_struct_by_name) and & ampersand in invokation (inside main). This way you are passing a pointer to your my_struct_ptr defined in main and assigning the right value directly to your variable in main.

This solution assumes that you are using pure C (in C++ having a reference to a pointer would be probably more convenient).

int get_my_struct_by_name(char *name, struct my_struct **my_struct_ptr)
{
    int i;
    for(i=0; i < (sizeof(my_structs)/sizeof(struct my_struct)); i++)
    {
        if(strcmp(name, my_structs[i].name) == 0)
        {
            *my_struct_ptr = &my_structs[i];
            printf("works: %s,%s,%s\n", my_struct_ptr->name, my_struct_ptr->descr,     
                   my_struct_ptr->value);
            return 0;
        }
    }

    return -1;
}


int main()
{
    int res = 0;
    struct my_struct *my_struct_ptr;

    if (res = get_my_struct_by_name("b", &my_struct_ptr))
        return res;

    printf( "nil: %p\n", my_struct_ptr);

    printf("seg fault: %s,%s,%s\n", my_struct_ptr->name, my_struct_ptr->descr, 
    my_struct_ptr->value);
    return res;
}
sirgeorge
  • 6,331
  • 1
  • 28
  • 33
0

If I recall correctly, you need to pass the address of the pointer and then dereference it inside of the routine. Since this is call by value, you need the address of the pointer passed to set the value of the pointer. I have update the routines below.

int get_my_struct_by_name(char *name, struct my_struct **my_struct_ptr) 
{      
    int i;    
    for (i=0; i < (sizeof(my_structs)/sizeof(struct my_struct)); i++) 
    {             
        if (strcmp(name, my_structs[i].name) == 0) 
        {                      
           *my_struct_ptr = &my_structs[i];
           printf("works: %s,%s,%s\n", my_struct_ptr->name, 
                   my_struct_ptr->descr, my_struct_ptr->value);
           return 0;             
        }     
    }      
 return -1; 
}

and then you need to take the address of the pointer and pass it in from the main routine.

if (res = get_my_struct_by_name("b", &my_struct_ptr))                 
   return res;

That way you can pass back the address of the structure.

Glenn
  • 1,169
  • 1
  • 14
  • 23