Почему C не обеспечивает структурированное сравнение?

Как известно большинству программистов на C, вы не можете напрямую сравнивать две структуры.

Рассмотрим:

void isequal(MY_STRUCT a, MY_STRUCT b)
{
    if (a == b)
    {
        puts("equal");
    }
    else
    {
        puts("not equal");
    }
 }

Сравнение a==b сделает AFAIK ошибкой компиляции для любого разумного компилятора C, потому что стандарт C не позволяет встроенного сравнения структуры. Обходные пути с использованием memcmp - это, конечно, плохая идея из-за выравнивания, упаковки, битполей и т.д., Поэтому мы заканчиваем тем, что пишем элемент по элементам сравнения.

С другой стороны, он разрешает назначение структуры, например. a = b является полностью законным. Очевидно, что компилятор может справиться с этим довольно тривиально, так почему бы не сравнить?

Единственная идея, которая у меня была, заключалась в том, что назначение структуры, вероятно, довольно близко к memcpy(), поскольку разрывы из-за выравнивания и т.д. не имеют значения. С другой стороны, сравнение может быть более сложным. Или это что-то мне не хватает?

Очевидно, что я знаю, что выполнение простого элемента путем сравнения элементов не обязательно достаточно, например. если структура содержит указатель на строку, но есть обстоятельства, в которых это было бы полезно.

Ответ 1

Как уже упоминалось, здесь выдержка из C: Справочное руководство от Harbison and Steele:

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

Ответ 2

Сравнение не поддерживается по той же причине memcmp не работает.

Из-за полей дополнений сравнение не получилось бы непредсказуемым образом, что было бы неприемлемо для большинства программистов. Назначение изменяет невидимые поля заполнения, но в любом случае они невидимы, поэтому ничего неожиданного нет.

Очевидно, вы можете спросить: так почему же он просто не заполняет все поля заполнения? Конечно, это сработает, но это также заставило бы все программы платить за то, что им может не понадобиться.

ИЗМЕНИТЬ

Oli Charlesworth отмечает в комментариях, что вы можете спрашивать: "Почему компилятор не генерирует код для сравнения по элементам". Если это так, я должен признаться: Я не знаю:-). Компилятор будет иметь всю необходимую информацию, если он позволит только сравнивать полные типы.

Ответ 3

Оператор автоматического генерации сравнения - плохая идея. Представьте, как будет работать сравнение для этой структуры:

struct s1 {
   int len;
   char str[100];
};

Это pascal как строка с максимальной длиной 100

Другой случай

struct s2 {
   char a[100];
}

Как компилятор знает, как сравнивать поле? Если это строка с завершающим NUL, компилятор должен использовать strcmp или strncmp. Если это char, компилятор массива должен использовать memcmp.

Ответ 4

Я нашел это в логике C (C99 обоснование V5.10), 6.5.9:

Комитет С89 рассмотрел не один раз, позволяя сравнивать структуры для равенства. Такие предложения усугублялись проблемой дыр в структурах. Байт-мудрый сравнение двух структур потребует, чтобы отверстия, безусловно, были установлены на ноль, так что все дыры сравнивали бы равные, сложную задачу для автоматических или динамически распределенных переменных.

Возможность объединения элементов типа в структуру создает непреодолимые проблемы с этим подход. Без уверенности, что все отверстия были установлены равными нулю, реализация будет иметь быть готовым нарушить сравнение структуры с произвольным числом сравнений с членами; казалось бы, простое выражение могло бы, таким образом, распространиться на существенный фрагмент кода, который вопреки духу C

В простом английском языке: поскольку structs/union могут содержать байты заполнения, и комитет не применял их для хранения определенных значений, они не будут реализовывать эту функцию. Потому что, если все байты заполнения должны быть установлены равными нулю, для этого потребуются дополнительные служебные данные времени выполнения.

Ответ 5

Чтобы добавить к существующим хорошим ответам:

struct foo {
    union {
        uint32_t i;
        float f;
    } u;
} a, b;
a.u.f = -0.0;
b.u.f = 0.0;
if (a==b) // what to do?!

Проблема возникает из-за того, что объединения не могут хранить/отслеживать, какой элемент является текущим.