A C Programming Tutorial for Linux
Learn C on Linux from hello-world through gcc flags, header files, multi-file projects, make, and the standard library — with real commands and examples.
Before you start
- ▸A terminal and basic comfort with the command line (cd, mkdir, cat)
- ▸A text editor installed (nano, vim, VS Code, or similar)
- ▸sudo privileges to install packages
C remains one of the most important languages on Linux — the kernel itself is written in it, and nearly every core system utility started in C. Learning C on Linux gives you a tight feedback loop: write code, compile it, run it, inspect it. This guide takes you from a blank terminal to a working multi-file program compiled with make, covering the tools you will actually use day to day.
Install the Build Tools
You need GCC (the GNU Compiler Collection), GNU Make, and the C standard library headers. On most desktops these are bundled in a meta-package.
Debian / Ubuntu
sudo apt update
sudo apt install build-essential
Fedora / RHEL / Rocky
sudo dnf groupinstall "Development Tools"
Arch Linux
sudo pacman -S base-devel
Verify the compiler is present and note the version — C standard support varies:
gcc --version
You should see something like gcc (Ubuntu 13.2.0-23ubuntu4) 13.2.0. Any GCC 10+ is fine for this guide.
Hello, World
Create a working directory and write the classic first program.
mkdir ~/c-tutorial && cd ~/c-tutorial
cat > hello.c << 'EOF'
#include
int main(void) {
printf("Hello, World!\n");
return 0;
}
EOF
#include <stdio.h> pulls in the standard I/O header so the compiler knows the signature of printf. main returns int — the exit code the shell sees. return 0 signals success.
Compile and Run
gcc hello.c -o hello
./hello
Output: Hello, World!
Understanding gcc Flags
Bare gcc file.c -o name works but hides problems. Always develop with warnings enabled:
gcc -Wall -Wextra -pedantic -std=c11 hello.c -o hello
- -Wall — enables the most useful warning classes.
- -Wextra — enables additional warnings Wall misses.
- -pedantic — rejects GNU extensions; keeps code portable.
- -std=c11 — targets the C11 standard. Use
c99orc17as needed. - -g — embeds debug symbols for use with
gdb. - -O2 — optimisation level 2 for production builds.
Header Files and Multiple Source Files
Real programs split code across files. A header (.h) declares functions; a source file (.c) defines them.
Create a Header
cat > greet.h << 'EOF'
#ifndef GREET_H
#define GREET_H
void greet(const char *name);
#endif /* GREET_H */
EOF
The #ifndef/#define/#endif pattern is an include guard. It prevents the header from being processed twice if included from multiple files — a common source of "redefinition" errors.
Implement the Function
cat > greet.c << 'EOF'
#include
#include "greet.h"
void greet(const char *name) {
printf("Hello, %s!\n", name);
}
EOF
Update main.c
cat > main.c << 'EOF'
#include "greet.h"
int main(void) {
greet("Linux Junkies");
return 0;
}
EOF
Compile Multiple Files
gcc -Wall -Wextra -std=c11 main.c greet.c -o greet_app
./greet_app
GCC compiles both .c files and links them into a single binary. For larger projects, compile to object files separately so only changed files are recompiled:
gcc -Wall -Wextra -std=c11 -c main.c # produces main.o
gcc -Wall -Wextra -std=c11 -c greet.c # produces greet.o
gcc main.o greet.o -o greet_app # link step
Automating Builds with Make
Typing long gcc commands repeatedly is error-prone. make reads a Makefile and rebuilds only what changed.
cat > Makefile << 'EOF'
CC = gcc
CFLAGS = -Wall -Wextra -pedantic -std=c11
TARGET = greet_app
OBJS = main.o greet.o
$(TARGET): $(OBJS)
$(CC) $(OBJS) -o $(TARGET)
main.o: main.c greet.h
$(CC) $(CFLAGS) -c main.c
greet.o: greet.c greet.h
$(CC) $(CFLAGS) -c greet.c
.PHONY: clean
clean:
rm -f $(OBJS) $(TARGET)
EOF
Critical: the lines under each rule must be indented with a real tab character, not spaces. Many editors default to spaces — check yours. A Makefile with space-indented rules fails with a cryptic missing separator error.
make
./greet_app
Edit greet.c, run make again — only greet.o and the final link step re-run. Run make clean to remove build artifacts.
Using the Standard Library
The C standard library is available on every Linux system. Key headers:
| Header | Provides |
|---|---|
<stdio.h> | printf, scanf, fopen, fclose, fgets |
<stdlib.h> | malloc, free, exit, atoi, rand |
<string.h> | strlen, strcpy, strncpy, strcmp, memcpy |
<math.h> | sqrt, pow, sin, cos (link with -lm) |
<errno.h> | errno, strerror — system call error codes |
<math.h> is the one case where you must explicitly link the library:
gcc -Wall -std=c11 mymath.c -o mymath -lm
Dynamic Memory Example
cat > dynmem.c << 'EOF'
#include
#include
#include
int main(void) {
size_t len = 64;
char *buf = malloc(len);
if (!buf) {
perror("malloc");
return 1;
}
strncpy(buf, "Heap-allocated string", len - 1);
buf[len - 1] = '\0';
printf("%s\n", buf);
free(buf);
return 0;
}
EOF
gcc -Wall -Wextra -std=c11 dynmem.c -o dynmem
./dynmem
Always check the return value of malloc. Always free every allocation. Tools like valgrind detect leaks: valgrind --leak-check=full ./dynmem.
Verify Your Setup End-to-End
cd ~/c-tutorial
make clean
make
./greet_app
echo "Exit code: $?"
The exit code should be 0. If the build succeeds and the program runs without errors, your environment is fully functional.
Troubleshooting
"command not found: gcc"
The build tools are not installed. Re-run the install step for your distro. On minimal server images, even build-essential is absent.
"undefined reference to sqrt" (or similar math function)
You forgot -lm at the end of the gcc command. The math library must be linked explicitly.
"Makefile:8: *** missing separator. Stop."
A rule's recipe line uses spaces instead of a tab. Open the Makefile in vim or nano and ensure the indented lines start with a literal tab (Ctrl+V Tab in nano inserts one explicitly).
Implicit function declaration warnings
You called a function without including its header. GCC warns; in C99 and later this is an error. Add the appropriate #include at the top of the file.
Segmentation fault
Compile with -g and run under gdb ./yourprogram. Type run, wait for the fault, then bt (backtrace) to see exactly which line caused it.
Frequently asked questions
- What C standard should I target as a beginner?
- Use C11 (-std=c11). It is widely supported on all Linux toolchains, adds useful features over C99 (like _Generic and improved Unicode support), and is strict enough to keep your code portable without being cutting-edge.
- Do I need an IDE, or is a text editor enough?
- A text editor (VS Code, Neovim, Geany) and the terminal are completely sufficient. IDEs like CLion or Eclipse CDT can help with larger projects, but they are not required and can hide what the toolchain is actually doing.
- Why does my Makefile say 'nothing to be done for all'?
- Make compares file timestamps. If the source files are older than the binary, it considers everything up to date. Touch a source file (touch main.c) or run make clean first to force a full rebuild.
- What is the difference between a compiler warning and an error?
- Errors stop compilation; warnings do not but usually indicate real bugs. Treat all warnings as errors during development by adding -Werror to your CFLAGS — this forces you to fix them rather than ignore them.
- How do I find which header declares a function I want to use?
- Run man 3 function_name (e.g. man 3 printf). The SYNOPSIS section at the top lists every required #include. The man-pages package must be installed; on Debian/Ubuntu: sudo apt install manpages-dev.
Related guides
Bash Arrays and Associative Arrays
Master bash indexed and associative arrays: declaration, element access, looping, mapfile, namerefs, and practical patterns for real scripting work.
Bash Functions and Variable Scoping
Master Bash function scoping with local variables, source-based libraries, correct use of return codes, and array passing techniques including namerefs.
Bash Loops: for, while and until
Learn all three Bash loop types — for, while, and until — with practical, copy-paste examples covering file iteration, counting, polling, and safe line reading.
Bash Scripting for Beginners
Learn Bash scripting from scratch: shebang lines, variables, conditionals, loops, and arguments, plus a real backup script to tie it all together.