Beginners Guide: Overview, Features, and Learning Path
C's syntax, features, and recommended learning path — all organized in one place.
The history, lineage, and design philosophy of C are covered in detail in the IT History Dictionary: "C — The Language Born to Write UNIX". This page focuses on practical information for writing C code.
What Is C? (In a Nutshell)
C is a programming language developed in 1972 by Dennis Ritchie at AT&T Bell Labs. It was built to write the UNIX operating system, and as of 2026 it remains the primary choice for OS kernels, embedded systems, device drivers, and high-performance libraries.
| Standard | Year | Major additions |
|---|---|---|
| C89 / C90 | 1989 / 1990 | First ANSI standard. Function prototypes, void type, enumerations |
| C99 | 1999 | // comments, long long, variable-length arrays, stdint.h |
| C11 | 2011 | Multi-threading, _Generic, anonymous structs |
| C17 | 2018 | Mainly bug fixes for C11; virtually no new features |
| C23 | 2024 | nullptr, attributes, typeof, constexpr |
File Extensions
C source code is saved with the following file extensions.
.c— C source file.h— Header file (function declarations, macros, type definitions, etc.)
Six Points to Know Before You Start
Before reading and writing C, here are six points where C differs from other languages and tends to trip people up. The later sections go deeper on each.
- No string type. Strings are represented as
chararrays, terminated by a null character'\0'that signals the end. - Direct pointer manipulation. Pointer types are declared with
*and addresses are obtained with&. This is one of C's defining features. - Manual memory management. Memory allocated with
malloc()must always be released withfree(). - No array bounds checking. Accessing out-of-bounds indices does not produce a compile error — it causes undefined behavior at runtime.
- Compilation is required. Source files (
.c) must be compiled withgccto produce an executable before running. - Header files and implementation files are separate. Function declarations go in
.hfiles; definitions go in.cfiles.
The design philosophy behind all of these — the UNIX philosophy, the "trust the programmer" principle, the BCPL → B → C lineage — is covered in the IT History Dictionary / C.
Features of C
C has several distinctive characteristics. These are both strengths and points that require care.
Low-level Memory Access
C lets you directly manipulate memory addresses using a feature called pointers. You can specify exact locations in memory and write code that is very close to the hardware. This is essential in device drivers and embedded systems. On the other hand, invalid memory access can lead to crashes or security vulnerabilities.
"Direct memory address access" is a privilege that almost no high-level language gives you. In Java or Python, you only think of variable x as "something with a value." In C, you can also see that x sits at memory address 0x7ffd1234. C lets you reach into the inside of the computer, and that is a big part of its appeal.
Manual Memory Management
C has no garbage collector. The programmer is responsible for allocating memory with malloc() (memory allocation) and releasing it with free() when done. Unlike Java or Python, there is no automatic mechanism to reclaim unused memory. Forgetting to release memory causes memory leaks, and accessing memory after it has been freed is another common source of bugs.
This cuts both ways: more responsibility, more freedom. Garbage-collected languages are convenient, but a GC pause that fires at an unpredictable moment can ruin real-time work. C has no GC, so the programmer is fully in charge. In embedded systems and game engines, where every millisecond matters, that level of control is decisive.
Fast Execution Speed
C code is compiled directly into machine code. Because the CPU (Central Processing Unit — the processor that executes computations) executes it without going through an interpreter, the processing speed is extremely fast. This is one of the main reasons C is chosen in performance-critical applications.
"Fast" means different things, but compared with Python, C can be tens to hundreds of times faster for the same task. Python is a language tuned for readability; C is tuned for squeezing performance out of hardware. Different tools for different jobs — neither is "better" than the other.
High Portability
C source code can run on almost any platform as long as a compiler is available. The same source code can often be made to work on Windows, Linux, or an embedded microcontroller with only minor changes, making C known as a highly portable language.
For example, if you wrote a tiny C utility on a Mac, you could ship the source to a Linux server, run gcc there, and it would mostly just work. "If you have the source, it runs anywhere" — that idea is the very root of UNIX/C culture.
Strengths and Weaknesses
The following table summarizes the trade-offs. Neither column is "good" or "bad" — it comes down to how well the language fits the job.
| Aspect | Strength | Weakness |
|---|---|---|
| Speed | Very fast — compiled to machine code | Compilation step required; no immediate feedback |
| Memory | Fine-grained control | Manual management; easy to introduce bugs |
| Hardware control | Device- and OS-level operations possible | More code than higher-level languages |
| Portability | Runs on most platforms when following the standard | Platform-dependent behavior (e.g., integer sizes) |
| Type safety | — | Implicit conversions; type errors easy to miss |
| Standard library | Core library is well-rounded | Not as rich as Java or Python's standard libraries |
| Learning | What you learn stays valid for decades | Concepts like pointers take time to absorb |
For low-level domains like operating systems and embedded development, C is still one of the top choices. For web applications or scripting tasks where development speed matters more than raw performance, other languages are often a better fit.
How C Code Runs
A C program goes through the following sequence: write source code → compile → run. This is different from Python (python3 hello.py) or Node.js (node hello.js), where you pass the code directly to an interpreter.
Compiling means transforming the source code a human wrote into machine code (a binary) that the CPU can execute directly. Because the binary is already translated into instructions the CPU understands, no interpretation step is needed at runtime.
The Four Stages of Compilation
In practice, a single gcc command runs all four stages. You usually do not need to think about them — but when something goes wrong, knowing which stage failed tells you a lot about what to look for.
| Stage | What happens | If you see an error here |
|---|---|---|
| Preprocessing | Expands #include and #define directives. | Header file not found, or a malformed macro. |
| Compilation | Translates C source into assembly language (a human-readable representation close to machine code). | Syntax errors, type mismatches, undeclared variables. |
| Assembly | Converts assembly into object files (.o). | You almost never see errors here directly — gcc handles it. |
| Linking | Combines object files and libraries into a single executable. | undefined reference = a function has no definition, or a library was not specified. |
An error like "undefined reference to printf" while you are using printf means you are failing at the linking stage. #include <stdio.h> brings in declarations, but the actual compiled bodies of those functions are pulled in during linking. Most of the standard library is linked automatically, but the math library needs an explicit gcc -lm.
Compiling and Running Your First Program
hello.c
#include <stdio.h>
int main(void) {
printf("Hello, World!\n");
return 0;
}
Compile it.
gcc hello.c -o hello
Run it (macOS / Linux).
./hello Hello, World!
The -o hello flag specifies the output file name. Without it, the output is named a.out (or a.exe on Windows).
On Windows, run it as hello.exe instead of ./hello.
./ means "from the current directory" — an old UNIX convention, still going strong after thirty years.
Variables and Types
C is a statically typed language. Before using a variable, you must declare what kind of data it will hold. Writing int x; locks the variable to integers; double y; locks it to floating-point numbers.
Basic Types
A sample using the main types.
#include <stdio.h>
int main(void) {
int score = 1500; /* integer */
double temperature = 36.5; /* double-precision floating point */
char grade = 'A'; /* single character */
printf("score: %d\n", score);
printf("temperature: %.1f\n", temperature);
printf("grade: %c\n", grade);
return 0;
}
score: 1500 temperature: 36.5 grade: A
| Type | Purpose | Typical size | Example |
|---|---|---|---|
int | Integer | 4 bytes | 42, -10 |
char | Character / small integer | 1 byte | 'A', 65 |
double | Double-precision float | 8 bytes | 3.14 |
float | Single-precision float | 4 bytes | 3.14f |
long | Large integer | 8 bytes (64-bit) | 1000000L |
Type sizes can vary by platform. The same int can be 2 bytes on a 16-bit system, 4 bytes on a 32- or 64-bit system. When you need exact sizes, use <stdint.h> types such as int32_t and uint64_t.
Use the sizeof operator to check the exact size.
The sizeof Operator
Use sizeof to check the size of a type.
#include <stdio.h>
int main(void) {
printf("int: %zu bytes\n", sizeof(int));
printf("char: %zu bytes\n", sizeof(char));
printf("double: %zu bytes\n", sizeof(double));
return 0;
}
int: 4 bytes char: 1 bytes double: 8 bytes
sizeof looks like a runtime call, but its value is actually computed at compile time. The compiler figures out how many bytes the variable or type takes and inlines that constant. There is zero runtime overhead.
For more on sizeof, see sizeof.
Functions
In C, you use functions to bundle related code together. A function is defined with the form: return_type function_name(parameter_type parameter_name).
#include <stdio.h>
/* A function that adds two integers and returns the result */
int add(int a, int b) {
return a + b;
}
int main(void) {
int result = add(1048, 576);
printf("result: %d\n", result);
return 0;
}
result: 1624
A function that returns nothing uses void as the return type.
#include <stdio.h>
void greet(char *name) {
printf("Hello, %s!\n", name);
}
int main(void) {
greet("user1");
greet("user2");
return 0;
}
Hello, user1! Hello, user2!
Functions are how you give a name to a piece of behavior. Instead of writing the same code twice, you write a function once and call it twice — and when you fix a bug, you fix it in one place. It is an absolute fundamental of programming, and how skillfully you use functions decides how maintainable your code becomes.
For function definitions and prototype declarations, see Function Definition.
Control Flow
if / else if / else
The basic conditional branching pattern.
#include <stdio.h>
int main(void) {
int level = 120;
if (level < 80) {
printf("level_a\n");
} else if (level < 100) {
printf("level_b\n");
} else {
printf("level_c\n");
}
return 0;
}
level_c
For if details, see if / else / else if.
for loop
A loop that repeats a set number of times.
#include <stdio.h>
int main(void) {
int i;
for (i = 1; i <= 3; i++) {
printf("Loop iteration %d\n", i);
}
return 0;
}
Loop iteration 1 Loop iteration 2 Loop iteration 3
while loop
A loop that repeats while a condition is true.
#include <stdio.h>
int main(void) {
int count = 0;
while (count < 3) {
printf("count: %d\n", count);
count++;
}
return 0;
}
count: 0 count: 1 count: 2
switch / case
Branches execution based on a value.
#include <stdio.h>
int main(void) {
int day = 3;
switch (day) {
case 1:
printf("Monday\n");
break;
case 2:
printf("Tuesday\n");
break;
case 3:
printf("Wednesday\n");
break;
default:
printf("Other\n");
break;
}
return 0;
}
Wednesday
Forgetting break causes execution to fall through into the next case. This is occasionally useful by design, but it is also a common source of bugs when written by accident.
For for / while details, see for / while / do-while. For switch, see switch / case.
Arrays
An array is a contiguous sequence of elements of the same type. Indexes start at 0.
#include <stdio.h>
int main(void) {
int scores[3] = {1500, 1200, 1800};
int i;
for (i = 0; i < 3; i++) {
printf("scores[%d] = %d\n", i, scores[i]);
}
return 0;
}
scores[0] = 1500 scores[1] = 1200 scores[2] = 1800
C arrays have no built-in bounds checking. Accessing an index outside the array bounds does not produce a compile error; it causes undefined behavior at runtime. Even something like scores[100] compiles cleanly — the compiler will just say "okay, here is the address you asked for." What lives at that address is anyone's guess. Managing indexes correctly is the programmer's responsibility.
This is an expression of C's "trust the programmer" design principle, formally part of the ANSI C / ISO C philosophy. The language imposes minimal restrictions, and responsibility for safety — bounds checking, type checking, and so on — lies with the programmer.
Out-of-bounds access is classified as Undefined Behavior (UB) in the C standard. UB means "anything can happen and the standard says nothing about the result." The program may appear to work correctly, crash, or corrupt unrelated variables. Compilers are allowed to optimize under the assumption that UB never occurs, so code containing UB may behave differently under different optimization levels. Avoiding UB is one of the most important principles when writing C.
For array details, see Arrays.
Pointers — The Heart of C
What Is a Pointer?
Pointers are one of the most distinctive features of C. A pointer is a variable that holds the memory address of another variable. Use * to declare a pointer type, and & (the address-of operator) to get the address of a variable.
#include <stdio.h>
int main(void) {
int x = 42;
int *p = &x; /* p holds the address of x */
printf("value of x: %d\n", x);
printf("address of x: %p\n", (void *)&x);
printf("value at *p: %d\n", *p);
*p = 100; /* modify x through the pointer */
printf("x after modification: %d\n", x);
return 0;
}
value of x: 42 address of x: 0x7ffd5e8a3b2c (varies by environment) value at *p: 42 x after modification: 100
How Pointers Work — Memory Address and Value
A pointer's behavior is structurally similar to an array and its index. Just as a[i] returns the value at the position an index points to, *p returns the value at the memory address a pointer holds. In fact, the C specification treats *p and p[0] as equivalent.
| Code | What happens |
|---|---|
int x = 42; | The integer 42 is stored at a location in memory. |
int *p = &x; | The memory address of that location is assigned to the pointer p. |
*p | Retrieves the value (42) at the address held by p. |
*p = 100; | Writes the value 100 to the address held by p. |
A pointer is an integer holding an address value — 8 bytes on a 64-bit system. Even for 100 MB of data, passing just the pointer (8 bytes) to a function provides access without copying the data itself. That is why pointers are efficient.
Pass-by-Reference via Pointers
Pointers allow you to pass a variable by reference to a function. Because C passes arguments by value, there is no other way to modify a caller's variable from inside a function.
#include <stdio.h>
/* Receives a pointer and doubles the value it points to */
void doubleValue(int *n) {
*n = *n * 2;
}
int main(void) {
int hp = 100;
doubleValue(&hp);
printf("hp: %d\n", hp);
return 0;
}
hp: 200
We hand over the address of hp with &hp, and inside the function we use *n to reach back through that address and rewrite the value at the destination. The address slip is what travels — and via that slip, the function can poke at the original variable.
What You See Once Pointers Click
Once pointers make sense, a lot of behavior in other languages that used to be invisible becomes visible:
- "Passing an array to a JavaScript function and finding it modified" — the array is actually a reference (a pointer-like thing).
- "Passing a Python
listand seeing it modified" — also a reference. - "Java throws because some object was null" — a null-pointer dereference.
- "Go's
*and&confuse me" — they are inherited almost as-is from C.
The moment you see pointers as "not a nuisance, but an honest expression of what the computer actually does", C suddenly becomes interesting.
For pointer basics, see Pointer Basics. For the relationship between pointers and arrays, see Pointers and Arrays.
Strings (char Arrays)
C has no built-in string type. Strings are represented as arrays of char. A null character '\0' at the end marks where the string terminates.
#include <stdio.h>
#include <string.h>
int main(void) {
char name[] = "sample_text";
printf("name: %s\n", name);
printf("length: %zu\n", strlen(name));
return 0;
}
name: sample_text length: 11
"sample_text" is 11 characters, but the array actually takes up 12 bytes in memory. The 12th byte holds '\0', the null terminator that marks "this is where the string ends." strlen() walks from the beginning until it hits '\0' and returns the count without including that terminator — hence 11.
No String class, just a plain char array. C can be remarkably blunt about its data structures.
strlen() is a standard library function that returns the length of a string. It requires #include <string.h>.
For string functions (strlen, strcpy, strncpy), see strlen / strcpy / strncpy.
Structs
A struct bundles multiple variables together into a single data type. Related data can then be treated as one unit. Without classes, structs are C's primary way to group data.
#include <stdio.h>
struct Member {
char name[32];
int age;
char dept[64];
};
int main(void) {
struct Member m1;
snprintf(m1.name, sizeof(m1.name), "user1");
m1.age = 25;
snprintf(m1.dept, sizeof(m1.dept), "engineering");
printf("name: %s\n", m1.name);
printf("age: %d\n", m1.age);
printf("dept: %s\n", m1.dept);
return 0;
}
name: user1 age: 25 dept: engineering
Using typedef lets you omit the struct keyword when declaring a variable.
typedef struct {
char name[32];
int age;
} Member;
Member m2; /* no need to write 'struct' */
"It must be inconvenient without classes" — but actually, structs plus functions cover most of what classes do. The Linux kernel and the internals of the major web browsers are built almost entirely from structs and functions. You really can build enormous systems without classes — and that is reassuring to know.
File I/O (Input/Output)
To read and write files in C, use a FILE pointer together with functions from <stdio.h>.
#include <stdio.h>
int main(void) {
FILE *fp;
char buf[128];
/* Open for writing */
fp = fopen("message.txt", "w");
if (fp == NULL) {
printf("Could not open file\n");
return 1;
}
fprintf(fp, "sample message\n");
fclose(fp);
/* Open for reading */
fp = fopen("message.txt", "r");
if (fp == NULL) {
printf("Could not open file\n");
return 1;
}
fgets(buf, sizeof(buf), fp);
fclose(fp);
printf("Read: %s", buf);
return 0;
}
Read: sample message
Every file you open with fopen() must eventually be closed with fclose(). If you forget, you can run out of file descriptors (the OS-level handles to open files) or lose data that was still buffered. "If you opened it, close it" is one of C's iron rules.
For fopen() details, see fopen / fclose.
Memory Layout — Stack and Heap
Spend any time with C and you will run into the words "stack" and "heap". They describe how memory is used while a program runs, and you cannot use pointers and malloc safely without at least a working mental model of them.
The Memory Regions a Program Uses
When a program starts, the OS lays out its memory in several regions. The two that matter most for C beginners:
- Stack: where function-local variables live. A new region is allocated each time a function is called and torn down automatically when the function returns. Fast, size-limited, fully automatic.
- Heap: memory you allocate with
malloc(). It lives until you callfree()on it. More flexible in size, but you are in charge of cleaning up.
Stack Example
Local variables inside a function are allocated on the stack.
#include <stdio.h>
void greet(void) {
int local_score = 1500; /* allocated on the stack */
printf("%d\n", local_score);
} /* gone when the function returns */
int main(void) {
greet();
return 0;
}
1500
local_score sits on the stack and disappears the moment greet returns. No free() needed. That is the upside of the stack.
Heap Example
Memory allocated with malloc() lives on the heap and survives after the function returns.
#include <stdio.h>
#include <stdlib.h>
int *create_score(void) {
int *p = (int *)malloc(sizeof(int)); /* allocate on the heap */
*p = 1500;
return p; /* survives even after the function returns */
}
int main(void) {
int *score = create_score();
printf("%d\n", *score);
free(score); /* you free it yourself when done */
return 0;
}
1500
Heap memory survives across function calls. That is what makes it the right tool when you want to "hand data back to the caller" or "decide the size at runtime." The downside is that if you forget free(), the memory sticks around until the program exits — that is a memory leak.
Stack is automatic, heap is manual. Convenience versus responsibility — the C bargain in one sentence.
Memory Management (malloc / free)
C allows you to allocate memory dynamically — that is, to decide at runtime how much memory you need. Use malloc() (memory allocation) to allocate and free() to release it when done.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void) {
int n = 3;
int *scores;
int i;
/* Allocate memory for n integers */
scores = (int *)malloc(sizeof(int) * n);
if (scores == NULL) {
printf("Memory allocation failed\n");
return 1;
}
scores[0] = 1500;
scores[1] = 1200;
scores[2] = 1800;
for (i = 0; i < n; i++) {
printf("scores[%d] = %d\n", i, scores[i]);
}
/* Always free when done */
free(scores);
return 0;
}
scores[0] = 1500 scores[1] = 1200 scores[2] = 1800
Memory allocated with malloc() is not released until free() is called (or the program exits). Forgetting to call free() causes a memory leak. Accessing memory after freeing it is also undefined behavior.
It can feel like extra work, but it also means the programmer has total control over memory. No garbage collector decides to fire and stall your program; you decide when to allocate, when to free, and how the memory is laid out. For real-time work, that level of control is decisive.
For malloc / free details, see malloc / free / calloc / realloc.
The Preprocessor
The preprocessor transforms source code before compilation. Lines starting with # are preprocessor directives.
#include — Including Files
#include inserts the contents of the specified file at that point. Standard library headers use <>; your own files use "".
#include <stdio.h> /* standard library (printf, etc.) */ #include <stdlib.h> /* malloc / free, etc. */ #include <string.h> /* strlen / strcpy, etc. */ #include "myheader.h" /* your own header file */
Writing #include <stdio.h> tells the compiler to literally paste in the contents of that header (function declarations and so on). Because the paste is real, longer headers really do mean longer compile times.
#define — Macro Definition
#define performs text substitution before compilation. It is often used to define constants or short macros.
#include <stdio.h>
#define MAX_HP 9999
#define SQUARE(x) ((x) * (x))
int main(void) {
printf("MAX_HP: %d\n", MAX_HP);
printf("SQUARE(5): %d\n", SQUARE(5));
return 0;
}
MAX_HP: 9999 SQUARE(5): 25
#define is just text substitution, so the compiler does not check types. It is convenient, but it is also a classic source of subtle bugs. Modern code often uses const and inline functions instead.
For #include / #define details, see #include / #define.
Common Errors and Solutions
Compile Error: implicit declaration of function
This occurs when a function is called before it has been declared. Check whether you have included the necessary header file.
/* NG: missing #include for stdio.h */
int main(void) {
printf("Hello\n"); /* warning: implicit declaration of function 'printf' */
return 0;
}
The corrected version is:
/* OK: include stdio.h at the top */
#include <stdio.h>
int main(void) {
printf("Hello\n");
return 0;
}
Hello
Segmentation Fault
A segmentation fault occurs when the program attempts to access a memory region it is not allowed to touch. Common causes are null pointer dereferencing, out-of-bounds array access, and accessing memory after it has been freed.
int *p = NULL; *p = 42; /* NG: writing through a NULL pointer -> Segmentation fault */
This is one of the most common fatal errors in C programs. The cause is almost always something pointer-related — a NULL pointer dereference, an out-of-bounds array access, or use of a pointer after free().
Infinite Loop
An infinite loop happens when the loop variable is never updated or the condition is always true.
int i = 0;
while (i < 5) {
printf("%d\n", i);
/* NG: missing i++ causes an infinite loop */
}
Using an Uninitialized Variable
Local variables in C are not initialized automatically. They hold whatever happens to be in that memory location — a "garbage value" — until you assign a value.
int x;
printf("%d\n", x); /* NG: uninitialized — result is unpredictable */
The same logic can also be written as:
int x = 0;
printf("%d\n", x); /* OK: always assign an initial value */
Memory Leak
Memory allocated with malloc() and never free()'d stays unreleased until the program exits. This is hard to notice in short-lived programs but can be fatal in long-running servers and daemons.
int *p = (int *)malloc(sizeof(int) * 100); /* ... do something with p ... */ return 0; /* NG: forgot to free(p) */
The same logic can also be written as:
int *p = (int *)malloc(sizeof(int) * 100); /* ... do something with p ... */ free(p); /* OK: always free when done */ return 0;
How to Write Comments
Comment syntax in C. Comments have no effect on program execution — they are used to document intent or temporarily disable code (commenting out).
Types of comments
| Type | Syntax | Description |
|---|---|---|
| Block comment | /* text */ | Everything between /* and */ is a comment. Used for multi-line notes. The traditional comment form in C. |
| Line comment | // text | Everything from // to the end of the line is a comment. Introduced in C99, borrowed from C++. |
Sample
A sample that uses both single-line and block comments.
#include <stdio.h>
/* calculate_area: returns the area from width and height */
int calculate_area(int width, int height) {
return width * height;
}
int main(void) {
int w = 10;
int h = 5;
int area = calculate_area(w, h); // compute area
/*
* Print the result with printf.
* %d is the placeholder for an int value.
*/
printf("area = %d\n", area);
return 0;
}
area = 50
Comments can hold whatever you find useful. Common uses include why the code is written a certain way, what the code does, TODOs, and notes — but you are free to write whatever fits the situation. For full details on comment syntax and common patterns, see the comment reference page.
Recommended Learning Order
C covers a lot of ground. The following order is a practical way to build understanding incrementally.
| Step | What You Learn | Pages |
|---|---|---|
| 1 | Environment setup and the entry point | Environment Setup↓The main Function |
| 2 | Variables and basic types | int / char / double / float |
| 3 | Control flow | if / else / else if↓for / while / do-while |
| 4 | Functions and arrays | Function Definition↓Arrays |
| 5 | Pointers — the heart of C | Pointer Basics↓Pointers and Arrays |
| 6 | String operations | strlen / strcpy / strncpy |
| 7 | Structs and dynamic memory | struct↓malloc / free |
| 8 | Preprocessor and file I/O | #include / #define↓fopen / fclose |
Pointers (step 5) are the part of C that takes the most time to understand. Getting a firm grasp of what an address is and what indirection means before moving on is important.
Summary
C is a low-level programming language developed by Dennis Ritchie in 1972 for implementing UNIX. It is still used today across a wide range of applications — from operating systems and embedded systems to device drivers and performance-critical software.
Because code is compiled directly into machine code, execution is extremely fast. At the same time, C has many mechanisms that can become sources of bugs if not handled carefully: manual memory management through pointers, implicit type conversions, and more.
Working with C means directly handling how a computer operates at the level of memory, addresses, stacks, and heaps. Beyond that, the syntax of nearly every major language today (C++, Java, C#, JavaScript, PHP, Python) traces back to C, so learning C is, in effect, learning the substrate of almost every modern language.
The standard has evolved from C89 through C99, C11, C17, and C23, with C23 (ratified in 2023) being the current version. The slow pace of revisions means that knowledge from years ago still works today — and that is part of C's quiet charm.
Each feature has its own dictionary page on this site, with examples.
If you find any errors or copyright issues, please contact us.