CoreTechED

Loading

Master C fundamentals with practice problems: variables, data types, I/O, operators, conditionals, and loops. Full C99 code with edge cases.

C Fundamental Practice Problems: Complete Guide

C Fundamental Practice Problems: A Comprehensive Tutorial

Learning C is not about memorizing syntax—it’s about building muscle memory through practice. This tutorial is a structured practice guide covering the fundamental concepts every C programmer must master: variables, data types, input/output, format specifiers, operators, conditional statements, and loops.

Each section explains the concept, then walks through a representative practice problem with a complete, portable C99/C11 solution and expected output.


Conceptual Introduction & Fundamentals

Why Practice Fundamentals?

Fundamentals are the foundation of every C program, whether it’s 20 lines or 20,000. Weak fundamentals show up as:

  • Off-by-one errors in loops.
  • Format specifier mismatches causing undefined behavior.
  • Buffer overflows from unsafe input functions.
  • Silent type conversion bugs in arithmetic.
  • Incorrect edge-case handling in conditionals.

The Fundamental Topics

TopicPurpose
VariablesNamed storage for values
Data TypesSize and interpretation of values
Input / OutputCommunicating with the user
Format SpecifiersControlling how data is printed or read
OperatorsArithmetic, relational, logical, bitwise
Conditional StatementsBranching logic (if, switch)
LoopsRepetition (for, while, do-while)

Difficulty Progression

  • Easy: Printing, variables, simple arithmetic, I/O.
  • Medium: Control flow, digit manipulation, number theory basics.

Syntax & Core Mechanics

1. Variables and Data Types

c

int age = 25; double price = 19.99; char grade = 'A';

Use int for whole numbers, double for decimals, char for single characters. Prefer double over float for computation; use long long when values may exceed int.

2. Input with fgets and Parsing

Never use scanf("%s", ...) or gets for strings. Prefer fgets plus strtol/strtod for robust parsing.

c

char buffer[64]; if (fgets(buffer, sizeof(buffer), stdin) == NULL) return 1; int value = (int)strtol(buffer, NULL, 10);

3. Output with printf

Match the specifier to the type:

TypeSpecifier
int%d
unsigned int%u
long long%lld
double%f, %lf, %g
char%c
String%s
size_t%zu
Pointer%p

4. Operators

c

int a = 7, b = 3; int q = a / b; /* 2 — integer division */ int r = a % b; /* 1 */ double f = (double)a / b; /* 2.333... */

Use casting to force floating-point division.

5. Conditionals and Loops

c

if (a > b) { /* ... */ } else if (a == b) { /* ... */ } else { /* ... */ } for (int i = 0; i < n; i++) { /* ... */ } while (cond) { /* ... */ } do { /* ... */ } while (cond);

Full Practical Code Example with Explanation

Below are eight representative problems covering the fundamentals, each a self-contained C99 program with verified output.

Problem 1: Print “GeeksforGeeks” with Quotes

Goal: Print a string including literal double quotes.

c

#include <stdio.h> int main(void) { /* Escape double quotes with backslash */ printf("\"GeeksforGeeks\"\n"); return 0; }

Expected output:

text

"GeeksforGeeks"

Notes: \" inside a string literal produces a literal ".

Problem 2: Swap Two Numbers (Without Temp for Integers)

Goal: Swap a and b.

c

#include <stdio.h> int main(void) { int a = 5, b = 7; /* Arithmetic swap (works for ints; watch overflow) */ a = a + b; b = a - b; a = a - b; printf("a = %d, b = %d\n", a, b); return 0; }

Expected output:

text

a = 7, b = 5

Notes: For large values, prefer a temporary variable or XOR swap. XOR is safe for equal values but not for aliasing.

Problem 3: Convert double to int

Goal: Truncate a floating-point number.

c

#include <stdio.h> int main(void) { double d = 3.99; int i = (int)d; /* explicit cast truncates toward zero */ printf("%d\n", i); return 0; }

Expected output:

text

3

Notes: (int) truncates; round() from <math.h> rounds to nearest.

Problem 4: Last Digit of a Number

Goal: Extract the last decimal digit.

c

#include <stdio.h> #include <stdlib.h> int main(void) { int n = 12345; int last = n % 10; /* Works for non-negative; see notes for negatives */ printf("%d\n", last); return 0; }

Expected output:

text

5

Notes: For negative n, % in C99 yields a non-positive remainder. Use abs(n) % 10 or n % 10 + 10 (mod 10) for a non-negative digit.

Problem 5: Compare Two Numbers

Goal: Print which of two numbers is greater.

c

#include <stdio.h> int main(void) { int a = 10, b = 20; if (a > b) printf("%d is greater\n", a); else if (b > a) printf("%d is greater\n", b); else printf("Both are equal\n"); return 0; }

Expected output:

text

20 is greater

Problem 6: FizzBuzz (1 to N)

Goal: Print Fizz, Buzz, or FizzBuzz per divisibility rule.

c

#include <stdio.h> void fizzbuzz(int n) { for (int i = 1; i <= n; i++) { if (i % 15 == 0) printf("FizzBuzz "); else if (i % 3 == 0) printf("Fizz "); else if (i % 5 == 0) printf("Buzz "); else printf("%d ", i); } printf("\n"); } int main(void) { fizzbuzz(15); return 0; }

Expected output:

text

1 2 Fizz 4 Buzz Fizz 7 8 Fizz Buzz 11 Fizz 13 14 FizzBuzz

Notes: Check i % 15 == 0 first, otherwise the individual cases shadow the combined one.

Problem 7: Multiplication Table

Goal: Print an n × n multiplication table.

c

#include <stdio.h> int main(void) { int n = 5; for (int i = 1; i <= n; i++) { for (int j = 1; j <= n; j++) { printf("%4d", i * j); } printf("\n"); } return 0; }

Expected output:

text

 1 2 3 4 5 2 4 6 8 10 3 6 9 12 15 4 8 12 16 20 5 10 15 20 25

Problem 8: Sum of N Numbers with Safe Input

Goal: Read n integers from stdin, sum them.

c

#include <stdio.h> #include <stdlib.h> #include <errno.h> #include <limits.h> int readInteger(const char *prompt, int *outValue) { char buffer[64]; char *endPtr; long val; printf("%s", prompt); fflush(stdout); if (fgets(buffer, sizeof(buffer), stdin) == NULL) return -1; errno = 0; val = strtol(buffer, &endPtr, 10); if (endPtr == buffer || (*endPtr != '\n' && *endPtr != '\0')) return -1; if (errno == ERANGE || val < INT_MIN || val > INT_MAX) return -1; *outValue = (int)val; return 0; } int main(void) { int n; if (readInteger("How many numbers? ", &n) != 0 || n <= 0) { fprintf(stderr, "Invalid count.\n"); return 1; } long long sum = 0; for (int i = 0; i < n; i++) { int x; char prompt[40]; snprintf(prompt, sizeof(prompt), "Number %d: ", i + 1); if (readInteger(prompt, &x) != 0) { fprintf(stderr, "Invalid number.\n"); return 1; } sum += x; } printf("Sum = %lld\n", sum); return 0; }

Sample run:

text

How many numbers? 3 Number 1: 10 Number 2: 20 Number 3: 30 Sum = 60

Notes: Uses long long for the sum to prevent overflow; strtol for robust parsing.

Bonus Problem: Medium — Count Digits of an Integer

c

#include <stdio.h> #include <stdlib.h> int countDigits(long long n) { if (n == 0) return 1; if (n < 0) n = -n; int count = 0; while (n > 0) { count++; n /= 10; } return count; } int main(void) { printf("%d\n", countDigits(0)); /* 1 */ printf("%d\n", countDigits(12345)); /* 5 */ printf("%d\n", countDigits(-9876)); /* 4 */ return 0; }

Expected output:

text

1 5 4

Bonus Problem: Medium — Leap Year

c

#include <stdio.h> int isLeapYear(int year) { /* A year is leap if divisible by 4 but not 100, unless divisible by 400 */ return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0); } int main(void) { int years[] = {1900, 2000, 2024, 2023, 2100}; for (int i = 0; i < 5; i++) { printf("%d → %s\n", years[i], isLeapYear(years[i]) ? "Leap" : "Not Leap"); } return 0; }

Expected output:

text

1900 → Not Leap 2000 → Leap 2024 → Leap 2023 → Not Leap 2100 → Not Leap

Common Pitfalls, Edge Cases & Memory Considerations

1. Integer Division Truncation

5 / 2 is 2, not 2.5. Cast one operand.

c

double r = (double)a / b;

2. Format Specifier Mismatch

printf("%d", 3.14) is undefined behavior. Match specifiers to types.

3. Negative Remainders

In C99, -7 % 3 == -1, not 2. Use (n % m + m) % m for a non-negative result.

4. Off-by-One in Loops

for (i = 0; i <= n; i++) iterates n+1 times. Use < unless you need inclusive bounds.

5. Unsafe Input

Never use gets (removed in C11). Prefer fgets with size limits.

6. Uninitialized Variables

Reading before assignment is undefined behavior.

c

int x; printf("%d\n", x); /* BUG */

7. Signed/Unsigned Confusion

Mixing signed and unsigned in comparisons yields surprising results.

c

int a = -1; unsigned int b = 1; if (a < b) { /* may be FALSE due to conversion */ }

8. Overflow

int overflows for large sums. Use long long.

c

long long sum = 0;

9. Character Classifications

Use <ctype.h> functions for portability.

c

if (isdigit((unsigned char)c)) { /* ... */ }

Cast to unsigned char before passing.

10. Memory Considerations

  • Fundamental programs rarely allocate memory.
  • When they do, prefer dynamic allocation for large inputs.
  • Free everything you malloc.
  • Never return pointers to stack variables.

Summary & Best Practices

Fundamentals are the platform on which all C skill is built. Practice them until they become reflexive.

Key Takeaways

  • Variables and types define what can be stored.
  • Format specifiers define how data is printed/read.
  • Operators and control flow define how programs compute.
  • Safe input requires fgets + strtol/strtod.
  • Every bug you encounter at scale is a fundamentals bug in disguise.

Best Practices

  1. Compile with -Wall -Wextra -pedantic -std=c11 on every exercise.
  2. Match format specifiers to types.
  3. Use long long for sums; double for decimals.
  4. Prefer fgets over scanf("%s", ...).
  5. Cast before division when you need floating-point results.
  6. Initialize all variables.
  7. Handle negative remainders explicitly.
  8. Cast char to unsigned char for <ctype.h> functions.
  9. Test boundary cases: zero, negatives, maxima, empty input.
  10. Practice daily — fundamentals are perishable without repetition.

Recommended Practice Progression

  • Easy: Print GeeksforGeeks; Global Variable; Swap Numbers; Convert double to int; Input Integer; Print with Space/Without/Separator/New Line; Be Precise; Print with Quotes; Last Digit; Compute a/b; Compare Two Numbers; Sign Check; Big Number; Sum of N; Multiplication Table.
  • Medium: AP Term; GP Term; Even Odd Game; Greatest of Three; Leap Year; FizzBuzz; Calculator; First Digit; Count Digits; Days Before n Days; Prime Factorization; Next Prime.

Work through each exercise with the templates and safety rules above. By the time you finish, fundamentals become second nature.

Learn C input/output with printf, fputs, scanf, fgets, and getchar. Safe I/O practices, format specifiers, error handling, and common pitfalls explained.

Basic Input and Output in C: Complete Tutorial

Basic Input and Output in C: A Comprehensive Tutorial

Input and output (I/O) operations are fundamental to C programming. They enable communication between a program and its environment: reading data from users, files, or devices, and displaying results. In C, I/O is handled through the standard library, primarily declared in <stdio.h>. This tutorial covers the core I/O functions, their syntax, safe usage, common pitfalls, and best practices for writing robust C code.


Conceptual Introduction & Fundamentals

What are Input and Output in C?

Input operations allow a program to receive data from the outside world (keyboard, files, network). Output operations send data from the program to the user (screen, files, printers). In C, all I/O is performed on streams—abstract representations of input sources or output destinations.

Standard Streams

The C standard library defines three standard streams automatically opened when a program starts:

StreamPurposeDefault Device
stdinStandard inputKeyboard
stdoutStandard outputScreen
stderrStandard error (unbuffered)Screen

These streams are of type FILE * and are declared in <stdio.h>. They can be redirected by the shell.

Formatted vs. Unformatted I/O

  • Formatted I/O converts data to/from human-readable text using format specifiers. Functions: printf, fprintf, sprintf, scanf, fscanf, sscanf.
  • Unformatted I/O reads or writes raw bytes without conversion. Functions: fgets, fputs, getchar, putchar, fread, fwrite.

The Standard I/O Library (<stdio.h>)

The <stdio.h> header declares functions for input/output, including:

  • printf and fprintf for formatted output.
  • scanf and fscanf for formatted input (use with caution).
  • fgets and fputs for line-oriented string I/O.
  • getchar and putchar for character I/O.
  • fopen, fclose, etc., for file I/O (covered in a separate tutorial).

All functions return a value that should be checked to detect errors (e.g., EOF or NULL).


Syntax & Core Mechanics

Output: printf()

The printf function writes formatted output to stdout.

Prototype:

c

int printf(const char *format, ...);

It returns the number of characters printed, or a negative value on error.

Format specifiers are placeholders that begin with % and specify how to format the corresponding argument. Common specifiers:

SpecifierType
%d or %iSigned integer
%uUnsigned integer
%fFloating-point (decimal notation)
%e or %EFloating-point (scientific notation)
%g or %GUses %f or %e depending on value
%cCharacter
%sString (null-terminated)
%pPointer address
%x or %XHexadecimal integer
%%Literal %

Examples:

c

#include <stdio.h> int main(void) { int age = 22; double pi = 3.14159; char grade = 'A'; char name[] = "Alice"; // Print text printf("Hello, World!\n"); // Print integer printf("Age: %d\n", age); // Print double with precision printf("Pi: %.2f\n", pi); // Print character and string printf("Grade: %c, Name: %s\n", grade, name); // Print multiple values printf("%s is %d years old and scored %c.\n", name, age, grade); return 0; }

Expected output:

text

Hello, World! Age: 22 Pi: 3.14 Grade: A, Name: Alice Alice is 22 years old and scored A.

Format flags: You can control width, precision, alignment, and more. Example: %10d prints an integer right-aligned in a field of width 10; %-10s left-aligns a string; %08.3f zero-pads to width 8 with 3 decimal places.

Output: fputs()

fputs writes a string (without formatting) to a specified stream. It does not append a newline automatically.

Prototype:

c

int fputs(const char *str, FILE *stream);

To print to the console, use stdout.

Example:

c

#include <stdio.h> int main(void) { fputs("This is a string without newline.", stdout); fputs(" This continues on the same line.\n", stdout); fputs("New line after this.\n", stdout); return 0; }

Expected output:

text

This is a string without newline. This continues on the same line. New line after this.

Input: scanf()

scanf reads formatted input from stdin. It is powerful but error-prone; the C community often recommends using fgets followed by parsing functions (like strtol, strtod) for robust input.

Prototype:

c

int scanf(const char *format, ...);

It returns the number of successfully matched input items, or EOF on input failure.

Basic usage:

c

#include <stdio.h> int main(void) { int age; printf("Enter your age: "); if (scanf("%d", &age) == 1) { printf("Age is: %d\n", age); } else { printf("Invalid input.\n"); } return 0; }

Sample run:

text

Enter your age: 25 Age is: 25

Important: scanf leaves the newline in the input buffer, causing problems when mixed with fgets. It can also overflow strings if %s is used without a width limit.

Safer Input: fgets()

fgets reads a line from a stream into a buffer, including the newline (if it fits). It is the recommended way to read strings in C.

Prototype:

c

char *fgets(char *str, int n, FILE *stream);

It reads at most n-1 characters, leaving room for the null terminator. Returns NULL on error or end-of-file.

Example:

c

#include <stdio.h> #include <string.h> int main(void) { char name[20]; printf("Enter your name: "); if (fgets(name, sizeof(name), stdin) != NULL) { // Remove trailing newline (if present) name[strcspn(name, "\n")] = '\0'; printf("Hello, %s!\n", name); } else { printf("Error reading input.\n"); } return 0; }

Sample run:

text

Enter your name: John Doe Hello, John Doe!

Character I/O: getchar() and putchar()

getchar reads a single character from stdin; putchar writes a single character to stdout.

Prototypes:

c

int getchar(void); int putchar(int c);

Example:

c

#include <stdio.h> int main(void) { int ch; printf("Enter a character: "); ch = getchar(); if (ch != EOF) { printf("You entered: "); putchar(ch); putchar('\n'); } return 0; }

Sample run:

text

Enter a character: Z You entered: Z

Full Practical Code Example with Explanation

This program demonstrates safe and robust input/output in C. It reads a user’s name, age, and height using fgets for strings and strtol/strtod for numeric parsing, then displays a formatted summary.

Program: User Information Collector

c

/** * user_info.c * Demonstrates robust input/output in C. * Uses fgets for all input and strtol/strtod for safe numeric parsing. */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include <limits.h> #define MAX_NAME_LEN 50 #define MAX_LINE_LEN 100 /* Function prototypes */ int readInteger(const char *prompt, int *outValue); int readDouble(const char *prompt, double *outValue); int main(void) { char name[MAX_NAME_LEN]; int age; double height; char buffer[MAX_LINE_LEN]; // temporary buffer for reading lines /* Read name using fgets */ printf("Enter your full name: "); fflush(stdout); if (fgets(name, sizeof(name), stdin) == NULL) { fprintf(stderr, "Error reading name.\n"); return EXIT_FAILURE; } /* Remove trailing newline */ name[strcspn(name, "\n")] = '\0'; /* Read age with validation */ if (readInteger("Enter your age: ", &age) != 0) { fprintf(stderr, "Failed to read age.\n"); return EXIT_FAILURE; } /* Read height with validation */ if (readDouble("Enter your height in meters (e.g., 1.75): ", &height) != 0) { fprintf(stderr, "Failed to read height.\n"); return EXIT_FAILURE; } /* Output summary using printf */ printf("\n--- User Summary ---\n"); printf("Name: %s\n", name); printf("Age: %d years\n", age); printf("Height: %.2f m\n", height); return EXIT_SUCCESS; } /* Reads an integer from stdin using fgets and strtol. * Returns 0 on success, -1 on failure. * On success, stores the value in outValue. */ int readInteger(const char *prompt, int *outValue) { char buffer[MAX_LINE_LEN]; char *endPtr; long val; while (1) { printf("%s", prompt); fflush(stdout); if (fgets(buffer, sizeof(buffer), stdin) == NULL) { return -1; // I/O error or EOF } /* Check if line is too long (no newline and buffer full) */ if (strchr(buffer, '\n') == NULL && !feof(stdin)) { /* Discard rest of line to avoid affecting next input */ int ch; while ((ch = getchar()) != '\n' && ch != EOF); printf("Input too long, please try again.\n"); continue; } /* Convert to long with error checking */ errno = 0; val = strtol(buffer, &endPtr, 10); /* Check for conversion errors */ if (endPtr == buffer) { printf("No digits found. Please enter a valid integer.\n"); continue; } /* Check for extra characters after number (except newline) */ if (*endPtr != '\n' && *endPtr != '\0') { printf("Invalid characters after number. Please try again.\n"); continue; } /* Check for out-of-range (errno set or val outside int range) */ if (errno == ERANGE || val < INT_MIN || val > INT_MAX) { printf("Number out of range for int. Please try again.\n"); continue; } *outValue = (int)val; return 0; } } /* Reads a double from stdin using fgets and strtod. * Returns 0 on success, -1 on failure. * On success, stores the value in outValue. */ int readDouble(const char *prompt, double *outValue) { char buffer[MAX_LINE_LEN]; char *endPtr; double val; while (1) { printf("%s", prompt); fflush(stdout); if (fgets(buffer, sizeof(buffer), stdin) == NULL) { return -1; } /* Check for overlong line */ if (strchr(buffer, '\n') == NULL && !feof(stdin)) { int ch; while ((ch = getchar()) != '\n' && ch != EOF); printf("Input too long, please try again.\n"); continue; } errno = 0; val = strtod(buffer, &endPtr); if (endPtr == buffer) { printf("No digits found. Please enter a valid number.\n"); continue; } if (*endPtr != '\n' && *endPtr != '\0') { printf("Invalid characters after number. Please try again.\n"); continue; } if (errno == ERANGE) { printf("Number out of range for double. Please try again.\n"); continue; } *outValue = val; return 0; } }

Explanation

  • fgets for all string input: Prevents buffer overflow.
  • strtol and strtod for numeric conversion: Provide full error detection (no digits, extra characters, range errors).
  • Looping on invalid input: The helper functions keep prompting until valid input is received.
  • Handling overlong lines: Detects if input line was truncated and flushes the remaining characters to avoid affecting subsequent reads.
  • Output with printf: Uses format specifiers to display the collected data.
  • Error handling: Returns from main with EXIT_FAILURE if initial reads fail.

Expected Output

Sample interaction:

text

Enter your full name: Jane Doe Enter your age: 25 Enter your height in meters (e.g., 1.75): 1.68 --- User Summary --- Name: Jane Doe Age: 25 years Height: 1.68 m

If invalid input:

text

Enter your age: abc No digits found. Please enter a valid integer. Enter your age: 25 ...

Common Pitfalls, Edge Cases & Memory Considerations

1. Buffer Overflow with scanf("%s", ...)

Using scanf with %s reads a string until whitespace, without any length limit. A user entering more characters than the buffer can hold causes stack corruption.

c

char name[10]; scanf("%s", name); // DANGEROUS

Solution: Use fgets(name, sizeof(name), stdin) or specify width in scanf: scanf("%9s", name).

2. Mixing scanf and fgets

scanf leaves the newline character in the input buffer. A subsequent fgets will read that newline and appear to skip input.

c

int age; char name[20]; scanf("%d", &age); fgets(name, sizeof(name), stdin); // reads leftover newline

Solution: Always flush the input buffer after scanf (e.g., while ((c = getchar()) != '\n' && c != EOF);) or use fgets for all input and parse with sscanf, strtol, etc.

3. Not Checking Return Values

printf, fgets, scanf, etc., can fail. Ignoring return values may lead to using uninitialized variables or infinite loops.

c

int age; scanf("%d", &age); // If input is not a number, age is uninitialized

Solution: Always check the return value. For fgets, check for NULL. For scanf, ensure the expected number of items was read.

4. Trailing Newline with fgets

fgets includes the newline character if it fits. This can cause unexpected output or string comparison failures.

Solution: Remove the newline: str[strcspn(str, "\n")] = '\0';

5. Format String Vulnerabilities

Passing user input directly as the format string to printf is a severe security risk.

c

printf(userInput); // DANGEROUS: user can include %s, %x, etc.

Solution: Always use a fixed format string: printf("%s", userInput);

6. Integer Parsing without Range Checking

atoi or sscanf with %d may not detect overflow, leading to undefined behavior.

Solution: Use strtol with errno and range checks, as shown in the practical example.

7. Unbuffered Output and Flushing

Output to stdout may be buffered, so prompts might not appear before input is requested if using scanf after printf. This can confuse users.

Solution: Call fflush(stdout) after printing prompts when using scanf or other input functions that don’t automatically flush stdout.

8. Character Input Buffering

getchar reads one character but the user must press Enter, so the newline remains. This can cause issues in loops.

Solution: Handle newline characters explicitly, or use line-based input with fgets.

9. Using gets (Removed in C11)

gets was inherently unsafe because it cannot limit input length. It was removed from the C11 standard.

Solution: Never use gets. Use fgets.

10. Memory Considerations

  • Stack buffers: Large local arrays in functions can cause stack overflow. Use dynamic allocation (malloc) for large buffers, or declare them static.
  • Input size: Assume user input may be arbitrarily long. Always specify buffer sizes to fgets and handle truncation.
  • Return values: Proper checking prevents use of uninitialized memory, which can lead to crashes or security holes.

Summary & Best Practices

Basic input and output in C is built around the <stdio.h> library. Formatted output via printf is straightforward, but input requires care to avoid buffer overflows and parsing errors. The safest approach is to use fgets for all input and strtol/strtod for numeric conversion.

Key Takeaways

  • Use printf for formatted output; know common format specifiers.
  • Use fputs for simple string output to streams.
  • Avoid scanf for strings; use fgets instead.
  • For integers and doubles, use fgets + strtol/strtod with error checking.
  • Always check return values of I/O functions.
  • Remove trailing newlines from fgets input.
  • Flush stdout before input prompts to ensure visibility.

Best Practices

  1. Prefer fgets over scanf or gets for all user input.
  2. Use strtol, strtoul, strtod for converting strings to numbers, and check for errors.
  3. Check return values of all I/O functions; handle NULL and EOF.
  4. Limit string inputs using the buffer size parameter in fgets.
  5. Remove newline from fgets result if not needed.
  6. Flush stdout after printing prompts without newline.
  7. Never use user input as a format string in printf.
  8. Use snprintf for formatted output into a buffer to avoid overflow.
  9. For large data, use dynamic memory allocation rather than huge stack arrays.
  10. Compile with warnings (-Wall -Wextra) to catch unused return values and other issues.

By following these guidelines, you’ll write C programs that handle input and output safely and robustly, even in the face of unexpected user input.