Skip to content

Beginner · Strings in C · topic 18 of 60 · 25 min

Reading strings safely

Explanation

Read lines of text with fgets(buf, sizeof buf, stdin): it never writes past the buffer, keeps the newline (remove it with buf[strcspn(buf, "\n")] = '\0'), and returns NULL at end of input. gets() was removed from C in 2011 because it cannot be used safely. scanf("%s") stops at spaces and needs a width. Convert text to numbers with strtol and check for errors, instead of atoi.

Why reading text is dangerous in C

A program cannot know in advance how long a user's input will be. If it reads into a 20-byte array and the user types 100 characters, a careless function writes the extra 80 bytes over whatever comes next in memory. Attackers deliberately send long inputs to exploit this. Safe input means always telling the reading function how big the buffer is.

fgets: the standard safe way

c
#include <stdio.h>
#include <string.h>

int main(void) {
    char name[30];
    printf("Full name: ");
    if (fgets(name, sizeof name, stdin) == NULL) {   /* NULL: end of input or error */
        return 1;
    }
    name[strcspn(name, "\n")] = '\0';                 /* remove the newline if present */
    printf("Hello, %s!\n", name);                     /* spaces are kept: "Ravi Kumar" */
    return 0;
}

fgets reads up to size - 1 characters, stops after a newline, and always adds \0. If the line is longer than the buffer, the rest stays in the input for the next read; check whether the newline was found if that matters.

Careful: Never use gets(). It reads a line with no limit at all, so any long input overflows the buffer. It was deprecated in C99 and REMOVED in C11; modern compilers warn or refuse. The 1988 Morris worm, one of the first internet worms, spread partly through a gets() overflow in a Unix server program.

scanf with strings

c
char city[20];
scanf("%19s", city);          /* reads one word, at most 19 chars; stops at a space */
char line[100];
scanf(" %99[^\n]", line);     /* reads up to a newline, skipping leading whitespace (a "scanset") */
Works, but fgets plus parsing is easier to get right.

Converting text to numbers: strtol

c
#include <errno.h>
#include <stdlib.h>

int parse_int(const char *text, long *out) {
    char *end;
    errno = 0;
    long v = strtol(text, &end, 10);         /* base 10 */
    if (end == text) return 0;               /* no digits at all */
    while (*end == ' ' || *end == '\n') end++;
    if (*end != '\0') return 0;              /* junk after the number, e.g. "12kg" */
    if (errno == ERANGE) return 0;           /* too big for long */
    *out = v;
    return 1;
}

atoi("12kg") returns 12 and atoi("abc") returns 0, with no way to know the input was bad, and its behaviour on overflow is undefined. strtol reports where parsing stopped (end) and sets errno to ERANGE on overflow, so you can reject bad input. Use strtod for decimals.

A robust input pattern

  1. Read a whole line with fgets into a big enough buffer.
  2. Remove the newline.
  3. Parse it with strtol / strtod / sscanf and check every result.
  4. If it fails, print a clear message and ask again (in a loop) or exit with an error code.
Think of it like this: fgets is like a form with a fixed number of boxes for each letter: you cannot write more letters than there are boxes. gets is a form with an "unlimited" line that actually ends at the edge of the paper, and anything longer is written on the next person's form.
Tip: When testing input code, try an empty line, a very long line, letters where numbers are expected, and pressing Ctrl+D (Linux/macOS) or Ctrl+Z then Enter (Windows) to send end-of-input.

Need help?

Stuck on a word or an idea? Search this course, or every course, and jump straight to the explanation or a worked question.

Try:

Watch the lesson

Reading strings safely · 1/6

Safe string input

In this lesson you will read text input safely in C.

Prefer a video by a teacher? Find free videos on “c fgets vs gets vs scanf string input strtol” on YouTube ↗

In real life: Reading a quantity at a pharmacy counter

A pharmacy billing tool reads the quantity for each medicine. Staff sometimes type "2 strips" or press Enter by mistake. Reading a line with fgets and parsing with strtol lets the program reject bad input politely instead of billing a wrong quantity.

c
char line[32];
long qty;
for (;;) {
    printf("Quantity: ");
    if (!fgets(line, sizeof line, stdin)) return 1;
    if (parse_int(line, &qty) && qty > 0 && qty <= 100) break;
    printf("Please enter a whole number from 1 to 100.\n");
}

Practice (0/3 done)

Task 1 · Question

The user types "Ravi Kumar" and Enter. What does name hold after scanf("%19s", name)?

Task 2 · Code · Javascript

The same strict number-parsing logic as strtol with an end check, in JavaScript: write parseQuantity(text) that accepts optional surrounding spaces and a newline, then only digits, and returns the number; return null for empty text, junk like "2 strips" or "12kg", and anything longer than 9 digits (to avoid overflow).

Task 3 · Practice

Write a C program that asks for a 6-digit PIN code until a valid one is entered: read with fgets, remove the newline, check the length is exactly 6, every character is a digit (isdigit, with an unsigned char cast) and the first is not '0'. Then ask for an age and parse it with strtol, rejecting junk and values outside 1–120.

Quick quiz

Question 1

1.Why was gets() removed from C11?

Question 2

2.What does fgets keep that you usually remove?

Question 3

3.What does atoi("abc") return?

Question 4

4.fgets returns NULL when…

Read more (free)

Open a question to see the answer and why. The first ones are this topic's own quiz, so try the quiz above before you peek.

Why was gets() removed from C11?

Answer: It cannot limit how much it writes, so it always risks overflow

There is no way to use gets safely.

What does fgets keep that you usually remove?

Answer: The trailing newline

Remove it with strcspn.

What does atoi("abc") return?

Answer: 0, indistinguishable from the input "0"

That is why strtol is preferred.

fgets returns NULL when…

Answer: End of input is reached (or an error occurs) before reading anything

Check it before using the buffer.

What does scanf return?Related

Answer: The number of items successfully read (or EOF)

Check it against the expected count.

From Input and output: printf and scanf

Why is overwriting the return address dangerous?Related

Answer: It can redirect execution to an attacker's choice

Classic stack smashing.

From Buffer overflows and safe string handling

Which is the best way to handle a typo in user input?Related

Answer: match on the parse result and ask again

Matching on Ok and Err lets you recover gracefully.

From Mini project: reading input from the user

Which function always respects the buffer size and terminates the result?Related

Answer: snprintf

snprintf writes at most size - 1 chars plus \0.

From The <string.h> functions

Pass the quiz (60% or more) or mark this topic as done when you are ready.

Your progress is saved on this device only.

Next steps

  1. 2 more topics in “Strings in C” before its exam: Characters and <ctype.h>, Buffer overflows and safe string handling.
  2. Module 4 exam: Strings in C · 12 questions · 25 min, without help. 5 topics of this module still to finish first.
  3. Then the next module, Pointers. 60 topics left in the course before the final exam.

← Previous: The <string.h> functions