Clear just bit field members of a struct?

Viewed 194

I have a struct like the following:

struct Foo {
    unsigned int id;
    unsigned int flag_1 : 1;
    unsigned int flag_2 : 1;
    unsigned int flag_3 : 1;
    // Some arbitrary number of further flags. Code is
    // automatically generated and number will vary.
    // Notably, it may be more than an int's worth.
    int some_data;
    float some_more_data;
    // ...
};

From time to time, I need to reset all the flags to zero while preserving the rest of the struct. One way is obviously to set each flag to 0 individually, but it feels like there ought to be a way to do it in one fell swoop. Is that possible?

(Note that I am open to not using bit fields, but this is code that will sometimes run on memory-contrained systems, so the memory savings are very appealing.)

Edit:

There is a similar question here: Reset all bits in a c bitfield

However, the struct in that question is entirely bitfields. I cannot simply memset the entire struct to zero here, and the other answer involving unions is not guaranteed to work, especially if there are more than an int's worth of flags.

2 Answers

Just use a separate struct for the flags:

struct Foo_flags {
    unsigned int flag_1 : 1;
    unsigned int flag_2 : 1;
    unsigned int flag_3 : 1;
    // ...
};

struct Foo {
    unsigned int id;
    struct Foo_flags flags;
    int some_data;
    float some_more_data;
    // ...
};

Or even a simpler nested struct:

struct Foo {
    unsigned int id;

    struct {
        unsigned int flag_1 : 1;
        unsigned int flag_2 : 1;
        unsigned int flag_3 : 1;
        // ...
    } flags;

    int some_data;
    float some_more_data;
    // ...
};

Then, later in your code:

struct Foo x;
// ...
x.flags.flag_1 = 1;
// ...
memset(&x.flags, 0, sizeof(x.flags));

With some minor adjustments, you can use the offsetof macro to find the beginning and end of the "flag" data within the structure, then use memset to clear the relevant memory. (Note that you cannot use offsetof directly on bitfields, hence the addition of the flag_beg member!)

Here's a working example:

#include <stdio.h>
#include <stddef.h>  // defines offsetof
#include <string.h>  // declares memset

struct Foo {
    unsigned int id;
    unsigned int flag_beg;    // Could be unsigned char to save space
    unsigned int flag_1 : 1;
    unsigned int flag_2 : 1;
    unsigned int flag_3 : 1;
    unsigned int flag_end;    // Could be unsigned char to save space
    // Some arbitrary number of further flags. Code is
    // automatically generated and number will vary.
    // Notably, it may be more than an int's worth.
    int some_data;
    float some_more_data;
    // ...
};

#define FBEG (offsetof(struct Foo, flag_beg))
#define FEND (offsetof(struct Foo, flag_end))

int main()
{
    struct Foo f;
    f.id = 3; f.flag_1 = 1; f.flag_2 = 0; f.flag_3 = 1;
    f.some_data = 33; f.some_more_data = 16.2f;

    printf("%u %u %u %u %d %f\n", f.id, f.flag_1, f.flag_2, f.flag_3, f.some_data, f.some_more_data);
    memset((char*)(&f) + FBEG, 0, FEND - FBEG);
    printf("%u %u %u %u %d %f\n", f.id, f.flag_1, f.flag_2, f.flag_3, f.some_data, f.some_more_data);
    return 0;
}
Related