Search code examples
ccompiler-errorslinked-listbinary-operators

'invalid operands to binary operands' while comparing two unions


I am writing a code for implementing linked list in C that is type agnostic. This is what I am trying to do.

  1. Create a union that can store either of these: int, char*, double, char.

    union element { int num; char* str; double real; char alph; };

  2. An enum to keep track of what element the union is storing.

    enum { NUM, STR, REAL, CHAR };

  3. A struct that will store the node values.

    struct node { int type; union element data; struct node *next; };

  4. Since I might have more than one list, I am also creating a container for the node as,

    struct list { struct node *head; };

Now I want to create a funtion that will fetch a element from the list. The function is,

node* findkey(list *l, union element key)
{
    node *i=list->head;
    while(!i) {
        if(i->data == key) {
            return i;
        i=i->next;
    }
    return NULL;
}

When I compile the code, clang throws me this error,

linkedlist.c:33:11: error: invalid operands to binary expression ('union element' and
      'union element')
                if(i->data == key)
                   ~~~~ ^  ~~~
1 error generated.

Solution

  • Moved this to an answer since it's no longer comment appropriate.

    The root cause of the problem is that if the union member are different sizes, you are not guaranteed that the comparison will succeed if write to smaller members but then compare larger members. For example, if you wrote to the alph member of both unions, but then compare the num members, the one byte that holds alph will compare correctly, but even if they're equal, the remaining bytes in num may be different, leading to a "false unequal" result. Same with writing to the num members and then comparing the real members.

    So to avoid this problem, you need to first compare the two type values, and only if they match compare the appropriate pair of union members. The selection of which members to compare can most easily be done with a switch.

    node* findkey(list *l, union element key, int type)
    {
        node *i=list->head;
        while(!i) {
            // compare types first, assume no match if they differ
            if(i->type != type) {
                continue
            }
            switch (type)
            {
            case NUM:
                if(i->data.num == key.num) {
                    return i;
                }
                break;
            case STR:
                ...
            case REAL:
                ...
            case CHAR:
                ...
            }
            i=i->next;
        }
        return NULL;
    }
    

    I'll add a note that how you handle case STR: is dependant on the behavior you want. If you want the two stings to match, use strcmp() if you want them to refer to the same string, use == on the two pointers. I suspect that the former will be a more useful way to do things, but it's worth noting the exact pointer comparison. Somewhat akin to the difference between == and === in PHP.