One might also define data structures for control and status
registers using bitfield structs.
e.g. for the SATA UAHC_GLB_OOBR register:
union UAHC_GBL_OOBR {
uint32_t u;
struct UAHC_GBL_OOBR_s {
#if __BYTE_ORDER == __BIG_ENDIAN
uint32_t we : 1; /**< R/W/H - Write enable. */
uint32_t cwmin : 7; /**< R/W/H - COMWAKE minimum value [...] */
uint32_t cwmax : 8; /**< R/W/H - COMWAKE maximum value [...] */
uint32_t cimin : 8; /**< R/W/H - COMINIT minimum value [...] */
uint32_t cimax : 8; /**< R/W/H - COMINIT maximum value [...] */
#else
uint32_t cimax : 8;
uint32_t cimin : 8;
uint32_t cwmax : 8;
uint32_t cwmin : 7;
uint32_t we : 1;
#endif
} s;
};
scott@slp53.sl.home (Scott Lurndal) writes:
One might also define data structures for control and status
registers using bitfield structs.
Yeah. This kind of application (among others) I consider one of
the motivating forces behind bitfields.
[Some whitespace trimming done in the excerpt below.]
e.g. for the SATA UAHC_GLB_OOBR register:
union UAHC_GBL_OOBR {
uint32_t u;
struct UAHC_GBL_OOBR_s {
#if __BYTE_ORDER == __BIG_ENDIAN
uint32_t we : 1; /**< R/W/H - Write enable. */
uint32_t cwmin : 7; /**< R/W/H - COMWAKE minimum value [...] */
uint32_t cwmax : 8; /**< R/W/H - COMWAKE maximum value [...] */
uint32_t cimin : 8; /**< R/W/H - COMINIT minimum value [...] */
uint32_t cimax : 8; /**< R/W/H - COMINIT maximum value [...] */
#else
uint32_t cimax : 8;
uint32_t cimin : 8;
uint32_t cwmax : 8;
uint32_t cwmin : 7;
uint32_t we : 1;
#endif
} s;
};
To me it seems kind of goofy to use uint32_t for the bitfields type.
I would just use unsigned, which is just as sure to work as intended,
isn't it?
On 21/06/2026 23:13, Tim Rentsch wrote:
scott@slp53.sl.home (Scott Lurndal) writes:
One might also define data structures for control and statusYeah. This kind of application (among others) I consider one of
registers using bitfield structs.
the motivating forces behind bitfields.
[Some whitespace trimming done in the excerpt below.]
e.g. for the SATA UAHC_GLB_OOBR register:To me it seems kind of goofy to use uint32_t for the bitfields type.
union UAHC_GBL_OOBR {
uint32_t u;
struct UAHC_GBL_OOBR_s {
#if __BYTE_ORDER == __BIG_ENDIAN
uint32_t we : 1; /**< R/W/H - Write enable. */
uint32_t cwmin : 7; /**< R/W/H - COMWAKE minimum value [...] */
uint32_t cwmax : 8; /**< R/W/H - COMWAKE maximum value [...] */
uint32_t cimin : 8; /**< R/W/H - COMINIT minimum value [...] */
uint32_t cimax : 8; /**< R/W/H - COMINIT maximum value [...] */
#else
uint32_t cimax : 8;
uint32_t cimin : 8;
uint32_t cwmax : 8;
uint32_t cwmin : 7;
uint32_t we : 1;
#endif
} s;
};
I would just use unsigned, which is just as sure to work as intended,
isn't it?
Size-specific types are almost always the best choice for situations
like this.
When you are using bitfields simply as a way to pack small bits of
data more efficiently, you use whatever style of type fits best with
your needs - consistency with the rest of the code, making the sizes independent of the target, making the sizes adjust according to the
target, maximal portability across compilers and standards version -
whatever you like.
But when you are using them to fit to an existing externally defined structure, fixed-size types are a big advantage (for the whole struct,
not just the bitfields). It is easier to see that the structure is
correct because you are explicit about the sizes. Types like
"uint32_t" have the advantage that they are not portable to targets
that can't support them - as it is likely that you would need to write
such code somewhat differently for it to work on a machine that does
not have such types, causing a compile-time error is useful.
And when the structures represent hardware registers, such as here,
you have additional motivation - these registers are typically
accessed with volatile accesses, and you often want to be sure of the
exact size of the accesses. That is always up to the implementation,
but the norm is that when your bitfields are of a given size,
generated volatile accesses for them use that matching size.
So "uint32_t" says /precisely/ what the code author wants to say for
the type. "unsigned" does not. "uint32_t" is appropriate regardless
of the target and the choice of standard integer sizes - "unsigned" is
not.
scott@slp53.sl.home (Scott Lurndal) writes:
One might also define data structures for control and status
registers using bitfield structs.
Yeah. This kind of application (among others) I consider one of
the motivating forces behind bitfields.
[Some whitespace trimming done in the excerpt below.]
e.g. for the SATA UAHC_GLB_OOBR register:
union UAHC_GBL_OOBR {
uint32_t u;
struct UAHC_GBL_OOBR_s {
#if __BYTE_ORDER == __BIG_ENDIAN
uint32_t we : 1; /**< R/W/H - Write enable. */
uint32_t cwmin : 7; /**< R/W/H - COMWAKE minimum value [...] */
uint32_t cwmax : 8; /**< R/W/H - COMWAKE maximum value [...] */
uint32_t cimin : 8; /**< R/W/H - COMINIT minimum value [...] */
uint32_t cimax : 8; /**< R/W/H - COMINIT maximum value [...] */
#else
uint32_t cimax : 8;
uint32_t cimin : 8;
uint32_t cwmax : 8;
uint32_t cwmin : 7;
uint32_t we : 1;
#endif
} s;
};
To me it seems kind of goofy to use uint32_t for the bitfields type.
I would just use unsigned, which is just as sure to work as intended,
isn't it?
[snip]
uint32_t x;
says precisely that x is 32 bits, unsigned, with no padding bits. But
uint32_t bf : 1;
is meaningfully different from
unsigned bf : 1;
only because in most implementations (and ABIs), the underlying type of
a bit field affects the layout of the entire structure.
I accept that this is the case, but it's never made any sense to me, and >there's no hint of it in the C standard.
For example, if I write:
uint64_t bf : 1;
then the containing struct is typically at least 64 bits, even though
those other 63 bits aren't part of the bit field and other members can
be allocated within them.
It would make a lot more sense *to me* if an N-bit bit field were simply
N bits.
(And of course int, signed int, unsigned int, and bool are the only
portable types for bitfields -- but if you're using bit fields, it's
likely that portability isn't your only priority.)
David Brown <david.brown@hesbynett.no> writes:
On 21/06/2026 23:13, Tim Rentsch wrote:
scott@slp53.sl.home (Scott Lurndal) writes:
One might also define data structures for control and statusYeah. This kind of application (among others) I consider one of
registers using bitfield structs.
the motivating forces behind bitfields.
[Some whitespace trimming done in the excerpt below.]
e.g. for the SATA UAHC_GLB_OOBR register:To me it seems kind of goofy to use uint32_t for the bitfields type.
union UAHC_GBL_OOBR {
uint32_t u;
struct UAHC_GBL_OOBR_s {
#if __BYTE_ORDER == __BIG_ENDIAN
uint32_t we : 1; /**< R/W/H - Write enable. */
uint32_t cwmin : 7; /**< R/W/H - COMWAKE minimum value [...] */ >>>> uint32_t cwmax : 8; /**< R/W/H - COMWAKE maximum value [...] */ >>>> uint32_t cimin : 8; /**< R/W/H - COMINIT minimum value [...] */ >>>> uint32_t cimax : 8; /**< R/W/H - COMINIT maximum value [...] */ >>>> #else
uint32_t cimax : 8;
uint32_t cimin : 8;
uint32_t cwmax : 8;
uint32_t cwmin : 7;
uint32_t we : 1;
#endif
} s;
};
I would just use unsigned, which is just as sure to work as intended,
isn't it?
Size-specific types are almost always the best choice for situations
like this.
When you are using bitfields simply as a way to pack small bits of
data more efficiently, you use whatever style of type fits best with
your needs - consistency with the rest of the code, making the sizes
independent of the target, making the sizes adjust according to the
target, maximal portability across compilers and standards version -
whatever you like.
But when you are using them to fit to an existing externally defined
structure, fixed-size types are a big advantage (for the whole struct,
not just the bitfields). It is easier to see that the structure is
correct because you are explicit about the sizes. Types like
"uint32_t" have the advantage that they are not portable to targets
that can't support them - as it is likely that you would need to write
such code somewhat differently for it to work on a machine that does
not have such types, causing a compile-time error is useful.
And when the structures represent hardware registers, such as here,
you have additional motivation - these registers are typically
accessed with volatile accesses, and you often want to be sure of the
exact size of the accesses. That is always up to the implementation,
but the norm is that when your bitfields are of a given size,
generated volatile accesses for them use that matching size.
So "uint32_t" says /precisely/ what the code author wants to say for
the type. "unsigned" does not. "uint32_t" is appropriate regardless
of the target and the choice of standard integer sizes - "unsigned" is
not.
uint32_t x;
says precisely that x is 32 bits, unsigned, with no padding bits. But
uint32_t bf : 1;
is meaningfully different from
unsigned bf : 1;
only because in most implementations (and ABIs), the underlying type of
a bit field affects the layout of the entire structure.
I accept that this is the case, but it's never made any sense to me, and there's no hint of it in the C standard.
For example, if I write:
uint64_t bf : 1;
then the containing struct is typically at least 64 bits, even though
those other 63 bits aren't part of the bit field and other members can
be allocated within them.
It would make a lot more sense *to me* if an N-bit bit field were simply
N bits.
(And of course int, signed int, unsigned int, and bool are the only
portable types for bitfields -- but if you're using bit fields, it's
likely that portability isn't your only priority.)
scott@slp53.sl.home (Scott Lurndal) writes:
One might also define data structures for control and status
registers using bitfield structs.
Yeah. This kind of application (among others) I consider one of
the motivating forces behind bitfields.
[Some whitespace trimming done in the excerpt below.]
e.g. for the SATA UAHC_GLB_OOBR register:
union UAHC_GBL_OOBR {
uint32_t u;
struct UAHC_GBL_OOBR_s {
#if __BYTE_ORDER == __BIG_ENDIAN
uint32_t we : 1; /**< R/W/H - Write enable. */
uint32_t cwmin : 7; /**< R/W/H - COMWAKE minimum value [...] */
uint32_t cwmax : 8; /**< R/W/H - COMWAKE maximum value [...] */
uint32_t cimin : 8; /**< R/W/H - COMINIT minimum value [...] */
uint32_t cimax : 8; /**< R/W/H - COMINIT maximum value [...] */
#else
uint32_t cimax : 8;
uint32_t cimin : 8;
uint32_t cwmax : 8;
uint32_t cwmin : 7;
uint32_t we : 1;
#endif
} s;
};
To me it seems kind of goofy to use uint32_t for the bitfields type.
I would just use unsigned, which is just as sure to work as intended,
isn't it?
In article <86h5mv8umk.fsf@linuxsc.com>,
Tim Rentsch <tr.17687@z991.linuxsc.com> wrote:
scott@slp53.sl.home (Scott Lurndal) writes:
One might also define data structures for control and status
registers using bitfield structs.
Yeah. This kind of application (among others) I consider one of
the motivating forces behind bitfields.
[Some whitespace trimming done in the excerpt below.]
e.g. for the SATA UAHC_GLB_OOBR register:
union UAHC_GBL_OOBR {
uint32_t u;
struct UAHC_GBL_OOBR_s {
#if __BYTE_ORDER == __BIG_ENDIAN
uint32_t we : 1; /**< R/W/H - Write enable. */
uint32_t cwmin : 7; /**< R/W/H - COMWAKE minimum value [...] */
uint32_t cwmax : 8; /**< R/W/H - COMWAKE maximum value [...] */
uint32_t cimin : 8; /**< R/W/H - COMINIT minimum value [...] */
uint32_t cimax : 8; /**< R/W/H - COMINIT maximum value [...] */
#else
uint32_t cimax : 8;
uint32_t cimin : 8;
uint32_t cwmax : 8;
uint32_t cwmin : 7;
uint32_t we : 1;
#endif
} s;
};
To me it seems kind of goofy to use uint32_t for the bitfields type.
I would just use unsigned, which is just as sure to work as intended,
isn't it?
No. There are issues of alignment and padding one must consider
when using bitfields to model hardware registers, particularly
if (say) a device driver is meant to be shared across ISAs.
There are other hardware registers in our implementation of the SATA controller that are defined as 64-bit registers, for those we use
uint64_t (rather than relying on 'unsigned long' for 64-bit linux
or 'unsigned long long' for 32-bit OS - and this code was designed
to be compiled for both 32-bit and 64-bit targets originally).
uint32_t x;
says precisely that x is 32 bits, unsigned, with no padding bits.
But
uint32_t bf : 1;
is meaningfully different from
unsigned bf : 1;
only because in most implementations (and ABIs), the underlying type
of a bit field affects the layout of the entire structure.
I accept that this is the case, but it's never made any sense to me,
and there's no hint of it in the C standard.
For example, if I write:
uint64_t bf : 1;
then the containing struct is typically at least 64 bits, even
though those other 63 bits aren't part of the bit field and other
members can be allocated within them.
It would make a lot more sense *to me* if an N-bit bit field were
simply N bits.
Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:[...]
[...]But
uint32_t bf : 1;
is meaningfully different from
unsigned bf : 1;
only because in most implementations (and ABIs), the underlying type
of a bit field affects the layout of the entire structure.
I accept that this is the case, but it's never made any sense to me,
and there's no hint of it in the C standard.
I think saying there is not even a hint is an overstatement. The C
standard says that an implementation "may allocate any addressable
storage unit large enough to hold a bit-field." It shouldn't be a
surprise that how much storage is allocated depends on the type of
the bit-field member. For example, a bit-field of type 'unsigned'
might very well choose a larger storage unit than what is chosen
for a bit-field of type '_Bool'. It seems obvious that the type of
a bit-field might affect what size and layout is chosen.
For example, if I write:
uint64_t bf : 1;
then the containing struct is typically at least 64 bits, even
though those other 63 bits aren't part of the bit field and other
members can be allocated within them.
It would make a lot more sense *to me* if an N-bit bit field were
simply N bits.
Two problems with that. One, it seems to be in conflict with what
the C standard says about 0-width bit-fields.
Two, the C standard
explicitly allows allocating bit-fields using a high-to-low order or
a low-to-high order (implementation-defined choice). Presumably
this freedom is given to accommodate both big- and little-endian
platforms. The idea that an N-bit bit-field should simply be N bits
doesn't work in big-endian environments. It seems better to allow little-endian implementations to choose a size that matches what a
big-endian implementation would use, rather than insisting that they
be different.
Tim Rentsch <tr.17687@z991.linuxsc.com> writes:[...]
Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:
[...]It would make a lot more sense *to me* if an N-bit bit field were
simply N bits.
Two, the C standard
explicitly allows allocating bit-fields using a high-to-low order or
a low-to-high order (implementation-defined choice). Presumably
this freedom is given to accommodate both big- and little-endian
platforms. The idea that an N-bit bit-field should simply be N bits
doesn't work in big-endian environments. It seems better to allow
little-endian implementations to choose a size that matches what a
big-endian implementation would use, rather than insisting that they
be different.
I honestly don't understand your point here. How does making
N-bit bit-fields N bits not work in a big-endian environment?
Can you elaborate? Of course endianness can affect how bit-fields
are allocated within a "storage unit".
| Sysop: | Amessyroom |
|---|---|
| Location: | Fayetteville, NC |
| Users: | 70 |
| Nodes: | 6 (0 / 6) |
| Uptime: | 39:16:11 |
| Calls: | 948 |
| Calls today: | 2 |
| Files: | 1,325 |
| Messages: | 280,644 |