254

How do you compare two instances of structs for equality in standard C?

Hans Sjunnesson
  • 21,745
  • 17
  • 54
  • 63

11 Answers11

236

C provides no language facilities to do this - you have to do it yourself and compare each structure member by member.

Greg Hewgill
  • 951,095
  • 183
  • 1,149
  • 1,285
  • 23
    if the 2 structures variable are initialied with calloc or they are set with 0 by memset so you can compare your 2 structures with memcmp and there is no worry about structure garbage and this will allow you to earn time – MOHAMED Oct 03 '12 at 09:09
  • 26
    @MOHAMED Comparing floating point fields with `0.0, -0.0 NaN` is a problem with `memcmp()`. Pointers that differ in binary representation may point to the same location (e.g. DOS: seg:offset) and so are equal. Some systems have multiple null pointers which compare equally. Same for obscure `int` with -0 and floating point types with redundant encodings. (Intel long double, decimal64, etc.) These issues make no difference is `calloc()` used or not or padding. – chux - Reinstate Monica Apr 14 '15 at 15:02
  • 4
    @chux On *any* modern 32- or 64- bit system I know of, the only issue is with floating point. – Demi Sep 21 '17 at 00:11
  • 5
    In case you wonder why `==` is not working with structures (like me), please see https://stackoverflow.com/questions/46995631/why-are-structs-not-allowed-in-equality-expressions-in-c – stefanct Oct 28 '17 at 23:57
  • 4
    @Demi : Today. The 10th commandment for C programmers is 'Thou shalt foreswear, renounce, and abjure the vile heresy which claimeth that “All the world’s a VAX” ... '. Replacing this with "All the world's a PC" is not an improvement. – Martin Bonner supports Monica Dec 21 '17 at 17:11
  • 2
    @MartinBonner Can you name a specific system that is still in use on which `memcmp` will not work, provided that the struct has neither padding (use `-Wpadded` to check this) or floating-point values? – Demi Dec 23 '17 at 19:14
  • @Demi No. But they have existed, and may again – Martin Bonner supports Monica Dec 23 '17 at 19:46
  • If structure B is a copy of structure A, obtained through memcpy(), then it should be ok to compare them with memcmp(), because in both cases they are treated as binary objects. As long as B is not altered in any way, before the comparison. Even B = B should be avoided. – Igor Stoppa Dec 25 '18 at 15:37
  • Better answers for why C doesn't do this (including string buffers and especially unions) are here: https://stackoverflow.com/questions/7179174 – Matt Oct 28 '19 at 13:45
133

You may be tempted to use memcmp(&a, &b, sizeof(struct foo)), but it may not work in all situations. The compiler may add alignment buffer space to a structure, and the values found at memory locations lying in the buffer space are not guaranteed to be any particular value.

But, if you use calloc or memset the full size of the structures before using them, you can do a shallow comparison with memcmp (if your structure contains pointers, it will match only if the address the pointers are pointing to are the same).

kini
  • 1,781
  • 13
  • 23
Sufian
  • 8,627
  • 4
  • 22
  • 24
  • 21
    Close, because it works on "almost all" compilers, but not quite. Check out 6.2.1.6.4 in C90: "Two values (other than NaNs) with the same object representation compare equal, but values that compare equal may have different object representations." – Steve Jessop Sep 26 '08 at 22:43
  • 2
    What? Can anyone explain Steve's quotation? – Samuel Feb 08 '13 at 15:18
  • 26
    Consider a "BOOL" field. In terms of equality, any non-zero BOOL is equal to every non-zero BOOL value. So while 1 and 2 may both be TRUE and therefore equal, memcmp will fail. – ajs410 Apr 16 '13 at 03:41
  • 1
    Note that for many systems, there are usually compiler qualifiers, such as __packed (two underscores, e.g. `typedef __packed struct`) that will prevent the compiler from adding alignment padding. This makes things *much* easier – bunkerdive Nov 05 '13 at 23:45
  • 4
    @JSalazar Easier for you maybe, but much harder for the compiler and the CPU and thus also much slower. Why do you think compiler add padding in the first place? Certainly not to waste memory for nothing ;) – Mecki Mar 31 '14 at 22:48
  • @SteveJessop Do you know of any (non-embedded) system where this actually happens? – Demi Aug 19 '15 at 05:38
  • 5
    @Demetri: for example the float values positive and negative zero compare equal on any IEEE float implementation, but they do not have the same object representation. So actually I should not have said it works on "almost all compilers", it will fail on any implementation that lets you store a negative zero. I was probably thinking of funny integer representations at the time I made the comment. – Steve Jessop Aug 19 '15 at 13:35
  • 5
    @Demetri: but many do contain floats, and the questioner asks "how do you compare structs", not "how do you compare structs that don't contain floats". This answer says you can do a shallow compare with `memcmp` provided that the memory was cleared first. Which is close to working but not correct. Ofc the question also doesn't define "equality", so if you take it to mean "byte-wise equality of the object representation" then `memcmp` does exactly that (whether the memory is cleared or not). – Steve Jessop Aug 20 '15 at 12:12
  • 1
    It's worth noting that a standard-compliant C compiler may not add padding to *the beginning* of a struct; only after members... and if a `struct X` contains the same initial sequence of elements as a `struct Y` then those elements are required to exist at the same offsets. – autistic Dec 11 '15 at 02:31
  • Also note that from a pedantic perspective, two objects with the same type and binary representation can still have different behavior in certain cases. I don't think any real world compiler takes advantage of this though. – Antimony Jul 12 '16 at 02:52
  • if the structs contains pointers and each one is allocated with two different malloc calls then the pointer addresses will be different between the two structs and memcmp will fail. There needs to be a mechanism that compares by value regardless of the type of the struct member – eat_a_lemon May 29 '17 at 15:07
  • 1
    Initializing the struct with `memset`, etc is not enough to ensure the padding bits are still the same after writing to members of the struct, see: https://stackoverflow.com/q/52684192/689161 – gengkev Oct 11 '18 at 14:14
  • @SteveJessop if a and b is same struct type, then it will have the same padding, and same size. why memcmp will be problematic ? the padding will be empty bytes so it will be equal in both – Atheel Massalha Sep 02 '19 at 07:32
  • @AtheelMassalha *the padding will be empty bytes so it will be equal in both* That is not true. Any padding contains indeterminate values that can't reliably be compared to anything. – Andrew Henle Jan 22 '20 at 11:46
25

If you do it a lot I would suggest writing a function that compares the two structures. That way, if you ever change the structure you only need to change the compare in one place.

As for how to do it.... You need to compare every element individually

Ben
  • 2,771
  • 6
  • 33
  • 45
21

You can't use memcmp to compare structs for equality due to potential random padding characters between field in structs.

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

The above would fail for a struct like this:

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

You have to use member-wise comparison to be safe.

11

@Greg is correct that one must write explicit comparison functions in the general case.

It is possible to use memcmp if:

  • the structs contain no floating-point fields that are possibly NaN.
  • the structs contain no padding (use -Wpadded with clang to check this) OR the structs are explicitly initialized with memset at initialization.
  • there are no member types (such as Windows BOOL) that have distinct but equivalent values.

Unless you are programming for embedded systems (or writing a library that might be used on them), I would not worry about some of the corner cases in the C standard. The near vs. far pointer distinction does not exist on any 32- or 64- bit device. No non-embedded system that I know of has multiple NULL pointers.

Another option is to auto-generate the equality functions. If you lay your struct definitions out in a simple way, it is possible to use simple text processing to handle simple struct definitions. You can use libclang for the general case – since it uses the same frontend as Clang, it handles all corner cases correctly (barring bugs).

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?

Demi
  • 3,535
  • 5
  • 29
  • 45
  • 2
    Explicitly initializing the structs with `memset`, etc. does not guarantee the value of the padding bits after further writing to a struct element, see: https://stackoverflow.com/q/52684192/689161 – gengkev Oct 11 '18 at 14:15
4

Note you can use memcmp() on non static stuctures without worrying about padding, as long as you don't initialise all members (at once). This is defined by C90:

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

pixelbeat
  • 30,615
  • 9
  • 51
  • 60
  • 1
    Is it actually specified that `{0, }` will also zero any padding bytes? – Alnitak Dec 04 '15 at 16:21
  • GCC at least zeroes padding bytes for partially initialized structs as demonstrated at the link above, and http://stackoverflow.com/questions/13056364/is-zero-initialization-of-structures-guaranteed-to-wipe-padded-areas details that C11 specifies that behavior. – pixelbeat Dec 05 '15 at 17:26
  • 2
    Not very useful in general, because all padding becomes indeterminate upon assigning to any member – M.M Oct 29 '17 at 01:20
3

It depends on whether the question you are asking is:

  1. Are these two structs the same object?
  2. Do they have the same value?

To find out if they are the same object, compare pointers to the two structs for equality. If you want to find out in general if they have the same value you have to do a deep comparison. This involves comparing all the members. If the members are pointers to other structs you need to recurse into those structs too.

In the special case where the structs do not contain pointers you can do a memcmp to perform a bitwise comparison of the data contained in each without having to know what the data means.

Make sure you know what 'equals' means for each member - it is obvious for ints but more subtle when it comes to floating-point values or user-defined types.

domgblackwell
  • 5,042
  • 1
  • 19
  • 11
2

memcmp does not compare structure, memcmp compares the binary, and there is always garbage in the struct, therefore it always comes out False in comparison.

Compare element by element its safe and doesn't fail.

gnat
  • 6,213
  • 108
  • 53
  • 73
sergio
  • 399
  • 4
  • 3
  • 1
    if the 2 structures variable are initialied with calloc or they are set with 0 by memset so you can compare your 2 structures with memcmp and there is no worry about structure garbage and this will allow you to earn time – MOHAMED Oct 03 '12 at 09:08
  • 1
    calloc or memset will not help you, as every assignment returns the padding bytes to indeterminate values – Remember Monica Aug 15 '20 at 23:33
  • 1
    No, there is not always garbage. Padding is done only when it is needed. Some structures may be safely compared using memcmp. – grzegorz Aug 18 '21 at 10:04
0

If the structs only contain primitives or if you are interested in strict equality then you can do something like this:

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

However, if your structs contain pointers to other structs or unions then you will need to write a function that compares the primitives properly and make comparison calls against the other structures as appropriate.

Be aware, however, that you should have used memset(&a, sizeof(struct my_struct), 1) to zero out the memory range of the structures as part of your ADT initialization.

Kevin S.
  • 863
  • 6
  • 9
-2

if the 2 structures variable are initialied with calloc or they are set with 0 by memset so you can compare your 2 structures with memcmp and there is no worry about structure garbage and this will allow you to earn time

MOHAMED
  • 41,599
  • 58
  • 163
  • 268
-3

This compliant example uses the #pragma pack compiler extension from Microsoft Visual Studio to ensure the structure members are packed as tightly as possible:

#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))) {
    /* ... */
  }
}
Hesham Eraqi
  • 2,444
  • 4
  • 23
  • 45
  • 1
    That is indeed correct. But in most cases you do not want your structs to be packed! Quite a lot of instructions, and pointers, require that the input data is word-aligned. If it's not, then the compiler needs to add extra instructions to copy and re-align data before the actual instruction can be executed. If the compiler would not re-align the data the CPU will throw an exception. – Ruud Althuizen Apr 10 '15 at 07:31