$linuxjunkies
>

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.

BeginnerUbuntuDebianFedoraArch10 min readUpdated June 7, 2026

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 c99 or c17 as 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:

HeaderProvides
<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.

tested on:Ubuntu 24.04Fedora 40Arch 2024.05.01Debian 12

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