1
\$\begingroup\$

Im not that good at C, so go easy on me, but I wanted to be able to have individual bit access in C without using a ton of functions to do bit manipulation on a uint8_t. Tell me where I could improve upon it

#include <stdio.h>
#pragma pack(1)
union bit_array {
 struct {
 unsigned char b8:1, b7:1, b6:1, b5:1, b4:1, b3:1, b2:1, b1:1;
 } bits;
 unsigned char value;
};
int main() {
 // Creates char with binary value 11111111
 union bit_array test = { 1, 1, 1, 1, 1, 1, 1, 1 };
 // Displays 11111111 (255)
 printf("%u\n", test.value);
 // Sets 8th bit of binary value to 0
 // 11111111 (255) -> 11111110 (254)
 test.bits.b8 = 0;
 // Displays 11111110 (254)
 printf("%u\n", test.value);
 
 return 0;
}
Sᴀᴍ Onᴇᴌᴀ
29.5k16 gold badges45 silver badges201 bronze badges
asked Apr 18, 2021 at 22:29
\$\endgroup\$
3
  • 5
    \$\begingroup\$ Your current code does not show how you intend to use the bitwise access. The main function should demonstrate exactly that, but in the code you posted, it doesn't. Without this information, we cannot tell you how to write really good code for this task. \$\endgroup\$ Commented Apr 18, 2021 at 23:22
  • 1
    \$\begingroup\$ @RolandIllig The reason why i like this implementation is because you can do a lot of things with it, but I have now updated my code to show one of them. \$\endgroup\$ Commented Apr 19, 2021 at 0:31
  • 1
    \$\begingroup\$ b0 - b7 probably are more understandable. {a, b, c, d, e, f, g, h} giving hgfedcba (if I read your code right) is also less evident. You set b8 but the bit to the right is set. At least if 11111110 means 254. 4. uint8_t is better than unsigned char. \$\endgroup\$ Commented Apr 19, 2021 at 6:34

2 Answers 2

4
\$\begingroup\$

This code is not portable. The order of bit-fields within a word is completely compiler-dependent, so the test that appears to work on one platform may give completely different results on another.

You have avoided a common trap of using signed 1-bit fields (which can hold values 0 and -1) - these unsigned ones are much better.

I don't think there's any need for the bits member to be named - I would use an anonymous member there.

The numbering of bits is unconventional - most programmers would expect b0 to be the least-significant bit, and b7 the most significant (corresponding to 20 and 27 value of those bits in integers).

The test would be better if it were self-checking - return with non-zero status if any of the expectations are not met. For example:

int main(void)
{
 int failures = 0;
 {
 /* First test */
 union bit_array test = { {1, 1, 1, 1, 1, 1, 1, 1} };
 if (test.value != 0xFF) {
 fprintf(stderr, "Test 1 expected 0xff, got %#04hhx\n", test.value);
 ++failures;
 }
 }
 {
 /* Second test */
 union bit_array test = { {1, 1, 1, 1, 1, 1, 1, 1} };
 test.bits.b8 = 0;
 if (test.value != 0xFE) {
 fprintf(stderr, "Test 2 expected 0xfe, got %#04hhx\n", test.value);
 ++failures;
 }
 }
 return failures != 0;
}

We need tests of the most-significant bit, and setting as well as resetting bits. I'll leave that as an exercise.

answered Apr 19, 2021 at 12:26
\$\endgroup\$
9
  • \$\begingroup\$ If the order of bit fields is dependant on system endianness, can I use a preprossecor directive to determine what order the struct's members are declared in? \$\endgroup\$ Commented Apr 19, 2021 at 18:07
  • \$\begingroup\$ It's not dependent on endianness; that's only tangentially related. I don't believe there's a reliable compile-time test - see this relevant Stack Overflow answer. \$\endgroup\$ Commented Apr 19, 2021 at 19:38
  • \$\begingroup\$ I thought that the bit field ordering was dependent on the endianness of the platform, and it was limited to high-order to low-order or low-order to high-order \$\endgroup\$ Commented Apr 19, 2021 at 20:38
  • 1
    \$\begingroup\$ @QuestionLimitGoBrrrrr The bit field ordering is not specified to match the byte endianness. \$\endgroup\$ Commented Apr 20, 2021 at 1:26
  • 1
    \$\begingroup\$ 4 then does make sense in %#04hhx. I find 0x%02X to deemphasize the prefix as lower case, but upper case digits clearer to read output and ditch the pesky # with its 0 exemption. I expect %#04hhx\n", 0 to print 0000. \$\endgroup\$ Commented Apr 20, 2021 at 7:40
2
\$\begingroup\$

I wanted to be able to have individual bit access in C without using a ton of functions to do bit manipulation on a uint8_t

All things with bit-fields invite implementation defined behaviors. Even using unsigned char is implementation defined.

A bit-field shall have a type that is a qualified or unqualified version of _Bool, signed int, unsigned int, or some other implementation-defined type. C17dr § 6.7.2.1 5


Tell me where I could improve upon it

It really is not that hard to form portable set/get bit functions.

Only 2 functions are needed: BitGet() and BitSet()

Sample unchecked code below uses unsigned as uint8_t itself is optional. Narrow uint8_t is not really needed to make generic set/get as much code will promote to unsigned/int anyway.

#define UNSIGNED_BIT_WIDTH (CHAR_BIT * sizeof(unsigned))
unsigned BitGet(unsigned x, unsigned index) {
 index %= UNSIGNED_BIT_WIDTH; // Likely just an 'and' operation
 return (x >> index) & 1;
}
void BitSet(unsigned x, unsigned index, bool value) {
 index %= UNSIGNED_BIT_WIDTH;
 if (value) {
 x |= 1u << index;
 } else {
 x &= ~(1u << index);
 }
}

Other alternatives include macros, inline, etc.


without using a ton of functions

I estimate the weight of the above function, using 0.2 GB/g, at about 6 pico-grams. 😉

Toby Speight
87.1k14 gold badges104 silver badges322 bronze badges
answered Apr 20, 2021 at 1:37
\$\endgroup\$
2
  • \$\begingroup\$ 5.9847 pico-grams... \$\endgroup\$ Commented Apr 20, 2021 at 3:59
  • \$\begingroup\$ @DavidC.Rankin What's a few femto-grams between friends? \$\endgroup\$ Commented Apr 20, 2021 at 7:27

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.