Path: csiph.com!eternal-september.org!reader02.eternal-september.org!.POSTED!not-for-mail From: Keith Thompson Newsgroups: comp.std.c Subject: Re: bit-fields of type unsigned long and unsigned long long Date: Fri, 25 Jun 2021 02:02:35 -0700 Organization: None to speak of Lines: 160 Message-ID: <87a6neuxjo.fsf@nosuchdomain.example.com> References: <86fsx8bh88.fsf@linuxsc.com> <878s2zw30y.fsf@nosuchdomain.example.com> <87v962vrh3.fsf@nosuchdomain.example.com> Mime-Version: 1.0 Content-Type: text/plain Injection-Info: reader02.eternal-september.org; posting-host="e76a60976a03f7b22ccc859f0f74fb19"; logging-data="6434"; mail-complaints-to="abuse@eternal-september.org"; posting-account="U2FsdGVkX18acQOCLi9orzTkomIvmzgk" User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/27.2 (gnu/linux) Cancel-Lock: sha1:aKomjdb4JMP5WDNt+WJybzTwWtM= sha1:r/1SbSXSqYJXvTVyFc+ZNa0GeXU= Xref: csiph.com comp.std.c:6250 David Brown writes: > On 25/06/2021 00:16, Keith Thompson wrote: >> David Brown writes: >>> On 24/06/2021 20:06, Keith Thompson wrote: >>>> David Brown writes: >> [...] >>>>> This would give a very different result from the way compilers implement >>>>> bitfields today. In particular, consider : >>>>> >>>>> sizeof(struct { int8_t a : 1; }); >>>>> >>>>> today, that is 1 on compilers that accept any integer type in bit >>>>> fields. With your proposal, it would be sizeof(int). >>>> >>>> How does that follow? The bit field occupies only 1 bit. Why should >>>> its underlying type affect the size of the structure? >>> >>> The current practice is that the size (and alignment) of the struct or >>> its parts comes from the type used to declare the bit-field - just as >>> for any other field. As I said below, it would be conceivable for a >>> compiler to use the programmer's specified type to set the size of the >>> containing struct or addressable storage unit, while ignoring it for the >>> type of the field and how it is interpreted when used in an expression. >>> That would, however, seem arbitrary and counter-intuitive, as well as >>> being contrary to current practice and (if my interpretation is correct >>> - but I might be wrong here) to C++ standards. >> >> (For simplicity, assume CHAR_BIT==8 and sizeof(int)==4.) >> >> I understand that it's existing practice, but it just doesn't make any >> sense to me. If I define a struct with a bit-field defined as "unsigned >> int:1", it's a single bit, not a 32-bit unsigned int object. I can pack >> 8 of them into a single byte. I just don't see why the declared type >> of a bit field should affect the size of the containing structure when >> it has no effect on the size of the bit field itself. The structure >> doesn't *need* those 32 bits of storage. If I add a second unsigned:1 >> bit-field, the structure doesn't grow by another 32 bits. >> >> As N1570 6.7.2.1p10 says, "A bit-field is interpreted as having a signed >> or unsigned integer type consisting of the specified number of bits.", >> so the bit-field object isn't really of type unsigned int. >> >> For an implementation that doesn't support types other than the >> required ones, any struct with a 2-bit bit-field would have to be >> at least 32 bits, while a struct with an unsigned char member could >> be as small as 8 bits. (Same for a 1-bit bit-field pre-C99.) That >> seems to me like an arbitrary restriction. >> >> I don't use bit-fields much, so maybe I'm missing some reason why this >> behavior is reasonable and/or useful. >> >> [...] >> > > As I see it, bit-fields are used for two main purposes. > > One is for structures internal to a program, in order to reduce space. > There are two typical reasons for that. You might have a small embedded > system with extremely small ram size (though these are getting less > common, and since they are very rarely used with anything newer than > C99, changes to the standard matter little there). Or you might have a > bigger system with very large quantities of data, so that packing the > data is important for efficiency. Common for these is that you don't > really care about things like ordering, but you /do/ care that the sizes > are what you expect. In particular, for efficiency on big systems too > small is not better than too big - an array of 60 byte structs (with 4 > byte alignment) is going to be a lot slower than an array of 64 byte > structs for many purposes. > > The other main use for bitfields is for matching external structures. > These can be part of file formats, network packet formats, calls to > functions from other languages, or hardware and peripherals. For this > kind of use, precise specifications of all sizes, alignments, bit > ordering, etc., is essential. The alternative is to use masks and > shifting which is ugly and error-prone, but more portable. > > The bit-field requirements in the C specifications today are too limited > and under-specified to be of much use for anything - they are not enough > to do a good job of any common use-cases. (They could be useful for > making compact structures for large arrays in the days before caches, > when precise sizes mattered less.) > > The practical reality of bit-fields is that people use them based on the > specifications given by their compiler and/or target ABI, using > extensions provided by almost all good compilers (allowing any integer > type or enumeration type), or using undocumented assumptions about > sizes, alignments, ordering, etc., for their compiler and target. > > A change to the C standards which does not take them nearer to > guaranteeing the practical use of today's tools and needs of today's > programmers, is useless. > > A change to the C standards that goes against today's practice is worse > than useless. > > A change that differs from the C++ standards (except in regarding the > silly support for over-sized bit-fields) is worse than useless. > > A change that involves long, complicated reasoning about sizes and types > but provides enough implementation-defined wiggle room to fit with > today's implementations, is useless. The last thing the C standards > need is more language that is confusing, open to interpretation, vague, > and unhelpful to either compiler implementers or C programmers. > > If the C standard can't be changed to explicitly allow /all/ integer > types in bit-fields, then making any change at all here is a waste of > time and effort. > > If it can't be changed to mandate that the size of the allocation units > matches the size of the type specified by the programmer as the > bit-field type, then it must be left as it is (implementation-defined). > > If it can't be changed to mandate that the type of the bit-field matches > the type specified by the programmer, then it must be left as it is. I don't see an answer to my question anywhere in there. To be perhaps a bit clearer, when I compile this program with gcc or clang: #include #include int main(void) { struct has_bit_field { unsigned bit_field : 1; }; struct has_unsigned_char { unsigned char uc; }; printf("CHAR_BIT = %d\n", CHAR_BIT); printf("sizeof (unsigned) = %zu\n", sizeof (unsigned)); printf("sizeof (struct has_bit_field) = %zu\n", sizeof (struct has_bit_field)); printf("sizeof (struct has_unsigned_char) = %zu\n", sizeof (struct has_unsigned_char)); } the output on my system is: CHAR_BIT = 8 sizeof (unsigned) = 4 sizeof (struct has_bit_field) = 4 sizeof (struct has_unsigned_char) = 1 What is the rationale for a bit-field forcing the structure to be expanded to 4 bytes, when the bit-field itself is only 1 bit? Why does a 1-bit bit-field for a bigger structure size than an 8-bit unsigned char member? I understand that it's implementation-defined, probably ABI-defined, and that changing it would break existing code that depends on this behavior. I'm trying to understand why it was defined this way in the first place. I see nothing in the C standard that suggests this (though of course it does allow it). I would have assumed that both struct has_bit_field and struct has_unsigned_char could sensibly have a size of 1 byte. -- Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com Working, but not speaking, for Philips Healthcare void Void(void) { Void(); } /* The recursive call of the void */