Search code examples
cundefined-behaviorunspecified-behavior

Is this memset-memcmp on a struct variable valid C?


Is it legal to memset a struct to some value, then compare it with memcmp?

struct S {
    // struct definition not relevant, but it has bitfields
};

struct S invalid_S;
memset(&invalid_S, 0xFF, sizeof invalid_S);

struct S value;
memset(&value, 0, sizeof value); // actual data read would be here

if (memcmp(&invalid_S, &value, sizeof(struct S) != 0) {
    /// operate on fields of value
}

struct S value2;
value2 = invalid_S;

if (memcmp(&invalid_S, &value2, sizeof(struct S) != 0) {
    /// operate on fields of value, which doesn't happen now
}

Is the above codes behavior well defined, undefined or implementation-specified? Does validity of above code depend on struct S?

The reason for having a struct filled with 0xFF, and then comparing it with memcmp, is this: I have a function which returns a bitfield struct which matches what I read from a hardware device, with no wasted bits or bytes, and I want an efficient way to report error (the device can never return all 0xFF bytes). I have fixed platforms and toolchains, where the code works now, but can I trust it to not break if I for example increase optimization level?


Verdict: While this code could be made to work if I made sure there are no padding bits, float fields etc possibly problematic, I decided to instead just set one particular struct field to a specific "impossible" value to indicate error.


Solution

  • Is it legal to memset a struct to some value, then compare it with memcmp?

    Yes, since all of memset, memcmp, memcpy are documented to work on arbitrary memory zones (provided the pointer and the size passed to them are pointing to a valid memory zone).

    However, in some cases, that might not be meaningful. For example, if you memcpy from some uninitialized memory, you'll get garbage in the destination, and using that garbage might be undefined behavior.

    You are using memset with 0xff. In theory, you might have some implementations where a char is larger than 8 bits (but in practice that does not happen so you don't care).

    You might in theory have some implementation having trap representations for integral value. In practice, that does not happen.

    If you use memcmp on structures having padding that might not work as you expect. You may want to dive into the ABI specification of your platform to understand how they are implemented (and bitfields might not be specified in your ABI and be compiler specific).

    I believe that in practice you need to understand well the exact layout of your particular struct with your particular compiler. It looks like your code might be hardware specific, then you don't care that much about portability. So in practice your struct S is very relevant (perhaps avoiding bitfields in it would be "safer").