C Input Validation

C Input Validation: Securing User Input

Input validation is the process of ensuring that the data entered by a user or received from an external source meets your program's expectations before it is processed. In C, poor input validation is one of the leading causes of application crashes, infinite loops, and severe security vulnerabilities like buffer overflows.

Because C does not have built-in exception handling like Java or Python, it is entirely up to the developer to anticipate and handle incorrect input safely.

Why is Input Validation Important?


1. The Dangers of scanf()

The scanf() function is commonly taught to beginners for reading input, but it is notoriously unsafe if not used perfectly.

If you ask for an integer using scanf("%d", &num); and the user types "Hello", scanf() will fail to read the input. The characters "Hello" will remain stuck in the input buffer, causing subsequent scanf() calls to instantly fail and potentially throw your program into an infinite loop.

Example of a scanf Infinite Loop Pitfall:

Unsafe Input Example (Do Not Use in Production):

#include <stdio.h>

int main() { int age; printf("Enter your age: "); // If a user types letters here, the program breaks! while (scanf("%d", &age) != 1) { printf("Invalid input. Please enter a valid number: "); // The invalid letters are still in the buffer, // so this loop will run forever! } printf("You are %d years old.\n", age); return 0; }


2. Clearing the Input Buffer

To fix the issue above, we must clear the leftover characters from the standard input (stdin) buffer whenever scanf() fails. We can do this by reading characters one by one until we hit a newline (\n) or the end of the file (EOF).

Clearing the Buffer Example:

#include <stdio.h>

void clearBuffer() { int c; // Read characters until a newline or EOF is found while ((c = getchar()) != '\n' && c != EOF) { // Discard character } }

int main() { int age; printf("Enter your age: "); // scanf returns the number of successfully read items while (scanf("%d", &age) != 1) { printf("Invalid input. That is not a number.\n"); clearBuffer(); // Clear the bad input! printf("Please try again: "); } // Clear any trailing characters (like the newline) after a successful read clearBuffer(); printf("Valid age entered: %d\n", age); return 0; }


3. The Gold Standard: fgets() and sscanf()

While clearing the buffer works, the professional standard for reading input in C is to avoid scanf() entirely. Instead, use fgets() to read the entire line of input as a string, and then parse that string using sscanf() or strtol().

This completely eliminates the buffer-clogging issue because fgets() aggressively consumes the newline character and stops.

Using fgets and sscanf Example:

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

int main() { char inputBuffer[100]; int score; while (1) { printf("Enter your test score (0-100): "); // 1. Safely read a line of text (up to 99 chars + null terminator) if (fgets(inputBuffer, sizeof(inputBuffer), stdin) == NULL) { printf("Error reading input.\n"); return 1; } // 2. Parse the string into an integer if (sscanf(inputBuffer, "%d", &score) == 1) { // 3. Range Validation if (score >= 0 && score <= 100) { break; // Valid input, exit loop } else { printf("Error: Score must be between 0 and 100.\n"); } } else { printf("Error: Please enter a numeric value.\n"); } } printf("Score recorded: %d\n", score); return 0; }

Why is this better?

  1. Buffer Overflow Protection: fgets requires you to specify the maximum size (sizeof(inputBuffer)), making buffer overflows virtually impossible.
  2. No Leftover Garbage: fgets grabs the whole line, including the enter key (\n), so nothing is left behind to corrupt future input requests.

4. Validating String Lengths

When validating strings (like names or passwords), you must ensure the user hasn't entered too much text, and you must safely remove the trailing newline character that fgets includes.

String Validation Example:

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

int main() { char username[15]; // Max 14 chars + 1 null terminator printf("Enter a username (max 10 characters): "); if (fgets(username, sizeof(username), stdin) != NULL) { // Remove the newline character if it exists username[strcspn(username, "\n")] = 0; // Validate length if (strlen(username) == 0) { printf("Username cannot be empty.\n"); } else if (strlen(username) > 10) { printf("Error: Username is too long! Length: %lu\n", strlen(username)); } else { printf("Welcome, %s!\n", username); } } return 0; }

Pro Tip: strcspn(string, "\n") is a highly efficient way to find the index of the newline character in a string and replace it with a null terminator (0 or '\0').


5. Summary of Best Practices

To write industrial-strength, production-ready C code, follow these rules:

  1. Never use gets(): It has been removed from modern C standards because it causes catastrophic buffer overflows.
  2. Avoid scanf() for user interaction: It is brittle. Use fgets() + sscanf() instead.
  3. Always check return values: Functions like scanf and sscanf return the number of items successfully parsed. Always check if this matches your expectation.
  4. Validate Ranges: Don't just check if the input is an integer; check if it makes logical sense (e.g., age cannot be -5 or 5000).

Exercise 1 of 2

?

Why is fgets() preferred over scanf() for reading string input?

Exercise 2 of 2

?

If scanf("%d", &num); fails because the user entered letters, what happens to those letters?