A low-level, manually-memory-managed language that underlies most operating systems and runtimes, minimal abstraction between your code and the machine.
Setup
gcc --version # or clang --version
Compiling
gcc main.c -o main -Wall -Wextra
./main
-Wall -Wextra enables warnings that catch a large share of real bugs (uninitialized variables, signed/unsigned mismatches), always compile with them on.
Core syntax
#include <stdio.h>
int add(int a, int b) {
return a + b;
}
int main(void) {
int count = 0;
char name[] = "Ada";
printf("Hello, %s! Count: %d\n", name, count);
int nums[3] = {1, 2, 3};
for (int i = 0; i < 3; i++) {
printf("%d\n", nums[i] * 2);
}
return 0;
}
Pointers and manual memory management
#include <stdlib.h>
int *make_array(int size) {
int *arr = malloc(size * sizeof(int));
if (arr == NULL) return NULL; // always check malloc's return
for (int i = 0; i < size; i++) arr[i] = i * i;
return arr;
}
int main(void) {
int *arr = make_array(5);
if (arr) {
printf("%d\n", arr[2]);
free(arr); // you own it, you free it, exactly once
}
return 0;
}
There's no garbage collector, every malloc needs exactly one matching free. Freeing twice, forgetting to free, or using memory after freeing it are the classic C bug categories; tools like Valgrind or -fsanitize=address catch these during development.
Structs
typedef struct {
char name[32];
int age;
} Person;
Person p = { "Ada", 30 };
printf("%s is %d\n", p.name, p.age);
Header files
// math_utils.h
#ifndef MATH_UTILS_H
#define MATH_UTILS_H
int add(int a, int b);
#endif
The include guard (#ifndef/#define/#endif) prevents a header being processed twice if it's included from multiple files, which would otherwise cause "redefinition" errors.
Common gotchas
- Array bounds are not checked, reading/writing past an array's end is undefined behavior, not a caught exception, and can corrupt unrelated memory silently.
- Strings are just
chararrays terminated by a\0byte, forgetting to size a buffer for that extra byte is a classic off-by-one bug. - Comparing floats with
==is unreliable due to representation error, compare against a small epsilon instead (fabs(a - b) < 1e-9).
See also: C++, which extends C with classes, templates, and RAII-based memory management.