C Program Structure Examples: 5 Complete Programs From Beginner to Intermediate
The best way to understand C program structure is to read complete, working programs and understand every decision in them. This guide presents five progressively complex examples — from a minimal 6-line program to a structured multi-function program — each fully annotated with explanations of why each element is there and best practices that apply at that level.
Example 1: The Smallest Valid C Program
The smallest C program that compiles cleanly and does something useful. Every element here is required and has a purpose.
#include <stdio.h>
int main(void) {
printf("Hello, C!\n");
return 0;
}
#include <stdio.h>Declares printf(). Required whenever you use any stdio function.
blank line
int main(void)Entry point. int = returns exit code. void = no command-line args.
printf("Hello, C!\n");Prints text + newline. Semicolon ends the statement.
return 0;Sends exit code 0 (success) to the OS.
}Closes main()'s body. Every { needs a matching }.
gcc -Wall hello.c -o hello && ./hello
Example 2: Variables, Conditionals, and Formatted Output
Adds variable declarations, an if/else decision, and printf format specifiers. Note how the variable is declared close to where it is used (C99 style), not at the top of the function.
#include <stdio.h>
int main(void) {
int score = 85; /* declare close to first use (C99+) */
printf("Score: %d\n", score);
if (score >= 90) {
printf("Grade: A\n");
} else if (score >= 70) {
printf("Grade: B\n");
} else {
printf("Grade: F\n");
}
return 0;
}
Grade: B
Example 3: for Loop, User Input, and Input Validation
Adds a for loop and scanf for user input. The argc/argv check
pattern shows how to validate input before using it.
#include <stdio.h>
int main(void) {
int n;
printf("How many numbers to sum? ");
if (scanf("%d", &n) != 1 || n <= 0) {
fprintf(stderr, "Error: enter a positive integer.\n");
return 1; /* non-zero = failure */
}
long sum = 0;
for (int i = 1; i <= n; i++) { /* loop var declared in for (C99) */
sum += i;
}
printf("Sum of 1 to %d = %ld\n", n, sum);
return 0;
}
Sum of 1 to 10 = 55
Three best practices shown here that beginners often miss:
- Check scanf's return value.
scanfreturns the number of items successfully read. If the user types "abc",%dfails andnis uninitialized. The check!= 1catches this. - Use fprintf(stderr, ...) for error messages. Errors go to stderr, not stdout. This lets scripts redirect normal output while still seeing errors.
- Return non-zero on failure. Returning
1when input is invalid lets shell scripts and CI detect that something went wrong.
Example 4: Functions, Prototypes, and Modular Structure
This example introduces helper functions with prototypes — the standard pattern for all
real C programs. main() stays short and high-level; the details go in
helper functions.
#include <stdio.h>
/* --- Function prototypes (Section 3 of program structure) --- */
double celsius_to_fahrenheit(double c);
double fahrenheit_to_celsius(double f);
void print_conversion(double value, const char *unit);
/* --- main() (Section 4) --- */
int main(void) {
print_conversion(0.0, "C"); /* freezing */
print_conversion(100.0, "C"); /* boiling */
print_conversion(98.6, "F"); /* body temp */
return 0;
}
/* --- Helper functions (Section 5) --- */
double celsius_to_fahrenheit(double c) {
return c * 9.0 / 5.0 + 32.0;
}
double fahrenheit_to_celsius(double f) {
return (f - 32.0) * 5.0 / 9.0;
}
void print_conversion(double value, const char *unit) {
if (unit[0] == 'C') {
printf("%.1f°C = %.1f°F\n", value, celsius_to_fahrenheit(value));
} else {
printf("%.1f°F = %.1f°C\n", value, fahrenheit_to_celsius(value));
}
}
100.0°C = 212.0°F
98.6°F = 37.0°C
main() is only 4 lines — it reads
like a summary of what the program does. The implementation details are in clearly-named
helper functions. Adding a new conversion type only requires adding one line to main() and
one helper function — without touching any existing code.
Example 5: Complete Intermediate Program — Student Grade Manager
A complete program using all five structural sections, a struct, a loop, functions, and proper error handling. This represents the level of structure you should aim for in any real C project.
#include <stdio.h>
#include <string.h>
/* Section 1: preprocessor — done above */
/* Section 2: global type definitions */
#define MAX_STUDENTS 5
typedef struct {
char name[32];
int score;
} Student;
/* Section 3: function prototypes */
char score_to_grade(int score);
double class_average(const Student *students, int count);
void print_report(const Student *students, int count);
/* Section 4: main() */
int main(void) {
Student students[MAX_STUDENTS] = {
{ "Alice", 92 },
{ "Bob", 75 },
{ "Carol", 88 },
{ "Dave", 61 },
{ "Eve", 95 },
};
print_report(students, MAX_STUDENTS);
return 0;
}
/* Section 5: helper functions */
char score_to_grade(int score) {
if (score >= 90) return 'A';
if (score >= 80) return 'B';
if (score >= 70) return 'C';
if (score >= 60) return 'D';
return 'F';
}
double class_average(const Student *students, int count) {
int total = 0;
for (int i = 0; i < count; i++) {
total += students[i].score;
}
return (double)total / count;
}
void print_report(const Student *students, int count) {
printf("%-10s %5s %5s\n", "Name", "Score", "Grade");
printf("%-10s %5s %5s\n", "----------", "-----", "-----");
for (int i = 0; i < count; i++) {
printf("%-10s %5d %5c\n",
students[i].name,
students[i].score,
score_to_grade(students[i].score));
}
printf("\nClass average: %.1f\n", class_average(students, count));
}
---------- ----- -----
Alice 92 A
Bob 75 C
Carol 88 B
Dave 61 D
Eve 95 A
Class average: 82.2
C Best Practices Checklist
These are the habits that separate readable, maintainable C from code that works today but breaks mysteriously tomorrow.
Use int main() and return 0
Never use void main(). The OS needs the exit code. Return 0 for success, 1 (or specific codes) for failure.
Compile with -Wall -Wextra -std=c11
These flags catch uninitialized variables, implicit declarations, unused variables, and type mismatches before they become runtime bugs.
Always use braces with control structures
Even for single-statement if/for/while bodies. Prevents the "refactoring trap" and is required by most professional C style guides.
Declare variables where they are first used
In C99+, declare variables close to their first use, not all at the top of the function. Smaller scope = fewer bugs.
Write descriptive function and variable names
class_average() is clearer than calc(). student_count is clearer than n. Code is read more than it is written.
Check return values of scanf, malloc, fopen
These functions can fail. Ignoring failure leads to crashes from uninitialized memory, null pointer dereferences, and missing files.
Free every malloc with a matching free
Every heap allocation must have a matching deallocation. Use valgrind or -fsanitize=address to check.
Keep main() short and high-level
main() should read like a summary of the program. If main() is 100+ lines, refactor logic into named helper functions.
Common Beginner Structure Mistakes
1. All variables declared at the top (C89 habit in C99+ code)
int main() {
int i, result, temp; /* all at top */
/* ... 30 lines ... */
result = 0;
for (i = 0; i < 10; i++) { }
}int main() {
int result = 0;
for (int i = 0; i < 10; i++) {
result += i;
}
}2. Ignoring scanf's return value
int n;
scanf("%d", &n); /* n may be uninit */
printf("%d\n", n * 2);int n;
if (scanf("%d", &n) != 1) {
fprintf(stderr, "bad input\n");
return 1;
}3. Putting logic in main() instead of functions
main() grows beyond
30–40 lines, it is doing too much. Extract logical units into named functions. A
main() that calls read_input(), process_data(), and
print_results() is dramatically easier to read, test, and debug than one that
does all three inline.
Frequently Asked Questions
int main() and return 0; compile with
-Wall -Wextra -std=c11; always use braces with control structures; declare
variables near their first use; write descriptive function names; check return values of
scanf, malloc, and fopen; keep main() short and delegate to helper functions.
-std=c99 or -std=c11 to compile with modern rules.
/* block comments */ (all C versions) and
// single-line comments (C99+). Best practice: comment the why,
not the what. A comment like /* multiply by 5/9 to convert scale */
is useful. A comment like /* add 1 to i */ before i++ is not —
the code is self-explanatory.
stdio.h
for printf/scanf, stdlib.h for malloc/free/exit, string.h for
strlen/strcpy, math.h for sqrt/pow (also requires -lm flag),
stdbool.h for bool/true/false in C99+. Including unnecessary headers slightly
increases compile time and can cause name conflicts.