K&R C Exercise 2-3: htoi — Convert Hex String to Integer

Exercise 2-3. Write the function htoi(s), which converts a string of hexadecimal digits (including an optional 0x or 0X) into its equivalent integer value. The allowable digits are 0 through 9, a through f, and A through F.

How htoi Works

Hexadecimal is simply base-16 positional notation. Converting from a hex string to an integer uses exactly the same accumulation pattern as atoi does for decimal: scan each digit left to right and maintain a running total with n = n * 16 + digit. The only new challenges are mapping three distinct character ranges (digits, lowercase letters, uppercase letters) to the values 0–15, and skipping an optional 0x/0X prefix before the main loop starts.

The character arithmetic is the elegant core of this solution. To convert a hex letter to a numeric value, subtract the base character and offset by the starting value of that range: 'a' - 'a' + 10 = 10, 'b' - 'a' + 10 = 11, …, 'f' - 'a' + 10 = 15. The same formula works for uppercase with 'A' as the base. Note that the function stops cleanly at the first invalid character — so htoi("0x1Z") correctly returns the value accumulated up to but not including 'Z'.

/* Compile: gcc -ansi -Wall ex2-3.c -o ex2-3 */
#include <stdio.h>

int htoi(const char s[]);

int main(void)
{
    printf("htoi(\"0x1A\")   = %d\n", htoi("0x1A"));
    printf("htoi(\"0XFF\")   = %d\n", htoi("0XFF"));
    printf("htoi(\"ff\")     = %d\n", htoi("ff"));
    printf("htoi(\"10\")     = %d\n", htoi("10"));
    printf("htoi(\"0x10\")   = %d\n", htoi("0x10"));
    return 0;
}

int htoi(const char s[])
{
    int n, i;

    n = 0;
    i = 0;

    /* skip optional 0x or 0X prefix */
    if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X'))
        i = 2;

    for (; s[i] != '\0'; ++i) {
        int digit;
        if (s[i] >= '0' && s[i] <= '9')
            digit = s[i] - '0';
        else if (s[i] >= 'a' && s[i] <= 'f')
            digit = s[i] - 'a' + 10;
        else if (s[i] >= 'A' && s[i] <= 'F')
            digit = s[i] - 'A' + 10;
        else
            break;  /* invalid character — stop */
        n = n * 16 + digit;
    }
    return n;
}

Compile and Run

gcc -ansi -Wall ex2-3.c -o ex2-3
./ex2-3

Sample Output

htoi("0x1A")   = 26
htoi("0XFF")   = 255
htoi("ff")     = 255
htoi("10")     = 16
htoi("0x10")   = 16

Quick sanity check: 0x1A = 1×16 + 10 = 26; 0xFF = 15×16 + 15 = 255; 0x10 = 1×16 + 0 = 16.

What This Exercise Teaches

  • Horner’s method for base conversion: n = n * 16 + digit accumulates a multi-digit number in one left-to-right pass — the same pattern used in atoi for base 10.
  • Character arithmetic: subtracting the base character maps a character to its ordinal position within a range. 'f' - 'a' + 10 gives 15 without any lookup table.
  • Multi-character prefix detection: you must check both s[0] and s[1] before advancing i by 2 — checking only the first character would incorrectly skip the leading zero in "01".
  • Graceful handling of invalid input: breaking on the first unrecognised character rather than returning an error code matches the behaviour of the standard library’s strtol(s, NULL, 16).

Standard Library Equivalent

Once you understand how htoi works, it is worth knowing that the standard library provides strtol from <stdlib.h> for this job:

#include <stdlib.h>
long val = strtol("0xFF", NULL, 16);  /* val == 255 */

The second argument, when non-NULL, receives a pointer to the first character that was not converted — a more informative version of the break in our loop. Writing htoi by hand first makes strtol‘s design immediately clear.

Set Up Your C Environment

To compile and run this solution, you need GCC installed. If you haven’t set up C on your machine yet:

← Exercise 2-2  | 
Chapter 2 Solutions  | 
Exercise 2-4 →

Book:

The C Programming Language, 2nd Ed — Kernighan & Ritchie

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>