您如何比较标准 C 中结构的两个实例是否相等?
11 回答
C 没有提供任何语言工具来做到这一点——你必须自己做,并逐个比较每个结构成员。
您可能很想使用memcmp(&a, &b, sizeof(struct foo))
,但它可能不适用于所有情况。编译器可以将对齐缓冲区空间添加到结构中,并且不保证在缓冲区空间中的内存位置找到的值是任何特定值。
但是,如果在使用它们之前使用结构calloc
的memset
完整大小,则可以进行浅比较memcmp
(如果您的结构包含指针,则仅当指针指向的地址相同时才会匹配)。
如果您经常这样做,我建议您编写一个比较这两种结构的函数。那样的话,如果你改变了结构,你只需要在一个地方改变比较。
至于怎么做......你需要单独比较每个元素
由于结构中的字段之间可能存在随机填充字符,因此您不能使用 memcmp 来比较结构是否相等。
// bad
memcmp(&struct1, &struct2, sizeof(struct1));
对于这样的结构,上述内容将失败:
typedef struct Foo {
char a;
/* padding */
double d;
/* padding */
char e;
/* padding */
int f;
} Foo ;
为了安全起见,您必须使用成员比较。
@Greg 是正确的,在一般情况下必须编写显式比较函数。
在以下情况下可以使用memcmp
:
- 结构不包含可能是的浮点字段
NaN
。 - 结构不包含填充(使用
-Wpadded
clang 来检查)或者结构memset
在初始化时显式初始化。 - 没有具有不同但等效值的成员类型(例如 Windows
BOOL
)。
除非您正在为嵌入式系统编程(或编写可能在它们上使用的库),否则我不会担心 C 标准中的一些极端情况。在任何 32 位或 64 位设备上都不存在近与远指针的区别。我所知道的非嵌入式系统没有多个NULL
指针。
另一种选择是自动生成相等函数。如果您以简单的方式布置结构定义,则可以使用简单的文本处理来处理简单的结构定义。您可以将 libclang 用于一般情况——因为它使用与 Clang 相同的前端,它可以正确处理所有极端情况(排除错误)。
I have not seen such a code generation library. However, it appears relatively simple.
However, it is also the case that such generated equality functions would often do the wrong thing at application level. For example, should two UNICODE_STRING
structs in Windows be compared shallowly or deeply?
请注意,您可以在非静态结构上使用 memcmp() 而不必担心填充,只要您不初始化所有成员(一次)。这是由 C90 定义的:
这取决于您要问的问题是否是:
- 这两个结构是同一个对象吗?
- 它们是否具有相同的价值?
要确定它们是否是同一个对象,请比较指向这两个结构的指针是否相等。如果您想大致了解它们是否具有相同的值,则必须进行深入比较。这涉及比较所有成员。如果成员是指向其他结构的指针,您也需要递归到这些结构中。
在结构不包含指针的特殊情况下,您可以使用 memcmp 对每个结构中包含的数据执行按位比较,而无需知道数据的含义。
确保您知道“等于”对每个成员的含义——对于整数来说很明显,但对于浮点值或用户定义的类型来说更微妙。
memcmp
不比较结构,memcmp
比较二进制,结构中总是有垃圾,所以比较总是出假。
逐个元素比较其安全且不会失败。
如果结构仅包含原语或者如果您对严格相等感兴趣,那么您可以执行以下操作:
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) 将结构的内存范围归零。
如果 2 个结构变量是用 calloc 初始化的,或者它们由 memset 设置为 0,那么您可以将您的 2 个结构与 memcmp 进行比较,并且不用担心结构垃圾,这将让您赢得时间
此兼容示例使用 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))) {
/* ... */
}
}