"недопустимые операнды в двоичные операнды" при сравнении двух объединений

Я пишу код для реализации связанного списка в C, который не зависит от типа. Это то, что я пытаюсь сделать.

  1. Создайте объединение, которое может хранить одно из следующих значений: int, char*, double, char.

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

  2. Перечисление для отслеживания того, какой элемент хранит объединение.

    enum { NUM, STR, REAL, CHAR };

  3. Структура, которая будет хранить значения узла.

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

  4. Поскольку у меня может быть более одного списка, я также создаю контейнер для узла, как,

    struct list { struct node *head; };

Теперь я хочу создать функцию, которая будет извлекать элемент из списка. Функция есть,

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

Когда я компилирую код, clang выдает мне эту ошибку,

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

2 ответа

Решение

Перенес это в ответ, так как он больше не подходит для комментариев.

Основная причина проблемы заключается в том, что если члены объединения имеют разные размеры, то вам не гарантируется, что сравнение будет успешным, если записать в меньшие члены, но затем сравнить более крупные члены. Например, если вы написали alph член обоих союзов, но затем сравните num члены, один байт, который содержит alph будет сравниваться правильно, но даже если они равны, остальные байты в num может отличаться, что приводит к "ложному неравному" результату. То же самое с написанием num члены, а затем сравнивая real члены.

Поэтому, чтобы избежать этой проблемы, вам нужно сначала сравнить два type значения, и только если они совпадают, сравните соответствующую пару членов объединения. Выбор элементов для сравнения проще всего сделать с помощью переключателя.

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;
}

Я добавлю примечание, что, как вы справляетесь case STR: зависит от поведения, которое вы хотите. Если вы хотите, чтобы две строки соответствовали, используйте strcmp() если вы хотите, чтобы они ссылались на одну и ту же строку, используйте == на двух указателях. Я подозреваю, что первый будет более полезным способом сделать что-то, но стоит отметить точное сравнение указателей. Немного похоже на разницу между == а также === в PHP.

Вот

if(i->data == key) {

его сравнительная переменная объединения, которая недействительна, как сказано в книге Харбисона и Стила

Структуры и объединения нельзя сравнивать на равенство, даже если присваивание для этих типов разрешено. Пробелы в структурах и объединениях, вызванные ограничениями выравнивания, могут содержать произвольные значения, и компенсация этого может привести к неприемлемым накладным расходам при сравнении на равенство или на всех операциях, которые модифицировали структуру и типы объединения.

Вы можете сравнить членов профсоюза, как

if(i->data.num == key.num) {
 /*  some code */    
}
Другие вопросы по тегам