C: network, broadcast, and host count of a CIDR block
Use shifts and masks on a 32-bit address to describe a subnet.
On this page
C unsigned integers, sscanf, and bitwise operators.
- Use shifts and masks on a 32-bit address to describe a subnet.
- Predict the sample output and explain the state changes.
- Complete the practice task and check the boundary cases.
The concept
An IPv4 address is a 32-bit number written as four bytes. CIDR notation such as 172.16.5.130/20 states that the first 20 bits are the network prefix. The mask has 20 leading ones. ANDing the address with the mask gives the first address of the block, the network address.
ORing the network address with the inverted mask turns on all the host bits and gives the broadcast address. A block with h host bits contains 2^h addresses, of which 2^h − 2 are usable for hosts. For a /20 there are 12 host bits, so 4094 hosts. Use uint32_t, an unsigned type with exactly 32 bits, so that shifts are well defined.
Code example
#include <stdint.h>
#include <stdio.h>
static void print_ip(const char *label, uint32_t ip) {
printf("%s %u.%u.%u.%u\n", label, (unsigned)(ip >> 24), (unsigned)((ip >> 16) & 255u),
(unsigned)((ip >> 8) & 255u), (unsigned)(ip & 255u));
}
int main(void) {
unsigned a, b, c, d;
int prefix;
sscanf("172.16.5.130/20", "%u.%u.%u.%u/%d", &a, &b, &c, &d, &prefix);
uint32_t ip = (a << 24) | (b << 16) | (c << 8) | d;
uint32_t mask = prefix == 0 ? 0u : 0xFFFFFFFFu << (32 - prefix);
uint32_t network = ip & mask;
uint32_t broadcast = network | ~mask;
print_ip("mask ", mask);
print_ip("network ", network);
print_ip("broadcast", broadcast);
printf("hosts %u\n", (unsigned)((1u << (32 - prefix)) - 2u));
return 0;
}mask 255.255.240.0 network 172.16.0.0 broadcast 172.16.15.255 hosts 4094
Follow the code
Open the walkthrough to inspect the example one step at a time.
Show guided walkthrough
#include <stdint.h>#include <stdio.h> static void print_ip(const char *label, uint32_t ip) { printf("%s %u.%u.%u.%u\n", label, (unsigned)(ip >> 24), (unsigned)((ip >> 16) & 255u), (unsigned)((ip >> 8) & 255u), (unsigned)(ip & 255u));} int main(void) { unsigned a, b, c, d; int prefix; sscanf("172.16.5.130/20", "%u.%u.%u.%u/%d", &a, &b, &c, &d, &prefix); uint32_t ip = (a << 24) | (b << 16) | (c << 8) | d; uint32_t mask = prefix == 0 ? 0u : 0xFFFFFFFFu << (32 - prefix); uint32_t network = ip & mask; uint32_t broadcast = network | ~mask; print_ip("mask ", mask); print_ip("network ", network); print_ip("broadcast", broadcast); printf("hosts %u\n", (unsigned)((1u << (32 - prefix)) - 2u)); return 0;}Follow the example
Start to inspect the source, values, and output. These prepared steps explain the published sample; grouped loop steps describe multiple iterations.
View the complete dry-run table
| Step / line | What happens | State after step | Output so far |
|---|---|---|---|
| Parse the four bytes and the prefix length from the text. | a=172, b=16, c=5, d=130, prefix=20 | — | |
| 20 ones followed by 12 zeros. A shift by 32 would be undefined, so prefix 0 is handled separately. | mask = 255.255.240.0 | — | |
| Clear the host bits. The third byte 5 & 240 is 0. | network = 172.16.0.0 | — | |
| 2^12 − 2 addresses can be assigned to hosts. | network = 172.16.0.0 | mask 255.255.240.0 network 172.16.0.0 broadcast 172.16.15.255 hosts 4094 |
Behind the scenes
How C processes this code
The C compiler produces a native program. Variables occupy memory, and execution enters main.
Cost and common mistakes
O(1): a fixed number of operations.
- Always check the return value of sscanf in real code. Here the input is a fixed string that is known to be valid.
- Shifting by the width of the type (32) or more is undefined behaviour, so prefix 0 and prefix 32 need care.
- Do not use signed ints for addresses. Shifting into the sign bit and right-shifting negative values cause trouble.
Try it yourself
How many usable hosts does a /26 contain?
Reveal answer and reasoning
2^6 − 2 = 62 usable host addresses.