Question

In my project I have linked links of struct elem and each elem has many different attributes.

Currently each time I want to get an elems attribute, I call a function such as:

     get_elem_address(&linked_list);
     get_elem_this(&linked_list);
     get_elem_that(&linked_list);

In each function I have the same bit of code:

struct elem *elem = linked_list->first_elem;

while (elem != NULL) {
    if (elem->identifer == identifer) get_elem_whatever_i_need;
    elem = elm->next;
}

In some functions I can guarenteee that there will be an element where the identifers match, in other functions I am not sure. Is there a way I can write a function that will go through the linked list and find the element I want and then return it.

The reason why I am not sure if i can do this is because of the cases where it may not an elem that has the same identifer as the passed in one.

Was it helpful?

Solution

This answer is probably going to get a lot of comments from the traditional C programming community, not unlikely to get a lot of down votes either, but I'm still going to give it.

You could consider using a functional programming approach. For a simple linked list it is arguably overkill and I personal (too) would just stick with the loop you gave, but on more complex data-structures it is definitely worth considering. The linked list does make for a good demonstration.

So let's assume we have the following linked list:

#include <string.h>

typedef struct elem
{
        struct elem     * next;
        char            * identifier;
} t_elem;

then we could program a generic finder function as follows:

t_elem * find(t_elem * list, int (*sel)(t_elem * elem))
{
        for (; list != NULL; list = list->next)
        if (sel(list))
        {
                break;
        }
        return list;
}

It takes a selection function as a parameter, in C programming often called a call back and returns the first element in the list for which this function returns non-zero. Finding the first element with a certain identifier could then look as follows:

t_elem * find_id(t_elem * list, char * id)
{
        int sel(t_elem * e)
        {
                return strcmp(e->identifier, id) == 0;
        }
        return find(list, sel);
}

The above function is non-standard ANSI as it is using a nested function. GCC supports this and I would encourage the standardization committee to adopt such nested functions for this very reason.

If you want or have to stick to standard ANSI then you will have to make the so called closure explicit as I've done in the following example. In this case the closure simply consists of the parameter id, but in general a dedicated struct must be defined. The closure is passed around as void pointer and cast to the appropriate type whenever used (error prone indeed).

t_elem * find2(t_elem * list, int (*sel)(t_elem * elem, void *), void * cl)
{
        for (; list != NULL; list = list->next)
        if (sel(list, cl))
        {
                break;
        }
        return list;
}

int sel_id(t_elem * e, void * cl)
{
        char    * id = cl;
        return strcmp(e->identifier, id) == 0;
}

t_elem * find_id2(t_elem * list, char * id)
{
        return find2(list, sel_id, id);
}

If you feel comfortable with such an approach is of course up to you, but for me it has become a standard tool.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top