你如何比较 C 语言中相等的结构?

How do you compare structs for equality in C?

提问人:Hans Sjunnesson 提问时间:9/27/2008 更新时间:12/24/2017 访问量:261615

问:

在标准 C 中,如何比较两个结构实例的相等性?

c 结构 相等

评论


答:

237赞 Greg Hewgill 9/27/2008 #1

C 没有提供任何语言工具来执行此操作 - 您必须自己完成并逐个比较每个结构成员。

评论

23赞 MOHAMED 10/3/2012
如果 2 个结构变量是用 calloc 初始化的,或者它们被 memset 设置为 0,那么你可以将你的 2 个结构与 memcmp 进行比较,而不必担心结构垃圾,这将使你获得时间
26赞 chux - Reinstate Monica 4/14/2015
@MOHAMED 将浮点字段与 进行比较是 的问题。二进制表示方式不同的指针可能指向相同的位置(例如 DOS: seg:offset),因此是相等的。某些系统具有多个空指针,这些指针的比较相等。对于具有 -0 的模糊编码和具有冗余编码的浮点类型也是如此。(Intel long double、decimal64 等)这些问题与是否使用或填充没有区别。0.0, -0.0 NaNmemcmp()intcalloc()
4赞 Demi 9/21/2017
@chux 在我所知道的任何现代 32 位或 64 位系统上,唯一的问题是浮点。
5赞 stefanct 10/29/2017
如果您想知道为什么不使用结构(像我一样),请参阅 stackoverflow.com/questions/46995631/...==
4赞 Martin Bonner supports Monica 12/22/2017
@Demi : 今天。C 程序员的第 10 条诫命是“你要发誓、放弃和放弃声称”全世界都是 VAX“的卑鄙异端......'.用“全世界都是一台PC”代替它并不是一种改进。
25赞 Ben 9/27/2008 #2

如果你经常这样做,我建议你写一个函数来比较这两种结构。这样,如果您更改了结构,则只需在一个地方更改比较即可。

至于怎么做......您需要单独比较每个元素

评论

3赞 Sam 11/8/2018
即使我只使用一次,我也会编写一个单独的函数。
134赞 Sufian 9/27/2008 #3

您可能很想使用 ,但它可能并非在所有情况下都有效。编译器可以向结构添加对齐缓冲区空间,并且不能保证在缓冲区空间中的内存位置找到的值是任何特定值。memcmp(&a, &b, sizeof(struct foo))

但是,如果在使用结构之前使用结构的完整大小,则可以进行浅层比较(如果结构包含指针,则仅当指针指向的地址相同时,它才会匹配)。callocmemsetmemcmp

评论

21赞 Steve Jessop 9/27/2008
接近,因为它适用于“几乎所有”编译器,但不完全是。查看 C90 中的 6.2.1.6.4:“具有相同对象表示的两个值(NaN 除外)比较相等,但比较相等的值可能具有不同的对象表示。
26赞 ajs410 4/16/2013
考虑一个“BOOL”字段。就相等性而言,任何非零 BOOL 都等于每个非零 BOOL 值。因此,虽然 1 和 2 可能都是 TRUE 并且因此相等,但 memcmp 将失败。
4赞 Mecki 4/1/2014
@JSalazar 对你来说可能更容易,但对编译器和 CPU 来说要困难得多,因此速度也慢得多。为什么你认为编译器首先要添加填充?当然不要白白浪费记忆;)
5赞 Steve Jessop 8/19/2015
@Demetri:例如,浮点值正零和负零在任何IEEE浮点实现上都相等,但它们没有相同的对象表示。所以实际上我不应该说它适用于“几乎所有编译器”,它在任何允许您存储负零的实现上都会失败。在我发表评论时,我可能正在考虑有趣的整数表示。
5赞 Steve Jessop 8/20/2015
@Demetri:但许多确实包含浮点数,提问者问的是“你如何比较结构”,而不是“你如何比较不包含浮点数的结构”。这个答案说你可以做一个浅层的比较,前提是先清除内存。这接近工作,但不正确。当然,这个问题也没有定义“相等”,所以如果你把它理解为“对象表示的字节相等”,那么它就是这样做的(无论内存是否被清除)。memcmpmemcmp
22赞 structinator 9/27/2008 #4

您不能使用 memcmp 来比较结构的相等性,因为结构中的字段之间可能会随机填充字符。

  // bad
  memcmp(&struct1, &struct2, sizeof(struct1));

对于这样的结构,上述操作将失败:

typedef struct Foo {
  char a;
  /* padding */
  double d;
  /* padding */
  char e;
  /* padding */
  int f;
} Foo ;

为了安全起见,您必须使用成员比较。

评论

28赞 Jonathan Leffler 9/27/2008
不太可能在双倍之后填充;在双倍之后,字符将立即完全充分对齐。
0赞 Kevin S. 9/27/2008 #5

如果结构只包含原语,或者你对严格相等感兴趣,那么你可以做这样的事情:

int my_struct_cmp(const struct my_struct * lhs, const struct my_struct * rhs)
{
    return memcmp(lhs, rsh, sizeof(struct my_struct));
}

但是,如果您的结构包含指向其他结构或联合的指针,则需要编写一个函数来正确比较基元,并根据需要对其他结构进行比较调用。

但是请注意,作为 ADT 初始化的一部分,您应该使用 memset(&a, sizeof(struct my_struct), 1) 将结构的内存范围归零。

4赞 pixelbeat 9/27/2008 #6

请注意,您可以在非静态结构上使用 memcmp(),而无需 担心填充,只要不初始化 所有成员(一次)。这是由 C90 定义的:

http://www.pixelbeat.org/programming/gcc/auto_init.html

评论

1赞 Alnitak 12/5/2015
是否真的指定了也将任何填充字节归零?{0, }
0赞 pixelbeat 12/6/2015
GCC 至少为部分初始化的结构填充字节数为零,如上面的链接所示,并且 stackoverflow.com/questions/13056364/... 详细介绍了 C11 指定了该行为。
2赞 M.M 10/29/2017
一般不是很有用,因为所有填充在分配给任何成员时都变得不确定
3赞 domgblackwell 9/27/2008 #7

这取决于你问的问题是否是:

  1. 这两个结构是同一个对象吗?
  2. 它们有相同的价值吗?

若要确定它们是否是同一对象,请比较指向两个结构的指针是否相等。 如果你想从总体上找出它们是否具有相同的价值,你必须做一个深入的比较。这涉及比较所有成员。如果成员是指向其他结构的指针,则也需要递归到这些结构中。

在结构不包含指针的特殊情况下,您可以执行 memcmp 来对每个结构中包含的数据执行按位比较,而无需知道数据的含义。

确保您知道“等于”对每个成员意味着什么 - 对于整数来说很明显,但当涉及到浮点值或用户定义类型时,则更加微妙。

2赞 sergio 8/8/2012 #8

memcmp不比较结构,比较二进制文件,结构中总是有垃圾,因此比较总是出来 False。memcmp

逐个元素比较它是安全的,不会失败。

评论

1赞 MOHAMED 10/3/2012
如果 2 个结构变量是用 calloc 初始化的,或者它们被 memset 设置为 0,那么你可以将你的 2 个结构与 memcmp 进行比较,而不必担心结构垃圾,这将使你获得时间
1赞 Remember Monica 8/16/2020
calloc 或 memset 对您没有帮助,因为每次赋值都会将填充字节返回到不确定的值
1赞 grzegorz 8/18/2021
不,并不总是有垃圾。仅在需要时才进行填充。某些结构可以使用 memcmp 进行安全比较。
-2赞 MOHAMED 10/3/2012 #9

如果 2 个结构变量是用 calloc 初始化的,或者它们被 memset 设置为 0,那么你可以将你的 2 个结构与 memcmp 进行比较,而不必担心结构垃圾,这将使你获得时间

-3赞 Hesham Eraqi 10/1/2014 #10

此兼容示例使用 Visual Studio Microsoft 中的 #pragma Pack 编译器扩展来确保结构成员尽可能紧密地打包:

#include <string.h>

#pragma pack(push, 1)
struct s {
  char c;
  int i;
  char buffer[13];
};
#pragma pack(pop)

void compare(const struct s *left, const struct s *right) { 
  if (0 == memcmp(left, right, sizeof(struct s))) {
    /* ... */
  }
}

评论

1赞 Ruud Althuizen 4/10/2015
这确实是正确的。但在大多数情况下,你不希望你的结构被打包!相当多的指令和指针都要求输入数据与字对齐。如果不是,则编译器需要添加额外的指令来复制和重新对齐数据,然后才能执行实际指令。如果编译器不重新对齐数据,CPU 将引发异常。
11赞 Demi 8/19/2015 #11

@Greg在一般情况下必须编写显式比较函数是正确的。

如果出现以下情况,则可以使用:memcmp

  • 结构不包含可能是 的浮点字段。NaN
  • 结构不包含填充(与 clang 一起使用来检查这一点),或者结构在初始化时显式初始化。-Wpaddedmemset
  • 没有具有不同但等效值的成员类型(如 Windows)。BOOL

除非您正在为嵌入式系统编程(或编写可能在嵌入式系统上使用的库),否则我不会担心 C 标准中的一些极端情况。任何 32 位或 64 位设备上都不存在近指针与远指针的区别。据我所知,没有一个非嵌入式系统有多个指针。NULL

另一种选择是自动生成相等函数。如果以简单的方式布置结构定义,则可以使用简单的文本处理来处理简单的结构定义。您可以将 libclang 用于一般情况——因为它使用与 Clang 相同的前端,因此它可以正确处理所有极端情况(除非出现错误)。

我还没有见过这样的代码生成库。但是,它看起来相对简单。

但是,这种生成的相等函数经常在应用程序级别做错误的事情。例如,应该浅比较还是深入比较 Windows 中的两个结构?UNICODE_STRING

评论

2赞 gengkev 10/11/2018
使用 等显式初始化结构并不能保证在进一步写入 struct 元素后填充位的值,请参阅:stackoverflow.com/q/52684192/689161memset