C · Course navigation

C: validating numeric input with strtol

Accept only text that is entirely a number in the allowed range, and reject everything else.

On this page
Before you start

C strings, pointers, and errno.

  • Accept only text that is entirely a number in the allowed range, and reject everything else.
  • Predict the sample output and explain the state changes.
  • Complete the practice task and check the boundary cases.

The concept

Input from users, files, and the network cannot be trusted. Validate it: check its form, that the whole text was used, and that the value is in range. atoi is unsafe for this because it cannot report errors at all. It returns 0 for text such as abc.

strtol converts text to a long and reports where it stopped through an end pointer. The text is a complete number only if the end pointer reaches the terminating zero. errno becomes ERANGE if the value does not fit in a long. strtol also skips leading white space, so a strict validator rejects that explicitly.

Code example

C
#include <ctype.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>

static int parse_port(const char *text, int *out) {
  if (text[0] == '\0' || isspace((unsigned char)text[0])) {
    return 0;
  }
  char *end;
  errno = 0;
  long value = strtol(text, &end, 10);
  if (*end != '\0' || errno == ERANGE || value < 1 || value > 65535) {
    return 0;
  }
  *out = (int)value;
  return 1;
}

int main(void) {
  const char *inputs[] = {"8080", "0", "70000", "80a", "", " 22", "99999999999999999999"};
  for (int i = 0; i < 7; i++) {
    int port;
    if (parse_port(inputs[i], &port)) {
      printf("'%s' -> %d\n", inputs[i], port);
    } else {
      printf("'%s' -> rejected\n", inputs[i]);
    }
  }
  return 0;
}
Output
'8080' -> 8080
'0' -> rejected
'70000' -> rejected
'80a' -> rejected
'' -> rejected
' 22' -> rejected
'99999999999999999999' -> rejected

Follow the code

Open the walkthrough to inspect the example one step at a time.

Show guided walkthrough
Guided walkthroughReady
1#include <ctype.h>
2#include <errno.h>
3#include <stdio.h>
4#include <stdlib.h>
5
6static int parse_port(const char *text, int *out) {
7 if (text[0] == '\0' || isspace((unsigned char)text[0])) {
8 return 0;
9 }
10 char *end;
11 errno = 0;
12 long value = strtol(text, &end, 10);
13 if (*end != '\0' || errno == ERANGE || value < 1 || value > 65535) {
14 return 0;
15 }
16 *out = (int)value;
17 return 1;
18}
19
20int main(void) {
21 const char *inputs[] = {"8080", "0", "70000", "80a", "", " 22", "99999999999999999999"};
22 for (int i = 0; i < 7; i++) {
23 int port;
24 if (parse_port(inputs[i], &port)) {
25 printf("'%s' -> %d\n", inputs[i], port);
26 } else {
27 printf("'%s' -> rejected\n", inputs[i]);
28 }
29 }
30 return 0;
31}

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
Reject empty text and leading white space up front, since strtol would skip the spaces.—
—
Convert in base 10; end points at the first character that was not used.—
—
Reject trailing characters, overflow, and out-of-range values.—
—
Only 8080 passes all checks.—
'8080' -> 8080
'0' -> rejected
'70000' -> rejected
'80a' -> rejected
'' -> rejected
' 22' -> rejected
'99999999999999999999' -> rejected

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(n) for n characters.

  • errno must be set to 0 before the call, because strtol only sets it on failure.
  • Cast the char to unsigned char before passing it to isspace, or negative values cause undefined behaviour.
  • A valid number is still untrusted. Use it only for its intended purpose and range.

Try it yourself

Why does '80a' fail?

Reveal answer and reasoning

strtol stops at the letter a, so end does not point to the terminating zero, which means part of the text was not a number.

Further reading