C: network, broadcast, and host count of a CIDR block

Use shifts and masks on a 32-bit address to describe a subnet.

C
#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;
}
Output
mask      255.255.240.0
network   172.16.0.0
broadcast 172.16.15.255
hosts     4094

Explanation

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.

Explore the dry run
Guided walkthroughReady
1#include <stdint.h>
2#include <stdio.h>
3
4static void print_ip(const char *label, uint32_t ip) {
5 printf("%s %u.%u.%u.%u\n", label, (unsigned)(ip >> 24), (unsigned)((ip >> 16) & 255u),
6 (unsigned)((ip >> 8) & 255u), (unsigned)(ip & 255u));
7}
8
9int main(void) {
10 unsigned a, b, c, d;
11 int prefix;
12 sscanf("172.16.5.130/20", "%u.%u.%u.%u/%d", &a, &b, &c, &d, &prefix);
13
14 uint32_t ip = (a << 24) | (b << 16) | (c << 8) | d;
15 uint32_t mask = prefix == 0 ? 0u : 0xFFFFFFFFu << (32 - prefix);
16 uint32_t network = ip & mask;
17 uint32_t broadcast = network | ~mask;
18
19 print_ip("mask ", mask);
20 print_ip("network ", network);
21 print_ip("broadcast", broadcast);
22 printf("hosts %u\n", (unsigned)((1u << (32 - prefix)) - 2u));
23 return 0;
24}

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
Prepared trace of the published program. Some rows group repeated iterations.
Step / lineWhat happensState after stepOutput 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

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.

Further reading