C Basics · beginner · ~5 min
By the end of this lesson you will be able to: - Explain in plain words what C is: a small, statically typed, compiled language, and what each of those three words means. - Describe the journey source code takes to become a running program (preprocess, compile, assemble, link). - Identify where C is used in the real world and why it is still chosen in 2026. - Recognize the basic shape of a C program: `#include`, `main`, statements, and `return 0;`. - State the trade-off at the heart of C: total control over the machine in exchange for total responsibility for correctness and safety.
C is a small, statically typed, compiled programming language created in the early 1970s at Bell Labs (the language is usually dated to 1972). It is one of the most influential pieces of software ever written, because almost everything else is built on top of it.
Let us unpack the three words in that first sentence, since they shape everything you will do in this course.
int x;, you are promising the compiler that x will only ever hold an integer. The compiler checks these promises and refuses to silently mix incompatible types.C sits one thin layer above assembly language. That closeness to the hardware lets you manipulate memory directly, talk to the operating system through well-defined entry points called system calls (or syscalls), and reason about roughly what each line costs the CPU.
This lesson is the foundation of the whole C Basics track and has no prerequisites. The very next lesson, Compiling with gcc, shows you how to actually run the translation pipeline introduced here. For now the goal is a clear mental model: what C is, why it exists, and what you are signing up for when you learn it. Learning C teaches you what a computer is actually doing when it runs any program.
C matters because it is the foundation almost the entire software world stands on. When you understand C, you stop seeing computers as magic boxes and start seeing how they actually work.
Almost every higher-level language is built on C or a C derivative.
Knowing C gives you concrete, practical powers:
It also matters for security. A large fraction of the serious security vulnerabilities (CVEs) in widely used software trace back to memory mistakes that C makes possible. Understanding C is the first step to understanding why those bugs happen and how to prevent them. This course teaches the language and the safety habits together, not as an afterthought.
We will look at four ideas that together explain what C is. Take them one at a time.
Definition. Compilation is the process of turning your human-readable .c source file into a native executable the CPU can run directly.
How it works. It is not one step but a chain of small programs, each taking the previous one's output as input:
hello.c
| (1) preprocessor: handles #include, #define; pure text editing
v
expanded source
| (2) compiler: translates C into assembly for your CPU
v
hello.s (assembly)
| (3) assembler: turns assembly into machine code
v
hello.o (object file, machine code but not yet runnable)
| (4) linker: joins your object files + libraries into one program
v
hello (executable you can run)
Why it is a chain. Each stage does one job well. The preprocessor only shuffles text. The compiler only understands C. The linker only stitches pieces together. You will normally run all four with a single command (gcc hello.c), but knowing the stages helps you read error messages: a linker error ("undefined reference to ...") is a very different problem from a compiler error ("expected ';'").
When NOT to think about it. For tiny programs you can ignore the internals and just compile. The pipeline matters when projects grow and you start splitting code across files.
Common pitfall. Beginners often confuse a compile error (the code is not valid C) with a runtime crash (the code compiled fine but did something illegal while running). They are caught at completely different times.
Knowledge check. Which stage is responsible for replacing
#include <stdio.h>with the actual contents of that header file?
Definition. Every variable has a single, fixed type, decided when you write the code and checked by the compiler before the program ever runs.
Plain-language explanation. A type tells the compiler two things: how much memory a value needs, and what operations are legal on it. int x; reserves a fixed-size slot for a whole number. double y; reserves a (usually larger) slot for a number with a decimal point.
How it works internally. Because the type is known in advance, the compiler can generate exact machine instructions and pick the right amount of memory. There is no runtime guessing.
When to use vs. avoid. In C you do not get to opt out: every variable is typed. The skill is choosing the right type (we cover this in later lessons on integer and floating types).
Common pitfall. Assuming the compiler will catch every type mistake. It catches many, but C also allows risky conversions (for example squeezing a large value into a small type) that silently lose data.
Knowledge check. In
double pi = 3;the literal3is an integer. Explain in your own words what the compiler must do so thatpiends up holding a floating-point value.
Definition. In C, you decide when memory is reserved and when it is released. There is no garbage collector running behind your back to clean up after you.
How it works. Most simple data lives on the stack, which is reserved and freed automatically as functions start and finish. For data whose size or lifetime you control yourself, you request memory from the heap with malloc and return it with free.
Memory of a running C program (simplified)
high addresses
+---------------------+
| stack | <- local variables; grows DOWN as calls nest
| | |
| v |
| |
| ^ |
| | |
| heap | <- malloc/free live here; grows UP
+---------------------+
| globals / static |
+---------------------+
| program code |
low addresses
When to use the heap. When you need data that outlives the function that created it, or whose size you only learn at runtime. Otherwise prefer the stack: it is simpler and frees itself.
Common pitfall. Forgetting to free heap memory (a memory leak), or using memory after freeing it. These are the classic C bugs and we will return to them often.
Knowledge check. A program calls
malloc1,000 times in a loop but never callsfree. What kind of problem is this, and where in the diagram above is the memory accumulating?
Definition. A pointer is a variable whose value is a memory address: it points at where some other data lives.
Why it is powerful. With pointers you can read and write specific locations in memory. Combined with operating-system facilities, this is exactly how device drivers, kernels, and high-performance libraries do their work.
Why it is dangerous. Nothing stops you from pointing at the wrong place. Following a bad pointer is one of the most common ways a C program crashes or becomes insecure. Pointers get their own dedicated lessons later; for now just know they exist and are the source of much of C's power and its risk.
Common pitfall. Treating an uninitialized pointer as if it already points somewhere valid. It points at garbage until you make it point at something real.
A C program has a small, predictable shape. Here are the ground rules, with a minimal annotated example.
#include <stdio.h> // bring in declarations for printf and friends
int main(void) { // execution starts here; 'void' = takes no arguments
printf("Hi\n"); // a statement; \n is a newline; line ends with ;
return 0; // 0 tells the operating system "success"
} // block closes with a matching brace
Key rules:
.c; header files end with .h.main function. When main returns 0, it signals success to the operating system; a non-zero value signals an error.;.{ }.// for a single line or /* ... */ for a block.Main, MAIN, and main are three different names, and only main is the entry point.#include lines start with # and have no semicolon; they are instructions to the preprocessor, not normal statements.C is a small, compiled, statically typed programming language designed in the early 1970s at Bell Labs.
It has shaped almost every layer of modern computing:
Compared to higher-level languages, C gives you direct access to memory and only a thin abstraction over the CPU.
That power comes with responsibility. Most security vulnerabilities (CVEs) in low-level software trace back to mistakes that C makes possible. This course teaches both the language and how to use it safely.
#include <stdio.h>
int main(void) {
/* The classic first program: print a greeting and exit cleanly. */
printf("Hello, World!\n");
return 0; // 0 = success status to the operating system
}
What it does. This program prints the text Hello, World! followed by a newline, then exits and reports success to the operating system.
Expected output:
Hello, World!
Why each piece is there. #include <stdio.h> makes the printf function available. int main(void) is the entry point every C program needs. printf writes text to standard output, and the \n is an escape sequence that produces a newline so the cursor moves to the next line. return 0; ends the program and tells the operating system everything went fine.
Edge cases to notice. If you forget the \n, the text still prints but without a trailing newline, so your shell prompt may appear glued to the output. If you write print instead of printf, or forget the #include, the program will not compile, because the name printf would be unknown.
Here is what happens, in order, when you build and run the program above.
| Step | What happens |
|---|---|
| 1 | The preprocessor sees #include <stdio.h> and pastes in the declarations for printf so the compiler knows it exists. |
| 2 | The compiler reads int main(void) { ... }, checks the types and syntax, and translates it to assembly. |
| 3 | The assembler and linker turn that into a runnable executable (joining in the standard library code for printf). |
| 4 | You run the program. The operating system loads it and calls main. |
| 5 | printf("Hello, World!\n"); runs: the characters are sent to standard output and \n moves the cursor to a new line. |
| 6 | return 0; runs: main finishes and hands the value 0 back to the operating system as the exit status. |
| 7 | The program ends. In a shell you can confirm the status with echo $?, which prints 0. |
The most important idea: nothing in main runs until the operating system calls main, and main runs top to bottom. Statements execute in the order they are written. There is no hidden setup beyond loading the program and jumping to main.
Realistic beginner mistakes, each with the wrong version, why it fails, and the fix.
1. Using print instead of printf.
print("Hello\n"); // WRONG: there is no function called print in C
Why it is wrong: C's output function is printf (the f is for "formatted"), declared in <stdio.h>. Fix:
printf("Hello\n"); // correct
How to recognize it: the compiler complains about an undeclared or implicitly declared function named print.
2. Forgetting the #include.
int main(void) {
printf("Hi\n"); // WRONG without the include above
return 0;
}
Why it is wrong: without #include <stdio.h>, the compiler has never heard of printf. Fix: add #include <stdio.h> as the first line. Recognize it by warnings/errors mentioning an implicit declaration of printf.
3. Missing the semicolon.
printf("Hi\n") // WRONG: no semicolon
return 0;
Why it is wrong: every statement must end with ;. The compiler reads the next line as part of the unfinished statement and reports a confusing error, often pointing at the next line. Fix: end the printf line with ;.
4. Capitalizing Main.
int Main(void) { return 0; } // WRONG: this is not the entry point
Why it is wrong: C is case-sensitive, and the required entry point is the lowercase main. The linker will report that it cannot find main. Fix: use main.
5. Putting a semicolon after #include.
#include <stdio.h>; // WRONG: preprocessor lines take no semicolon
Why it is wrong: #include is a preprocessor directive, not a statement. The stray ; becomes part of the line and can cause errors. Fix: remove the semicolon.
Match your approach to the kind of problem you are having.
It does not compile (compiler errors).
expected ';' (missing semicolon, often on the line above where it points), implicit declaration of function 'printf' (missing #include <stdio.h>), undefined reference to 'main' (this is a linker error: your entry point is misspelled or missing).gcc -Wall -Wextra hello.c. Warnings often reveal the real bug before it bites.It compiles but crashes (runtime errors).
gdb ./a.out, then type run. When it crashes, type bt (backtrace) to see the chain of function calls that led to the crash.It runs but gives the wrong answer (logic errors).
printf("debug: x=%d\n", x); to watch how values change.Questions to ask when stuck: Did I save the file before compiling? Am I running the binary I just built? Did I read the first error message? Did I include the header for every function I call?
C trusts you completely, which means it will happily let you do dangerous things without warning. The key risks, even in simple programs, are:
These are rarely caught at compile time. They are silent corruption that may not crash immediately; the program can keep running and fail mysteriously much later, sometimes in a completely unrelated part of the code. This is exactly what makes C bugs hard to diagnose, and it is the root of many real-world security vulnerabilities.
The defensive mindset to start building now:
malloc, every array index, and every pointer use as a small contract you must personally uphold.-Wall -Wextra and, later, run with sanitizers (for example gcc -fsanitize=address) to catch memory errors at runtime.You are not expected to master memory safety yet. The point of this first lesson is to understand why it matters so much in C specifically: the language gives you the power, and never the seatbelt.
C is chosen wherever performance, small size, or unusual hardware matters.
A useful rule of thumb: if something has to be fast, small, or run on weird hardware, it is probably C.
Professional best-practice habits to start forming now.
Beginner rules:
main with an explicit return 0;.-Wall -Wextra and fix the warnings.More advanced habits (for later in the course):
malloc, file opens, and so on).Work through these in order; each builds on the last. Do not look for full solutions: aim to figure them out from the lesson.
Beginner 1 — Run your first program.
Hello, World! example unchanged.hello.c, compile it, and run the result.Hello, World! on its own line.main, printf.Beginner 2 — Personalize the greeting.
Hello, Sam!printf. Concepts: string literals, \n.Intermediate 1 — Three lines of numbers.
printf and the %d format specifier to print 1, 2, and 3.1
2
3
printf("%d\n", 1); prints a single integer and a newline. Concepts: printf formatting, statements run top to bottom.Intermediate 2 — Read the compiler.
Challenge — Confirm the exit status.
echo $? in your shell; it should print 0. Then change return 0; to return 3;, rebuild, run, and confirm echo $? now prints 3.main returns becomes the program's exit status. Concepts: return from main, exit status, the role of the operating system.#include, int main(void), statements ending in ;, and return 0; for success.print for printf, a forgotten #include, and a missing semicolon. Read the first compiler error.