为什么C不提供结构比较?

Wil*_*llW 14 c

正如大多数C程序员所知,你无法直接比较两种结构.

考虑:

void isequal(MY_STRUCT a, MY_STRUCT b)
{
    if (a == b)
    {
        puts("equal");
    }
    else
    {
        puts("not equal");
    }
 }
Run Code Online (Sandbox Code Playgroud)

a==b比较将AFAIK扔在任何合理的C语言编译器编译错误,因为C标准不允许内置结构比较.使用memcmp的变通方法当然是一个坏主意,因为对齐,打包,位域等,所以我们最终逐个元素比较函数.

另一方面,它允许结构分配,例如a = b完全合法.很明显,编译器可以相当简单地处理这个问题,为什么不进行比较呢?

我唯一的想法是结构分配可能与memcpy()非常接近,因为对齐等因素无关紧要.另一方面,比较可能更复杂.或者这是我缺少的东西?

显然,我知道通过元素比较做一个简单的元素不一定足够,例如,如果结构包含指向字符串的指针,但在某些情况下它会有用.

cni*_*tar 9

由于同样的原因memcmp失败,因此不支持比较.

由于填充字段,比较将以不可预测的方式失败,这对于大多数程序员来说是不可接受的.赋值会更改不可见的填充字段,但无论如何它们都是不可见的,因此没有任何意外.

显然,您可能会问:那么为什么不填充所有填充字段呢?当然这会起作用,但它也会使所有程序为他们可能不需要的东西付费.

编辑

Oli Charlesworth在评论中指出,您可能会问:"为什么编译器不会为逐个成员的比较生成代码".如果是这样,我必须承认:我不知道 :-).如果只允许比较完整类型,编译器将拥有所有需要的信息.

  • OP明确建议*不*使用`memcmp`,但是编译器会自动为成员比较生成代码. (2认同)

Max*_*sca 9

正如其他人所提到的,这里是Harbison和Steele的C:A参考手册的摘录:

尽管允许对这些类型进行赋值,但无法对结构和联合进行相等性比较.由对齐限制引起的结构和联合中的间隙可能包含任意值,并且对此进行补偿会对相等比较或修改结构和联合类型的所有操作施加不可接受的开销.

  • 我个人不相信这个论点,但我想原因如下:对于复制/分配结构,生成的代码相当简单,即使可能很慢:它只需要复制结构的整个内存范围,并且可以包括间隙的未定义内容。比较时,它必须从实际字段中辨别出差距(如果这是所需的行为),这将需要更复杂的代码。 (3认同)

Lun*_*din 6

我在C理由(C99理论基础V5.10),6.5.9中找到了这个:

C89委员会不止一次考虑允许比较结构的平等.这些建议在结构上的孔问题上失败了.两个结构的逐字节比较将要求确保将孔设置为零,以便所有孔都相等,这对于自动或动态分配的变量来说是一项艰巨的任务.

结构中联合类型元素的可能性使这种方法产生了不可克服的问题.如果不确保所有漏洞都设置为零,则必须准备实施以将结构比较分解为任意数量的成员比较; 因此,一个看似简单的表达式可能会扩展为大量的代码,这与C的精神背道而驰

用简单的英语:由于结构/联合可能包含填充字节,并且委员会没有强制执行这些以保存某些值,因此它们不会实现此功能.因为如果所有填充字节必须设置为零,则需要额外的运行时开销.

  • 不,这个理由是完全一致的。将静态事物初始化为零是很便宜的,并且只在启动时完成一次。这样做的成本非常低,因为整个内存段都被清零(在其中一些内存段被初始化之前),您要么出于安全原因想要执行此操作(托管系统),要么由硬件在启动时完成(嵌入式系统)。这与静态变量初始化为零而局部变量不初始化的原因相同 - 在运行时重复执行的操作的性能更为重要。 (2认同)
  • @WillW 不,.bss 在嵌入式系统上不会“由硬件初始化”......我关于部分初始化结构的零初始化的第一个论点适用于局部变量以及静态存储持续时间 - 这不一定由 . bss copy-down 但也可能发生在运行时。 (2认同)

vro*_*nov 5

自动生成比较运算符不是一个好主意。想象一下比较如何对这种结构起作用:

struct s1 {
   int len;
   char str[100];
};
Run Code Online (Sandbox Code Playgroud)

这是像最大长度为100的字符串一样的pascal

另一种情况

struct s2 {
   char a[100];
}
Run Code Online (Sandbox Code Playgroud)

编译器如何知道如何比较字段?如果这是NUL终止的字符串,则编译器必须使用strcmp或strncmp。如果是char数组,则编译器必须使用memcmp。

  • 嗯,不是很令人信服。没有人会期望比较遵循该类型的任何语义。仅组件明智的比较就可以了。令人信服的是,有一个简单的旧式语法问题,使我们无法比较数组。因此,其他包含数组的复合类型也无法进行比较。 (3认同)
  • 但是将字符数组解释为字符串不是问题,是吗?当您尝试比较包含 `char[]` 的结构时,没有人希望编译器将值 0x00 设为特殊值。 (2认同)

R..*_*R.. 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?!
Run Code Online (Sandbox Code Playgroud)

这个问题本质上是由于工会无法存储/跟踪哪个成员是当前成员而产生的。

  • 在我看来,这是正确的答案:联合是无法生成平等代码的真正原因,而不是运行时开销。 (3认同)