DEV Community

Cover image for #1 Setting or Clearing a Bit in C
Hrishikesh Karande
Hrishikesh Karande

Posted on • Edited on

#1 Setting or Clearing a Bit in C

When I first got the problem statement on EWskills — “set or clear a specific bit in an 8-bit register using C” — I thought it would be a straightforward coding exercise. But as I worked through it, I realized I was uncovering some important concepts that are directly related to firmware development and low-level C programming.

#include <stdio.h>

unsigned char modifyBit(unsigned char reg, int pos, int mode) {
    // Write your code here
    if (mode == 1) {
        //Set the bit for the position pos
        reg |= (1 << pos);
    } else if (mode == 0)  {
        //Clear the bit at the position pose
        reg &= ~(1 << pos);
    }
    return reg;

}

int main() {
    unsigned char reg; // This is your 8-bit register
    int pos, mode;
    scanf("%hhu %d %d", &reg, &pos, &mode);
    printf("%d", modifyBit(reg, pos, mode));
    return 0;
}
Enter fullscreen mode Exit fullscreen mode

Here are the key things I learned:

1. Registers Are Just Variables in Memory

In firmware development, hardware registers (like GPIO control registers, ADC data registers, etc.) are essentially memory locations that hold specific values. To experiment, I treated an 8-bit register as a normal C variable:

unsigned char reg;
Enter fullscreen mode Exit fullscreen mode

This taught me that C lets us model hardware registers using basic types, but how we choose the type matters a lot.

2. The Role of char in C

At first, I was confused — if I declare unsigned char reg;, am I saying reg is a “character”?

The answer is: no, not really.

In C, char, signed char, and unsigned char are all integer types. The keyword “char” simply reflects the size: 1 byte (typically 8 bits).

  • If I print a char with %c, it looks like a character (based on its ASCII value).
  • If I print it with %d or %u, it shows the integer value it’s holding.

This clarified why embedded programmers often use unsigned char (or, even better, uint8_t) to represent hardware registers: it’s exactly 8 bits and works perfectly for bitwise operations.

3. Using for Portability

One major learning was that relying on char could lead to portability issues, since the C standard only guarantees that char is at least 8 bits.

For portable and explicit code, I should use:

#include <stdint.h>
uint8_t reg;
Enter fullscreen mode Exit fullscreen mode

This way, I am 100% sure that the “register” I’m working with is exactly 8 bits, no matter the platform or compiler.

I also discovered that for input/output with uint8_t, I need macros like SCNu8 and PRIu8 from <inttypes.h> instead of %hhu.

4. Bitwise Operations Are the Heart of Firmware

To modify bits, I used:

  • reg |= (1 << pos); → set a bit
  • reg &= ~(1 << pos); → clear a bit

These tiny one-liners are the building blocks of firmware programming. In real hardware, they let us do things like:

  • Turn an LED on or off.
  • Enable/disable an interrupt.
  • Configure control bits in a peripheral register.

This exercise made me appreciate how bitwise operations are not just theory — they’re the language of hardware control.

5. Format Specifiers and %hhu

Another thing I learned is how format specifiers in C work. %u is for unsigned int, but since my variable was an unsigned char, I needed %hhu. The hh modifier tells C to treat the input/output as the smallest integer type.

Later, with uint8_t, I learned that using portable macros like SCNu8 and PRIu8 is safer and more future-proof.

Final Thoughts

This small problem gave me a big set of takeaways:

  • How C types map to register sizes.
  • Why <stdint.h> is a must for portable firmware code.
  • The practical use of bitwise operations in controlling hardware.
  • The importance of correct format specifiers when dealing with small integer types.

In firmware development, tiny details matter a lot — a single wrong type or operator can make the difference between a working driver and hours of debugging. This exercise reinforced the discipline of writing clear, portable, and hardware-friendly C code.

Top comments (0)