Your First C Program
Introduction
This chapter moves from “what C is” to a working edit–compile–run loop. You will write complete programs, read compiler diagnostics, split a tiny program across multiple files, and finish a short lab (personal info printer + unit converter).
Default compile line for every example:
gcc -std=c17 -Wall -Wextra -Wpedantic -o prog prog.cThe classic “Hello, World!”
#include <stdio.h>
int main(void) {
printf("Hello, World!\n");
return 0;
}Line-by-line
| Piece | Meaning |
|---|---|
#include <stdio.h> |
Bring in declarations for standard I/O (printf, scanf, …). |
int main(void) |
Entry point; returns an int status; no parameters. |
printf(...) |
Library call writing to standard output. |
\n |
Newline character in the string. |
return 0; |
Report success to the operating system / shell. |
Create, compile, run
# create hello.c with your editor, then:
gcc -std=c17 -Wall -Wextra -Wpedantic -o hello hello.c
./helloOn Windows (MinGW / similar), the executable may be hello.exe; run hello or .\hello.exe depending on your shell.
What the compiler does (short version)
hello.c → preprocess → compile → assemble → link → hello
| Stage | Flag to stop here | Output |
|---|---|---|
| Preprocess | -E |
Preprocessed source (.i) |
| Compile to assembly | -S |
Assembly (.s) |
| Assemble only | -c |
Object file (.o) |
| Full link | (default) | Executable |
gcc -std=c17 -E hello.c -o hello.i
gcc -std=c17 -S hello.c -o hello.s
gcc -std=c17 -c hello.c -o hello.o
gcc -o hello hello.oUseful flags you will reuse:
gcc -std=c17 -Wall -Wextra -Wpedantic -g -O0 -o hello hello.c # debug-friendly
gcc -std=c17 -Wall -Wextra -Wpedantic -O2 -o hello hello.c # optimized release-ish| Flag | Role |
|---|---|
-std=c17 |
Language dialect |
-Wall -Wextra -Wpedantic |
Strong warnings |
-g |
Debug symbols (GDB/LLDB) |
-O0 / -O2 |
Optimization level |
-o name |
Output file name |
Structure of a C program
/* 1. Preprocessor directives */
#include <stdio.h>
#define APP_NAME "demo"
/* 2. Optional global declarations / prototypes */
void greet(const char *who);
/* 3. main — required for hosted programs */
int main(void) {
greet("World");
return 0;
}
/* 4. Other function definitions */
void greet(const char *who) {
printf("Hello, %s!\n", who);
}Order rules of thumb:
- Includes and macros at the top.
- Prototypes before first use (or define functions before
main). - One clear
main. - Keep globals rare; prefer locals and parameters.
More complete single-file examples
Greeting with command-line arguments
#include <stdio.h>
int main(int argc, char *argv[]) {
if (argc < 2) {
printf("Usage: %s <name>\n", argv[0]);
return 1;
}
printf("Hello, %s!\n", argv[1]);
printf("Argument count: %d\n", argc);
return 0;
}gcc -std=c17 -Wall -Wextra -Wpedantic -o greeter greeter.c
./greeter Ada
./greeter # should print usage and exit non-zeroargv[0] is usually the program name as invoked. argc is the count of elements in argv.
Sum of two integers (with validation)
#include <stdio.h>
int main(void) {
int a, b;
printf("Enter two integers: ");
if (scanf("%d %d", &a, &b) != 2) {
fprintf(stderr, "error: expected two integers\n");
return 1;
}
printf("%d + %d = %d\n", a, b, a + b);
return 0;
}Multiple helpers in one file
#include <stdio.h>
static int square(int x) {
return x * x;
}
static int max2(int a, int b) {
return (a > b) ? a : b;
}
int main(void) {
int x = 5;
int y = 12;
printf("square(%d) = %d\n", x, square(x));
printf("max(%d, %d) = %d\n", x, y, max2(x, y));
return 0;
}static on a function at file scope means “internal linkage”: the name is not exported to the linker for other .c files. That keeps helpers private to the translation unit.
Multi-file tiny program
Real projects split declarations (headers) from definitions (source files). Minimal pattern:
util.h
#ifndef UTIL_H
#define UTIL_H
int add(int a, int b);
void print_sum(int a, int b);
#endif /* UTIL_H */util.c
#include <stdio.h>
#include "util.h"
int add(int a, int b) {
return a + b;
}
void print_sum(int a, int b) {
printf("%d + %d = %d\n", a, b, add(a, b));
}main.c
#include "util.h"
int main(void) {
print_sum(3, 4);
return 0;
}Build it
# Separate compile + link
gcc -std=c17 -Wall -Wextra -Wpedantic -c util.c -o util.o
gcc -std=c17 -Wall -Wextra -Wpedantic -c main.c -o main.o
gcc -o multifile main.o util.o
# Or one shot
gcc -std=c17 -Wall -Wextra -Wpedantic -o multifile main.c util.c
./multifileWhy the include guard?
#ifndef UTIL_H
#define UTIL_H
/* declarations */
#endifIf two files include util.h and a third includes both, guards prevent duplicate declarations in one translation unit.
Header vs source rules
Put in .h |
Put in .c |
|---|---|
| Function prototypes | Function bodies |
Shared struct types (later) |
static helpers |
extern data declarations (rare) |
Definitions of globals |
Include your own headers with quotes: #include "util.h". System headers use angle brackets: #include <stdio.h>.
Common compile errors (with explanations)
Missing semicolon
printf("hi\n") /* error: expected ';' */Fix: statements end with ;. The diagnostic often points at the next line—check the previous statement.
Missing #include <stdio.h>
int main(void) {
printf("hi\n");
return 0;
}Symptom: implicit declaration / unknown identifier printf.
Fix: include the proper header.
Wrong main spelling or signature
int mian(void) { return 0; }Symptom: linker error undefined reference to main.
Fix: the entry point must be named main.
Case sensitivity
Printf("hi\n"); /* wrong */Fix: printf, not Printf.
Undeclared function (multi-file)
main.c calls add but never includes util.h and has no prototype:
Symptom: implicit declaration warning/error, sometimes wrong assumptions about types.
Fix: #include "util.h" or declare int add(int, int); before use.
Multiple definition of a function
Defining int add(int a, int b) { ... } in a header that two .c files include.
Symptom: linker multiple definition of add.
Fix: declare in the header; define in exactly one .c file.
Forgetting to link a file
gcc -std=c17 -Wall -Wextra -Wpedantic -o multifile main.c
# undefined reference to print_sumFix: include every translation unit: main.c util.c.
Format / type mismatch (warning)
int x = 3;
printf("%f\n", x); /* wrong specifier for int */Fix: use %d for int. Enable -Wall; modern GCC/Clang catch many of these.
Using = instead of == in a condition (logic bug; may still compile)
int x = 0;
if (x = 1) { /* assignment; always true here */
printf("oops\n");
}Mitigation: -Wall often warns about suspicious assignments in conditions. Prefer clear comparisons.
Unterminated string
printf("hello\n);Symptom: missing terminating " / stray errors cascading.
Fix: close the string literal; recompile—cascading errors often shrink after the first fix.
Tips for reading diagnostics
- Start with the first error, not the last.
- Fix includes and syntax before chasing linker issues.
- Recompile after each fix; noise often disappears.
- If the message is opaque, reduce to a 10-line file that still fails.
Lab A — Personal info printer
Goal: print a short personal profile using variables and formatted output.
#include <stdio.h>
int main(void) {
char name[] = "Ada Lovelace";
int age = 36;
char city[] = "London";
char language[] = "C";
double years_experience = 2.5;
printf("==============================\n");
printf(" PERSONAL PROFILE\n");
printf("==============================\n");
printf("Name: %s\n", name);
printf("Age: %d\n", age);
printf("City: %s\n", city);
printf("Language: %s\n", language);
printf("Experience: %.1f years\n", years_experience);
printf("==============================\n");
return 0;
}gcc -std=c17 -Wall -Wextra -Wpedantic -o profile profile.c
./profileLab stretch:
- Change the data to your own details.
- Add a second
printfblock that re-prints the same data as a single comma-separated line. - Replace hard-coded values with
scanf/fgetsinput (after the next chapter if needed).
Lab B — Unit converter
Goal: convert between a few everyday units with a tiny menu.
#include <stdio.h>
int main(void) {
int choice;
double input;
double output;
printf("Unit converter\n");
printf(" 1) kilometers to miles\n");
printf(" 2) miles to kilometers\n");
printf(" 3) kilograms to pounds\n");
printf(" 4) pounds to kilograms\n");
printf("Select: ");
if (scanf("%d", &choice) != 1) {
fprintf(stderr, "Invalid menu selection\n");
return 1;
}
printf("Value: ");
if (scanf("%lf", &input) != 1) {
fprintf(stderr, "Invalid numeric value\n");
return 1;
}
switch (choice) {
case 1:
output = input * 0.621371;
printf("%.4f km = %.4f mi\n", input, output);
break;
case 2:
output = input * 1.60934;
printf("%.4f mi = %.4f km\n", input, output);
break;
case 3:
output = input * 2.20462;
printf("%.4f kg = %.4f lb\n", input, output);
break;
case 4:
output = input * 0.453592;
printf("%.4f lb = %.4f kg\n", input, output);
break;
default:
fprintf(stderr, "Unknown option %d\n", choice);
return 1;
}
return 0;
}gcc -std=c17 -Wall -Wextra -Wpedantic -o units units.c
./unitsLab stretch:
- Add Celsius ↔︎ Fahrenheit (reuse logic from the intro chapter).
- Split conversion formulas into functions in a second file (
convert.c/convert.h) and link them. - Loop the menu until the user chooses
0to quit (preview of control flow).
Variations worth knowing
Implicit return 0 in main (C99+)
#include <stdio.h>
int main(void) {
printf("Hello, World!\n");
}Legal in C99 and later for main only. Prefer an explicit return while learning.
Prototype before main
#include <stdio.h>
void print_greeting(void);
int main(void) {
print_greeting();
return 0;
}
void print_greeting(void) {
printf("Hello, World!\n");
}Using stderr for errors
#include <stdio.h>
int main(void) {
fprintf(stderr, "This goes to standard error\n");
printf("This goes to standard output\n");
return 1;
}./prog >out.txt 2>err.txtBeginner mistakes checklist
| Mistake | Fix |
|---|---|
Missing ; |
Terminate statements |
| Missing header | #include <stdio.h> etc. |
void main() |
Use int main(void) |
| Calling without prototype | Declare or include header |
| Ignoring warnings | Compile with -Wall -Wextra -Wpedantic |
| Wrong file extension / dialect | Save as .c, pass -std=c17 |
| Editing the wrong directory | Confirm path before compile |
| Running old binary | Rebuild after every edit |
Summary
- Every hosted C program needs a
mainand usually standard library headers for I/O. - Compilation is a pipeline; object files link into executables.
- Multi-file programs separate declarations (headers) from definitions (sources).
- Learn to read the first compiler/linker diagnostic.
- Labs: personal profile printer and unit converter reinforce the full cycle.
Next: Basic Input and Output — format strings, scanf pitfalls, and safer line input with fgets.
Exercises
Compile each with:
gcc -std=c17 -Wall -Wextra -Wpedantic -o exN exN.c- Banner — Print a boxed title using only
printforputs(no user input). - Three-file arithmetic — Create
mathx.h,mathx.c(mul,div_safe), andmain.c.div_safeshould return0and print an error tostderrwhen dividing by zero (or use anintstatus code—your design, document it). - Argc printer — Print all command-line arguments, one per line, including
argv[0]. - Warning hunt — Write a file that triggers at least two different warnings under
-Wall -Wextra, then fix them. Save both versions in comments at the bottom of the file. - Profile lab upgrade — Personal info printer that reads age with
scanfand name with a fixed string orscanf("%49s", ...). - Converter lab upgrade — Add an option
0to exit; use a loop if you already knowwhile/for, otherwise document that the stretch is deferred. - Explain a log — Deliberately omit
util.ofrom a multi-file link command and paste the linker error into a comment; explain it in one sentence. - Style pass — Reformat one lab with consistent indentation (4 spaces), braces on the same style throughout, and a one-line file header comment with your name and purpose.
Comments
Prefer comments that explain why or non-obvious constraints. Restating
i++as “increment i” adds noise.