What C is
C is a small compiled language from the early 1970s that sits very close to the machine: you decide how memory is laid out, when it is allocated and when it is given back. Writing it feels precise and a little unforgiving — there is no garbage collector, no built-in string type and very few safety nets — but a C program is just a set of functions calling each other, and the whole language fits in a short book. Operating system kernels, compilers and the reference interpreters of languages such as Python and Lua are written in it, which is why its ideas turn up everywhere.
Where C is used
- Operating systems and kernels
- The Linux kernel and the BSD kernels are written in C, and the device drivers that talk directly to hardware are usually C too.
- Embedded systems and firmware
- The code inside washing machines, car controllers, medical devices and hobby microcontroller boards runs on chips with very little memory, where C's tiny runtime and predictable output are essential.
- Language runtimes and interpreters
- CPython (the standard Python interpreter), Lua and the C standard library that most other languages call into are C code, so a C programmer can read the layer underneath the language they use every day.
- Databases and system tools
- SQLite, PostgreSQL and Redis are written in C, as are the classic Unix command-line utilities.
- Performance-critical libraries
- When a Python or JavaScript library needs to be fast — compression, image decoding, cryptography — the hot part is often a C library with a thin wrapper around it.
Your first C program
Saved as hello.c. You can paste it straight into the playground to see it run.
#include <stdio.h>
int main(void) {
int apples = 3;
double price = 0.5;
printf("Hello from C.\n");
printf("%d apples cost %.2f.\n", apples, apples * price);
return 0;
}What it prints
Hello from C.
3 apples cost 1.50.- Line 1 pulls in the standard input/output header, which is where printf is declared. Without it the compiler does not know what printf is.
- Line 3 declares main, the function every C program starts in.
voidin the brackets says it takes no arguments, and theintbefore it says it hands a whole number back to the operating system when it finishes. - Lines 4 and 5 declare two variables. In C every variable has a fixed type written before its name:
intholds whole numbers anddoubleholds decimals. Each statement ends with a semicolon. - Line 7 prints a line of text. The
\nat the end is the newline character; C does not add one for you, so without it the next print would continue on the same line. - Line 8 shows how C formats values.
%dis a placeholder for an int and%.2ffor a double shown to two decimal places; the values after the format string fill the placeholders in order.apples * pricemixes an int and a double, so C widens the int and the result is the double 1.5, printed as 1.50. - Line 9 returns 0, the conventional way to say the program finished without error, and line 10 closes main.
Run C on your own computer
C needs a compiler on your machine; there is no interpreter to type lines into. Every major platform has a free, well-supported one, and the setup is a single install followed by a two-step compile-then-run habit.
Install a compiler
On Debian or Ubuntu Linux, install the build-essential package, which brings in GCC; other distributions have an equivalent package. On macOS, run xcode-select --install to get Apple's command-line tools, which include Clang and a gcc command that forwards to it. On Windows, the usual routes are a GCC build installed through MSYS2, the C compiler in Microsoft's Visual Studio Build Tools, or a Linux environment under WSL. The commands below assume GCC; Clang accepts the same ones.
Shellsudo apt install build-essentialCheck it works
Open a terminal and ask the compiler for its version. If you installed Clang instead, clang --version does the same job.
Shellgcc --versionSave the file and compile it
Save the program as hello.c, then compile it from the folder it is in. The compiler writes an executable called hello (hello.exe on Windows). The -Wall and -Wextra flags switch on the warnings that catch most beginner mistakes; use them from day one and treat every warning as something to fix.
Shellgcc -Wall -Wextra hello.c -o helloRun the executable
Run the file you just built. In PowerShell or Command Prompt on Windows the command is .\hello instead. Notice the two separate steps: if you change the source, you must compile again before the change shows up.
Shell./helloPick an editor that shows warnings
C files are plain text, so any editor works. One that runs the compiler and underlines its warnings as you type — VS Code with a C extension, or Visual Studio on Windows — makes the compile-fix-compile loop much faster.
A learning order for C
Stages, not a timetable. Each one exists because the next would not make sense without it, and how long each takes depends on how much you write.
Stage 1. Compiling, printing and variables
- the compile-then-run cycle
- printf and format specifiers
- int, double and char
- arithmetic and integer division
- reading compiler errors and warnings
In C the compiler is your first teacher, so learning to read its messages comes before anything else. Integer division and format specifiers are where the first surprises live.
Stage 2. Control flow and functions
- if / else and comparisons
- while and for loops
- switch
- writing your own functions
- function prototypes and header files
C programs are nothing but functions. The compiler has to see a function's signature before the function is called, which is why prototypes and header files appear so early.
Stage 3. Arrays, strings and pointers
- arrays and indexing
- strings as char arrays ending in '\0'
- pointers, & and *
- pointer arithmetic
- passing arrays to functions
This is the stage that defines C. A pointer is just an address, but almost every serious C bug is a pointer or array mistake, so this deserves more time than any other stage.
Stage 4. Memory and structs
- the stack versus the heap
- malloc, calloc and free
- struct and typedef
- building a linked list
- AddressSanitizer (-fsanitize=address)
Dynamic memory lets a program handle data whose size is not known in advance, and freeing it exactly once is the discipline C demands in return. A sanitizer makes that discipline visible while you learn it.
Stage 5. Files, the preprocessor and multi-file programs
- fopen, fgets, fprintf and fclose
- #define and #include
- splitting a program across .c and .h files
- a first Makefile
- the standard library headers worth knowing
Once a program outgrows one file you need to understand how the pieces are compiled separately and linked together. The preprocessor is a text-substitution step that runs before the compiler, and knowing that explains most of its oddities.
Stage 6. Undefined behaviour and real code
- what undefined behaviour means
- integer overflow and signedness
- buffer overflows and bounds checking
- compiler flags: -O2, -g and -fsanitize
- reading someone else's C codebase
C trusts you completely, which means the compiler may do anything at all when you break its rules. Knowing which rules matter is what turns someone who can write C into someone who can be trusted with it.
Mistakes beginners make in C
- Reading a variable before giving it a value
- int total; followed by total = total + 5 reads whatever bytes happened to be in that memory. C does not zero local variables, and reading one is undefined behaviour: the program may print rubbish, print 0 today and 4198 tomorrow, or be optimised into something you did not write. Compiling with -Wall makes GCC warn that the variable 'may be used uninitialized'; always initialise on the line you declare.
- Walking off the end of an array
- int scores[5] has valid positions 0 to 4, so scores[5] is past the end. C performs no bounds check, so you silently read or overwrite neighbouring memory, and the crash, if there is one, happens somewhere else entirely. On Linux and macOS, compiling with -fsanitize=address turns this into an immediate, precise report ('stack-buffer-overflow' or 'heap-buffer-overflow') with the line number.
- Forgetting that a string needs its terminator
- A C string is a char array that ends with a zero byte, '\0'. char word[5] = "hello" leaves no room for it, so printf and strlen keep reading past the end until they happen to find a zero. Size the array one larger than the text, or let the compiler count for you with char word[] = "hello".
- Integer division throwing away the fraction
- 7 / 2 is 3 in C, not 3.5, because both operands are ints and so the result is an int. Make one side a double — 7.0 / 2, or (double) a / b — when you want the decimal part. There is no warning for this; the compiler considers it exactly what you asked for.
- Freeing memory twice, or using it after free
- Calling free on the same pointer twice, or reading through a pointer after it has been freed, is undefined behaviour and a long-standing source of security holes. The GNU C library often catches the first case and aborts with 'free(): double free detected in tcache 2'; the second case usually produces no message at all. Set a pointer to NULL straight after freeing it, and let AddressSanitizer check your programs while you learn.
Strengths and trade-offs
Where it is strong
- Direct control over memory and layout, which is exactly what operating systems, drivers and firmware need.
- A small language: the core syntax fits in a short reference, and it is the ancestor of the syntax used by C++, Java, C#, JavaScript and Go, so those languages look familiar afterwards.
- Compilers exist for an enormous range of processors, from desktop chips to tiny microcontrollers, and a C library can be called from nearly every other language.
- Compiled programs start instantly, use little memory and need no runtime to be installed alongside them.
Where it is not
- No memory safety: an off-by-one, a dangling pointer or an uninitialised variable is undefined behaviour, and the compiler is allowed to do anything in response. These mistakes are a long-standing source of crashes and security vulnerabilities.
- No built-in string type, growable list, dictionary or error handling. You write or find a library for each, and you check return values by hand.
- Slow to write compared with Python, Go or C#: a task that takes one line in those languages often takes a function in C.
- Dependencies, packaging and cross-platform builds have no single standard tool; each project chooses its own build system, and beginners often spend their first hours on setup rather than code.
Who C is for
C is a good first language for people who want to understand what a computer is actually doing — students in systems or embedded courses, anyone curious about operating systems, and hobbyists working with microcontrollers. It is also a strong second language for programmers who already know something higher level and keep meeting the word 'memory' without knowing what it means. It is a poor choice if your goal is a website, a mobile app, a data analysis or simply getting something working quickly; Python, JavaScript or C# will get you there sooner, and C's lessons will still be waiting when you want them.
Questions about learning C
- Should I learn C before C++?
- Not necessarily. C++ began as an extension of C and still accepts most C code, so the basic syntax overlaps, but modern C++ is written very differently, with classes, templates and a standard library that does the memory management C makes you do by hand. Learn C if you want the memory-level understanding on its own terms, or if you want to work on kernels, embedded systems or other places where C is the language in use. If your real target is C++, you can start there and pick up the C subset along the way.
- Is C still worth learning?
- Yes, for specific reasons rather than general ones. The Linux and BSD kernels, most embedded firmware and the interpreters underneath languages such as Python are written in C, and that code is not going away. Even if you never write C for a living, understanding pointers, the stack and the heap makes you better at every other language, because it shows you what they are hiding from you.
- Why does my program print rubbish or crash at random?
- Almost always because it has undefined behaviour: an uninitialised variable, an array index past the end, a missing string terminator or a pointer to memory that has already been freed. C does not check for any of these, so the symptom shows up far from the cause and can change from one run to the next. Compile with -Wall -Wextra -fsanitize=address,undefined and run again; the sanitizers report the exact line the first time a rule is broken.
- What is the difference between gcc and clang?
- They are two separate compilers that both implement the C standard. GCC is the GNU project's compiler and the usual default on Linux; Clang is the LLVM project's compiler and is what Apple ships on macOS. For learning they are interchangeable: the same source compiles with both, the flags on this page work on both, and their error messages differ only in wording.
The primary source
When this guide and the official documentation disagree, the documentation is right and we would like to hear about it.