您如何比较C中相等的结构?


Answers:


195

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


19
如果2个结构变量以calloc初始化或由memset设置为0,那么您可以将2个结构与memcmp进行比较,而不必担心结构垃圾,这将使您
节省

21
@MOHAMED与比较浮点字段0.0, -0.0 NaN是的问题memcmp()。二进制表示形式不同的指针可能指向相同的位置(例如DOS:seg:offset),因此相等。一些系统具有多个相等比较的空指针。对于int-0 晦涩难懂和带有冗余编码的浮点类型也是如此。(Intel long double,decimal64等),这些问题使calloc()使用与否或填充没有区别。
chux-恢复莫妮卡2015年

2
@chux在我所知道的任何现代32或64位系统上,唯一的问题是浮点数。
黛米(Demi)

2
如果您想知道为什么==不能像我这样使用结构,请访问stackoverflow.com/questions/46995631/…–
stefanct

4
@Demi:今天。对C程序员的第十条诫命是:“你应该放弃,放弃和废除宣称“全世界都是VAX”……的邪恶异端……”。用“全世界的PC”代替它并没有改善。
马丁·邦纳

110

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

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


19
关闭,因为它可以在“几乎所有”的编译器上运行,但不完全可以。在C90中检出6.2.1.6.4:“两个具有相同对象表示形式的值(NaN除外)比较相等,但是比较相等的值可能具有不同的对象表示形式。”
史蒂夫·杰索普

22
考虑一个“ BOOL”字段。就相等而言,任何非零BOOL等于每个非零BOOL值。因此,虽然1和2可能都为TRUE,因此相等,但memcmp将失败。
ajs410

4
@JSalazar也许对您来说更容易,但是对编译器和CPU来说要困难得多,因此也要慢得多。您为什么认为编译器首先要添加填充?当然不会浪费内存;)
Mecki 2014年

4
@Demetri:例如,浮点值正零和负零在任何IEEE浮点实现上都比较相等,但是它们没有相同的对象表示形式。因此,实际上我不应该说它适用于“几乎所有编译器”,它将在任何允许您存储负零的实现上失败。发表评论时,我可能会想到有趣的整数表示形式。
史蒂夫·杰索普

4
@Demetri:但是很多确实包含浮点数,发问者问“如何比较结构”,而不是“如何比较不包含浮点的结构”。这个答案说,如果memcmp先清除内存,则可以进行比较。这接近工作,但不正确。Ofc这个问题也没有定义“相等性”,因此,如果您将其表示为“对象表示形式的字节相等性”,那么memcmp它确实可以做到这一点(无论是否清除了内存)。
史蒂夫·杰索普

22

如果您做了很多,我建议您编写一个比较这两种结构的函数。这样,如果您更改结构,则只需要在一个位置更改比较即可。

至于如何做...。您需要分别比较每个元素


1
即使只使用一次,我也会编写一个单独的函数。
山姆

18

由于结构中字段之间可能存在随机填充字符,因此无法使用memcmp比较结构是否相等。

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

上面对于这样的结构将失败:

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

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


25
双打之后不太可能被填充;字符在加倍后立即完全对齐。
乔纳森·勒夫勒

7

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

在以下情况下可以使用memcmp

  • 该结构不包含可能存在的浮点字段 NaN
  • 结构体不包含填充(-Wpadded与clang一起使用以检查此内容),或者结构体使用显式初始化memset
  • 没有成员类型(例如Windows BOOL)具有不同但等效的值。

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

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

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

但是,在这种情况下,此类生成的相等函数通常在应用程序级别执行错误的操作。例如,应该UNICODE_STRING浅浅或深浅地比较Windows中的两个结构?


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

4

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

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


1
实际指定{0, }是否也将所有填充字节归零吗?
Alnitak

如上面的链接所示,GCC至少将部分初始化的结构的填充字节清零,并且stackoverflow.com/questions/13056364/…详细说明了C11指定该行为。
pixelbeat

1
不是一般的非常有用的,因为所有的填充后变得在分配给任何成员不确定的
MM

2

这取决于您要问的问题是:

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

要找出它们是否是同一对象,请比较两个结构的指针是否相等。如果您想大致了解它们是否具有相同的值,则必须进行深入比较。这涉及比较所有成员。如果成员是指向其他结构的指针,则也需要递归到这些结构。

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

确保知道每个成员的“等于”含义-对于整数来说很明显,但是对于浮点值或用户定义的类型则更微妙。


2

memcmp不比较结构,memcmp比较二进制文件,并且结构中始终有垃圾,因此在比较中它总是False。

逐个元素比较其安全性,并且不会失败。


1
如果2个结构变量以calloc初始化或由memset设置为0,那么您可以将2个结构与memcmp进行比较,而不必担心结构垃圾,这将使您
节省

1

如果结构仅包含基元,或者您对严格相等感兴趣,则可以执行以下操作:

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

但是,如果您的结构包含指向其他结构或联合的指针,那么您将需要编写一个函数,该函数可以正确比较基元,并在适当时针对其他结构进行比较调用。

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


-1

如果2个结构变量以calloc初始化或由memset设置为0,则可以将2个结构与memcmp进行比较,而不必担心结构垃圾,这将使您节省时间


-2

此兼容示例使用Microsoft Visual Studio的#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
确实是正确的。但是在大多数情况下,您不希望打包结构!很多指令和指针要求输入数据是字对齐的。如果不是,那么编译器需要添加额外的指令来复制和重新对齐数据,然后才能执行实际的指令。如果编译器不重新对齐数据,则CPU将引发异常。
Ruud Althuizen 2015年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.