What does the
volatilekeyword do?volatiletells the compiler a variable can change outside the visible control flow, so every read and write must happen, in order, and cannot be cached or removed. Use it for hardware registers, variables changed by interrupt or signal handlers (volatile sig_atomic_t), and locals that must survivelongjmp.volatile int data_ready = 0; /* set by an ISR */ void wait_for_data(void) { while (!data_ready) { } /* without volatile this can become an infinite loop */ }It does not make operations atomic or synchronize threads; use C11
_Atomicor a mutex. MSVC's/volatile:ms, the default on x86 and x64, adds acquire and release ordering as a Microsoft extension; see the /volatile docs.Can a variable be both
constandvolatile?Yes.
constmeans your code may not write it;volatilemeans something else may change it. A read-only hardware status register is the textbook case.#define STATUS_REG (*(const volatile uint32_t *) 0x40021000u) while ((STATUS_REG & 0x1u) == 0) { /* wait until the device sets bit 0 */ }How do you set, clear, toggle and check a bit in C?
Build a mask with
1u << n, then OR to set, AND with the inverted mask to clear, XOR to toggle and AND to test.C#include <stdio.h> #define BIT(n) (1u << (n)) #define SET_BIT(x, n) ((x) |= BIT(n)) #define CLEAR_BIT(x, n) ((x) &= ~BIT(n)) #define TOGGLE_BIT(x, n) ((x) ^= BIT(n)) #define CHECK_BIT(x, n) (((x) >> (n)) & 1u) int main(void) { unsigned int reg = 0; SET_BIT(reg, 3); SET_BIT(reg, 0); printf("after set: 0x%02X\n", reg); CLEAR_BIT(reg, 0); printf("after clear: 0x%02X\n", reg); TOGGLE_BIT(reg, 7); printf("after toggle: 0x%02X\n", reg); printf("bit 3 is %u, bit 1 is %u\n", CHECK_BIT(reg, 3), CHECK_BIT(reg, 1)); return 0; }It prints
0x09,0x08,0x88andbit 3 is 1, bit 1 is 0. Use1u:1 << 31on a 32-bitintis undefined behavior.Practice it: Power of Two
What is
0x0Fafter setting bit 6 and clearing bit 0?C#include <stdio.h> int main(void) { unsigned int x = 0x0F; x |= 1u << 6; x &= ~(1u << 0); printf("0x%X\n", x); return 0; }It prints
0x4E.0x0Fis0000 1111; setting bit 6 gives0100 1111, and clearing bit 0 gives0100 1110. Bits count from 0.How do you count the set bits in an integer?
Brian Kernighan's method:
n & (n - 1)clears the lowest set bit, so count iterations untilnis 0, once per set bit.C#include <stdio.h> int popcount(unsigned int n) { int count = 0; while (n) { n &= n - 1; /* drop the lowest set bit */ count++; } return count; } int main(void) { unsigned int values[] = {0, 1, 7, 8, 255, 0xF0F0}; for (int i = 0; i < 6; i++) printf("%u has %d set bits (builtin says %d)\n", values[i], popcount(values[i]), __builtin_popcount(values[i])); return 0; }In production use
__builtin_popcount, one instruction where the target has it (on x86-64 only with-mpopcntor a matching-march), or C23'sstdc_count_ones.Practice it: Counting Bits
How can a union tell you your machine's byte order?
C#include <stdio.h> int main(void) { union { unsigned int i; unsigned char c[sizeof(unsigned int)]; } u; u.i = 1; printf("%d\n", u.c[0]); return 0; }It prints
1. On a little-endian machine (x86, and ARM as Linux normally runs it) the least significant byte comes first; big-endian would print 0. Byte order is implementation-defined. Reading another union member is allowed in C but undefined in C++.How do you swap two numbers without a temporary variable?
With XOR:
a ^= b; b ^= a; a ^= b;, becausex ^ xis 0 andx ^ 0isx.C#include <stdio.h> int main(void) { int a = 12, b = 25; a ^= b; b ^= a; a ^= b; printf("a = %d, b = %d\n", a, b); return 0; }It prints
a = 25, b = 12. If both names refer to the same object, the first XOR zeroes it. A temporary variable is clearer and as fast.Practice it: Single Number
Why do embedded programmers use
uint8_tanduint32_tinstead ofint?Because
intandlongvary in width (anintis 16 bits on some microcontrollers), while registers and protocols have exact sizes.<stdint.h>(C99) providesuint8_t,uint32_tand the rest. They are optional: a platform with no 8-bit type has nouint8_t, whileint_least16_tandint_fast16_talways exist.What should you avoid inside an interrupt service routine?
Keep it short: record the event and let the main loop do the work. Avoid blocking, non-reentrant calls such as
printfandmalloc, and floating point where the ISR does not save the FPU registers. Shared variables must bevolatile, and multi-byte ones need interrupts disabled (or atomic access) around the main loop's read.volatile uint8_t rx_byte; volatile bool rx_ready; void UART_IRQHandler(void) { rx_byte = UART->DR; /* on many UARTs, reading the data register clears the interrupt */ rx_ready = true; }What happens when you shift a negative number right or left in C?
Right-shifting a negative signed value is implementation-defined; GCC and most compilers copy the sign bit, so
-8 >> 1is-4. Left-shifting a negative value is undefined behavior in C17.C#include <stdio.h> int main(void) { int n = -8; unsigned int u = 0xF0000000u; printf("%d\n", n >> 1); /* implementation-defined, GCC: -4 */ printf("0x%X\n", u >> 4); /* unsigned: always fills with zeros */ return 0; }On this GCC build it prints
-4and0xF000000. Shifting by a negative count or by at least the type's width is undefined. Do bit work on unsigned types.
Embedded C Interview Questions
Where firmware interviews start: volatile, register bit operations, fixed-width types and endianness.