0

I am using a large struct with many members, and I would like an easy way to quickly see if any of the members are non-zero. I know that memcmp() should not be used to compare two structs for equality (as explained here: How do you compare structs for equality in C?), but I am hoping that by comparing a struct to a block of memory that has been set to 0, this may work.

For example, assume I have some struct:

typedef struct {
    int Int1;
    int Int2;
    int Int3;
} MyInts;

I create a variable from this struct:

MyInts MyStruct = {0};

Throughout execution of my program, the members of MyStruct will usually be 0, but may occasionally be set to some non-zero value temporarily. I want to quickly check if any of the ints in MyStruct are non-zero. Can I use memcmp() as below?

// Create empty struct to test against
MyInts EmptyStruct = {0};

// Make sure entire memory block of test struct is cleared
memset(&EmptyStruct, 0, sizeof(MyInts));

// Compare MyStruct to EmptyStruct to see if any non-zero members exist
int result = memcmp(&MyStruct, &EmptyStruct, sizeof(MyInts));
Community
  • 1
  • 1
phreaknik
  • 615
  • 5
  • 12

2 Answers2

0

You can't do this in the general case, because structs can have padding between fields and at the end of the struct, and the contents of the pad bytes isn't guaranteed (could be uninitialized garbage).

If both structs being compared were initially allocated with calloc, or had sizeof(thestruct) bytes memset to zero before filling in the real values though, then the padding would have predictable values, and memcmp would work. Similarly, if you can guarantee no padding, then this memcmp would work. But if one of them might not have been zeroed, nope, not safe.

Of course, if you want to be "nice", you could just compare the members directly, possibly in a factored out function, and let the compiler handle optimizing, without worrying if you have unzeroed structs with padding floating around.

Note: The second highest voted answer at the link you provided covers this. The comments on that answer also note some edge cases, e.g. floats can have values that compare equal but have different bit representations, so it's not generalizable to all structs even so.

Community
  • 1
  • 1
ShadowRanger
  • 143,180
  • 12
  • 188
  • 271
  • 1
    Hmmm. Is padding specified to be stable with struct assignment like `a = b`? – chux - Reinstate Monica Oct 14 '16 at 04:11
  • Ahhh, your comment makes total sense. Thanks for the quick reply! – phreaknik Oct 14 '16 at 04:18
  • 1
    Padding might not be preserved by struct assignment (C11 6.2.6.1/6). Also it's unclear to me whether padding is guaranteed to be preserved by assigning to individual members. – M.M Oct 14 '16 at 04:18
  • Thanks for the clarification, @M.M – phreaknik Oct 14 '16 at 05:29
  • It is always 100% legal to write to padding? So you could 'use' all bytes in a struct, even if they are not part of any variable, and still not invoke undefined behaviour? – Secto Kia Oct 14 '16 at 07:02
  • @M.M: The Standard would allow an implementation to write padding bytes in arbitrary fashion when writing a struct member. This is the reason the Common Initial Sequence rule only allows "inspection" rather than general access. Given `struct s1 {uint32_t a; uint16_t b;}; struct s2 {uint32_t a; uint16_t b,c;};` a platform where both structures are 8 bytes, and where 16-bit writes are especially slow, given `struct s1 *p; ... p->b = 4;` using a 32-bit write, but if `p` identified an instance of `s2` that would be allowed to trash member `c`. IMHO, the Standard would have been improved... – supercat Oct 14 '16 at 18:41
  • ...if it had provided a macro which implementations set if they never disturb struct padding in such a fashion, since such a guarantee would allow some kinds of code to be written more efficiently for use on systems that offer it. – supercat Oct 14 '16 at 18:42
0

I decided to go ahead and answer my own question for clarity based on the comments. As @M.M mentioned, the padding is not guaranteed to be preserved by struct assignment, and possibly also not preserved by individual member assignment. Therefore, using memcmp to check a struct for non-zero members is un-safe.

The safe way to do this, as ShadowRanger suggests, would be to individually compare each struct member.

Community
  • 1
  • 1
phreaknik
  • 615
  • 5
  • 12