Peter Lee
Peter Lee

Reputation: 183

struct pass by value

Suppose I have a struct like this..

struct object{
     int id;
     char *name;
     node *list_head; //a pointer to the head of a linked list
};

typedef struct object object;

I statically declared a struct variable. I have to pass this by value to a function which inserts an element to a list in the temp_obj as shown below.

We can assume that the add_element and print_list function are working fine. If I print the list after function_a then it won't print the inserted element. I think this is because I am passing the structure as a value so the change made in function_a won't reflect to the structure declared before the function_a.

But I have to pass the structure by value because of given interface. In this case, what can I do to reflect the change to the original structure?

object temp_obj
function_a(temp_obj);
print_list(temp_obj.list_head);

void function_a(object obj){
    //add an element to the list
    int num = 1;
    add_element(&obj.list_head, num);
}

Upvotes: 1

Views: 3837

Answers (4)

Jonathan Leffler
Jonathan Leffler

Reputation: 753735

You can do it subject to a not too onerous condition, but it is a cheat of sorts.

If the add_element() function adds the new element at the end of the list, not at the head, and if you arrange things so that there is an initial node in the list, then you can, just about, do it.

Proof:

#include <assert.h>
#include <errno.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

static void err_exit(const char *fmt, ...);

typedef struct node   node;
typedef struct object object;

struct object
{
    int   id;
    char *name;
    node *list_head; //a pointer to the head of a linked list
};

struct node
{
    node   *next;
    object *data;
};

static void add_element(node **list, int value)
{
    assert(list != 0);
    object *new_objt = calloc(sizeof(object), 1);
    node   *new_node = calloc(sizeof(node),   1);
    if (new_objt == 0 || new_node == 0)
        err_exit("Out of memory in %s\n", __func__);
    node *next = *list;
    while (next->next != 0)
        next = next->next;
    next->next = new_node;
    new_node->data = new_objt;
    new_objt->id = value;
}

static void print_list(const node *list)
{
    assert(list != 0);
    node *next = list->next;
    printf("List: ");
    while (next != 0)
    {
        if (next->data != 0)
            printf("%d ", next->data->id);
        next = next->next;
    }
    printf("EOL\n");
}

static void function_a(object obj)
{
    int num = 1;
    add_element(&obj.list_head, num);
}

int main(void)
{
    node   temp_node = { 0, 0 };
    object temp_obj  = { 0, 0, &temp_node };    // Key trick!

    print_list(&temp_node);
    function_a(temp_obj);
    print_list(&temp_node);
    function_a(temp_obj);
    print_list(&temp_node);

    return 0;
}

static void err_exit(const char *fmt, ...)
{
    int errnum = errno;
    va_list args;
    va_start(args, fmt);
    vfprintf(stderr, fmt, args);
    va_end(args);
    if (errno != 0)
        fprintf(stderr, " (%d: %s)", errnum, strerror(errnum));
    putc('\n', stderr);
    exit(EXIT_FAILURE);
}

Compilation

gcc -O3 -g -std=c99   -Wall -Wextra node.c -o node  

Output:

List: EOL
List: 1 EOL
List: 1 1 EOL

If the object of the exercise is to defeat a brain-dead interface, this works around it. If the object of the exercise is to create a usable interface, then you probably don't do it this way; you pass a pointer to a structure into function_a() so you can change the list_head.

Upvotes: 2

Nikos C.
Nikos C.

Reputation: 51840

list_head is a pointer, so why do you do this:

add_element(&obj.list_head, num);

This is the correct way:

add_element(obj.list_head, num);

The head of a list is the first element in it. That never changes, which is why you don't need to pass &obj.list_head. You only need the address of the list head, not the address of the pointer that holds the address of the list head.

Your add_element() implementation should look something like this, assuming your 'node' struct has a 'next' member:

void add_element(node* head, int num)
{
    /* Iterate over the list that starts at 'head' and when we reach the end,
       insert 'num' as a new element */
    node* cur = head;
    while (cur->next != NULL)
        cur = cur->next;
    /* We're at the end. */
    cur->next = malloc(sizeof(node));
    cur->next->next = NULL;
    cur->next->num = num;
}

Passing by value will work correctly, since your object struct has pointer members. That means copies of object variables will still point to the same data since they're all shallow copies (the same data is shared by all copies.)

Upvotes: 0

Jeegar Patel
Jeegar Patel

Reputation: 27210

You can not do it with pass by value..you must change your interface design and use pass by refference

Upvotes: 0

John3136
John3136

Reputation: 29265

You're screwed!

If you can't change the interface, then there is no way to make "pass by value" act like "pass by reference".

Your options are change the interface to take an object * or make the function return an object (or object*) - all of these options require a change to the interface.

Upvotes: 2

Related Questions