An IPv4 address is displayed as four decimal octets separated by dots (e.g., 192.168.1.1) but is stored internally as a single 32-bit unsigned integer. Converting between the dotted-decimal string and the integer form is a fundamental networking operation — inet_addr() in the POSIX socket API does exactly this. Understanding how to do it manually shows you how bit-shifting and bitwise OR combine four byte values into one 32-bit word.
The original post used unions and lacked validation. This rewrite uses explicit bit-shifting with full input validation, and prints the result in decimal, hex, and binary for full clarity.
The Conversion Formula
For address A.B.C.D:
32-bit int = (A << 24) | (B << 16) | (C << 8) | D
Example: 192.168.1.1
192 = 0xC0 → shift left 24 bits → 0xC0000000
168 = 0xA8 → shift left 16 bits → 0x00A80000
1 = 0x01 → shift left 8 bits → 0x00000100
1 = 0x01 → no shift → 0x00000001
OR result → 0xC0A80101 = 3,232,235,777
C Program to Convert IP Address to 32-bit Integer
/* Convert IPv4 address to 32-bit unsigned long integer
* Compile: gcc -ansi -Wall -Wextra ip_to_int.c -o ip_to_int */
#include <stdio.h>
unsigned long ip_to_long(int a, int b, int c, int d)
{
return ((unsigned long)a << 24) |
((unsigned long)b << 16) |
((unsigned long)c << 8) |
(unsigned long)d;
}
int octet_valid(int v)
{
return (v >= 0 && v <= 255);
}
int main(void)
{
int a, b, c, d;
unsigned long ip_int;
printf("Enter IPv4 address (e.g. 192.168.1.1): ");
if (scanf("%d.%d.%d.%d", &a, &b, &c, &d) != 4) {
printf("Error: invalid format. Use A.B.C.D\n");
return 1;
}
if (!octet_valid(a) || !octet_valid(b) || !octet_valid(c) || !octet_valid(d)) {
printf("Error: each octet must be 0-255.\n");
return 1;
}
ip_int = ip_to_long(a, b, c, d);
printf("IP address : %d.%d.%d.%d\n", a, b, c, d);
printf("Decimal : %lu\n", ip_int);
printf("Hexadecimal : 0x%08lX\n", ip_int);
printf("Binary : ");
{
int bit;
for (bit = 31; bit >= 0; bit--) {
printf("%lu", (ip_int >> bit) & 1);
if (bit > 0 && bit % 8 == 0) printf(".");
}
}
printf("\n");
return 0;
}
How to Compile and Run
gcc -ansi -Wall -Wextra ip_to_int.c -o ip_to_int
./ip_to_int
Sample Output
# 192.168.1.1 (private LAN address) Enter IPv4 address (e.g. 192.168.1.1): 192.168.1.1 IP address : 192.168.1.1 Decimal : 3232235777 Hexadecimal : 0xC0A80101 Binary : 11000000.10101000.00000001.00000001 # 0.0.0.0 (any/unspecified address) IP address : 0.0.0.0 Decimal : 0 Hexadecimal : 0x00000000 Binary : 00000000.00000000.00000000.00000000 # 255.255.255.255 (broadcast address) IP address : 255.255.255.255 Decimal : 4294967295 Hexadecimal : 0xFFFFFFFF Binary : 11111111.11111111.11111111.11111111 # 10.0.0.1 (private class A) IP address : 10.0.0.1 Decimal : 167772161 Hexadecimal : 0x0A000001 Binary : 00001010.00000000.00000000.00000001
Bit-Shift Breakdown: 192.168.1.1 = 0xC0A80101
| Octet | Value | Hex | Shift | Result |
|---|---|---|---|---|
| A = 192 | 192 | 0xC0 | << 24 | 0xC0000000 |
| B = 168 | 168 | 0xA8 | << 16 | 0x00A80000 |
| C = 1 | 1 | 0x01 | << 8 | 0x00000100 |
| D = 1 | 1 | 0x01 | none | 0x00000001 |
| OR all together | 0xC0A80101 = 3,232,235,777 | |||
Code Explanation
- unsigned long — IPv4 addresses span 0 to 4,294,967,295 (2³²−1). A signed int can only hold up to 2,147,483,647 (2³¹−1). Using unsigned long avoids overflow for addresses with the high bit set (all addresses with first octet ≥ 128). On 64-bit Linux, unsigned long is 64 bits, so there is no overflow risk.
- Cast before shifting: (unsigned long)a << 24 — without the cast, shifting an int value left 24 bits could overflow a 32-bit int if a ≥ 128 (the result would need the 32nd bit). Casting to unsigned long first prevents this.
- Bitwise OR to combine — the
|operator combines the four non-overlapping bit fields. Each octet occupies exactly 8 bits in a non-overlapping position, so OR (not addition) is the correct operator here. They produce the same result when non-overlapping, but OR is semantically correct for “placing” values into specific bit positions. - Binary output with dot separators — printing a period every 8 bits (
bit % 8 == 0) splits the binary output into four octets, matching the visual structure of the dotted-decimal notation. - scanf(“%d.%d.%d.%d”) returns 4 — scanf returns the number of items successfully matched. Checking the return value catches formats like “192.168” that would partially match but leave c and d uninitialized.
What This Program Teaches
- Bit-shifting to build multi-byte values — the same technique packs fields into network packets, image pixels (RGB: 0xRRGGBB), and hardware registers. Left-shift positions a byte; OR places it.
- Unsigned arithmetic for networking — network values are always unsigned. Using signed int for an IP address causes subtle bugs with addresses where the MSB is 1 (first octet ≥ 128, e.g., 192.x.x.x) because signed right-shift is implementation-defined and signed overflow is undefined behavior.
- Extracting bits for display —
(ip_int >> bit) & 1extracts the value of any single bit. Right-shift to bring the bit to position 0, then AND with 1 to isolate it. This pattern appears in every binary display routine in C. - Reversing the conversion — to go back from a 32-bit int to dotted-decimal: A=(ip>>24)&0xFF, B=(ip>>16)&0xFF, C=(ip>>8)&0xFF, D=ip&0xFF.
Related Programs
- Binary to Decimal/Octal/Hex in C
- 2’s Complement in C
- Bit Flipping in C
- Endianness and Pointer Arithmetic — C Aptitude
- Binary to Decimal in C
Recommended book:
The C Programming Language — Kernighan & Ritchie (India) |
(US)
|
C Programming: A Modern Approach — K.N. King (India) |
(US)
Practice what you learned: C Aptitude Questions — or try our C Programming Quiz App on Android.