Every C program starts in main. #include <stdio.h> pulls in a header 头文件 so you can use printf to print. A function 函数 is a named block you can call; \n starts a new line. main returns 0 to mean "success".
中文
每个 C 程序都从 main 开始。#include <stdio.h> 引入一个头文件(header),这样你就能用 printf 来打印。函数(function)是一个有名字、可以调用的代码块;\n 表示换行。main 返回 0 表示"成功"。
#include <stdio.h>
void greet(void) {
printf("Hello, world!\n");
}
int main(void) {
greet();
printf("I am learning C.\n");
return 0;
}
Every C program runs from main after including headers
1.2
变量、类型与算术
English
C needs a type for every variable: int (integer 整数), double (floating-point 浮点数), char (one character). printf uses a format specifier 格式说明符 — %d, %f, %c — for each value. / between two ints is integer division; % is the remainder 余数.
The format string drives both printing and reading:
Specifier
Type
Example output
%d
int
17
%f
double (%.2f = 2 decimal places)
3.14
%c
char
A
%s
a string
Mei
%zu
a size (from sizeof / strlen)
3
%x
int, printed in base 16
ff
scanf("%d", &n) reads keyboard input with the same specifiers (note the &). Its sibling sscanf parses values out of a string, so it runs anywhere:
中文
C 要求每个变量都有类型:int(整数,integer)、double(浮点数,floating-point)、char(单个字符)。printf 为每个值使用一个格式说明符(format specifier)——%d、%f、%c。两个整数之间的 / 是整数除法;% 是余数(remainder)。
#include <stdio.h>
int main(void) {
int n = 17;
double pi = 3.14;
char grade = 'A';
printf("%d %.2f %c\n", n, pi, grade); // 17 3.14 A
printf("%d %d\n", 7 / 2, 7 % 2); // 3 1
return 0;
}
A comment 注释 is a note for humans; the compiler ignores it. Use // for one line and /* ... */ for a block. Good indentation 缩进 and clear names make code easy to read.
Common mistakes
Every statement ends with ;, and main should return int (return 0;).
printf needs a format string: %d for int, %f for double, and \n for a new line.
A wrong format like %d for a double prints garbage — match the type.
An if runs a block when a condition 条件 is true. C has no real boolean 布尔值 type by default: 0 is false and any non-zero value is true. Chain choices with else if and else. Compare with ==, !=, <, >, <=, >=.
中文
if 在条件(condition)为真时运行一个代码块。C 默认没有真正的布尔值(boolean)类型:0 为假,任何非零值为真。用 else if 和 else 串联多个选择。用 ==、!=、<、>、<=、>= 比较。
#include <stdio.h>
int main(void) {
int score = 72;
if (score >= 90) {
printf("A\n");
} else if (score >= 60) {
printf("Pass\n");
} else {
printf("Fail\n");
}
return 0;
}
if 选择真分支;else 选择假分支
2.2
switch
English
A switch picks one case by an integer value. Each case needs a break to jump out 跳出 — without it, C "falls through" into the next case. default runs when nothing matches.
Common mistakes
if (x = 5) assigns and is always true; use == to compare.
Each switch case needs a break;, or it falls through to the next.
0 is false and any non-zero value is true in a condition.
中文
switch 根据一个整数值选择一个 case。每个 case 都需要一个 break 来跳出(jump out)——否则 C 会"贯穿"到下一个 case。当没有匹配时运行 default。
#include <stdio.h>
int main(void) {
int day = 3;
switch (day) {
case 1: printf("Mon\n"); break;
case 2: printf("Tue\n"); break;
case 3: printf("Wed\n"); break;
default: printf("Other\n");
}
return 0;
}
A loop 循环 repeats a block. A while loop runs as long as a condition is true. A for loop packs the start, the test, and the increment 自增 (i++) into one line — best when you know how many times to repeat.
A function takes parameters 参数 (inputs) and gives back a return value 返回值. The return type comes first (int, double, …); void means it returns nothing.
#include <stdio.h>
int square(int x) { // x 是一个参数
return x * x; // 交回一个值
}
int main(void) {
int r = square(5);
printf("%d\n", r); // 25
return 0;
}
Parameters in; return value out
4.2
函数原型与作用域
English
C reads top to bottom, so a function must be known before it is called. A prototype 函数原型 — the header line plus ; — declares it early so you can keep main first. A variable's scope 作用域 is the block it lives in: it is local 局部 and disappears when the block ends.
Common mistakes
A function used before it is defined needs a prototype above main.
Arguments are passed BY VALUE — changes inside a function do not affect the caller unless you pass a pointer.
A variable declared inside a function is local to it.
中文
C 从上往下读,所以函数在被调用之前必须先被认识。函数原型(prototype)——就是头部那一行加上 ;——提前声明它,这样你就能把 main 放在最前面。变量的作用域(scope)是它所在的代码块:它是局部(local)的,代码块结束时就消失。
#include <stdio.h>
int add(int a, int b); // 函数原型:在使用前声明
int main(void) {
printf("%d\n", add(3, 4)); // 7
return 0;
}
int add(int a, int b) { // 定义放在后面
int sum = a + b; // sum 是 add 的局部变量
return sum;
}
An array 数组 holds several values of one type. Index 索引 from 0. C does not store an array's length, so a common trick computes the element 元素 count: sizeof(a) / sizeof(a[0]).
#include <stdio.h>
int main(void) {
int scores[3] = {88, 71, 95};
printf("%d\n", scores[0]); // 88
scores[1] = 100;
printf("%d\n", scores[1]); // 100
int n = sizeof(scores) / sizeof(scores[0]);
printf("%d\n", n); // 3
return 0;
}
Array slots are indexed from 0
5.2
数组算法
English
Traverse 遍历 an array with a for loop to find a max, a total, or a count. Always loop from 0 up to n - 1.
中文
用 for 循环遍历(traverse)数组,求最大值、总和或计数。始终从 0 循环到 n - 1。
#include <stdio.h>
int main(void) {
int a[] = {3, 9, 2, 7};
int n = sizeof(a) / sizeof(a[0]);
int max = a[0], total = 0;
for (int i = 0; i < n; i++) {
if (a[i] > max) max = a[i];
total += a[i];
}
printf("%d %d\n", max, total); // 9 21
return 0;
}
5.3
二维数组
English
A 2-D array is a grid of rows 行 and columns 列: grid[row][col]. Use two nested loops to visit every cell.
Common mistakes
C does NOT check array bounds: writing a[n] in a size-n array is undefined behaviour.
Valid indexes run from 0 to n - 1.
The array name is a pointer to its first element; sizeof only gives the size in the array's own scope.
A pointer 指针 stores the address 地址 of a variable. &x gives the address of x; *p dereferences 解引用 the pointer — it reads or writes the value stored there. Passing a pointer lets a function change the caller's variable (pass-by-pointer).
The classic use is a swap function. Passing values only copies them — the swap is lost. Passing pointers lets the function reach the caller's variables:
An array name acts as a pointer to its first element, and pointer arithmetic 指针运算 steps by whole elements:
NULL is the pointer that points at nothing — check for it before you dereference.
Common mistakes
&x is the address of x; *p is the value stored at p.
Never use *p on an uninitialised or NULL pointer — it crashes or corrupts memory.
To change a caller's variable, pass its address and write through the pointer.
中文
指针(pointer)存储一个变量的地址(address)。&x 给出 x 的地址;*p 对指针解引用(dereference)——读取或写入存放在那里的值。传递指针能让函数修改调用者的变量(按指针传递,pass-by-pointer)。
指针存储一个地址;解引用就是沿着箭头找到那个值
#include <stdio.h>
void addOne(int *p) { // p 保存一个地址
*p = *p + 1; // 修改那个地址处的值
}
int main(void) {
int x = 10;
int *ptr = &x; // & 取得 x 的地址
printf("%d\n", *ptr); // * 读取值:10
addOne(&x);
printf("%d\n", x); // 11 —— 通过指针被修改了
return 0;
}
最经典的用法是交换函数。按值传递只是拷贝——交换会丢失。传指针才能让函数够到调用者的变量:
#include <stdio.h>
void swap_values(int a, int b) { // 拷贝:调用者看不到变化
int t = a; a = b; b = t;
}
void swap_pointers(int *a, int *b) { // 地址:交换是真的
int t = *a; *a = *b; *b = t;
}
int main(void) {
int x = 1, y = 2;
swap_values(x, y);
printf("%d %d\n", x, y); // 1 2 (没变!)
swap_pointers(&x, &y);
printf("%d %d\n", x, y); // 2 1 (交换了)
return 0;
}
A C string 字符串 is an array of char. Every string ends with a hidden null terminator 空终止符'\0', which marks where it stops. strlen (from <string.h>) counts characters up to that '\0'. Print a whole string with %s and one character 字符 with %c.
You can traverse a string by looping until you reach '\0'.
A struct 结构体 groups related values into one type. Each value is a member 成员. typedef gives the struct a short name so you can write Dog instead of struct Dog. Use . on a struct value, but -> on a pointer to a struct.
Common mistakes
Use . on a struct value and -> on a struct pointer.
typedef lets you drop the word struct when declaring; without it you write struct Point p;.
Assigning one struct to another copies all its fields.
中文
结构体(struct)把相关的值组合成一个类型。其中每个值是一个成员(member)。typedef 给结构体起一个短名字,这样你就能写 Dog 而不用写 struct Dog。对结构体值用 .,但对指向结构体的指针用 ->。
#include <stdio.h>
typedef struct {
char name[20];
int age;
} Dog;
int main(void) {
Dog d = {"Rex", 3};
printf("%s is %d\n", d.name, d.age); // Rex is 3 (对值用 .)
Dog *p = &d;
p->age = 4; // 对指针用 ->
printf("%s is %d\n", d.name, d.age); // Rex is 4
return 0;
}
常见错误
对结构体值用 .,对结构体指针用 ->。
typedef 让你声明时省去 struct 这个词;没有它就要写 struct Point p;。
malloc allocates 分配 memory on the heap 堆 at run time and returns a pointer to it. realloc resizes that block; free gives it back. Every malloc needs a matching free, or you get a memory leak 内存泄漏. These live in <stdlib.h>.
calloc(n, size) allocates an array like malloc AND fills it with zeros.
Common mistakes
Every malloc needs a matching free; forgetting to free leaks memory.
Using memory after free (a "use-after-free") is a serious bug.
Check that malloc did not return NULL before you use the memory.
A linked list 链表 is a chain of nodes 节点. Each node holds a value and a pointer to the next node; the last one points to NULL. Unlike an array it grows one node at a time with malloc. Always free every node when done.
#include <stdio.h>
#include <stdlib.h>
typedef struct Node {
int value;
struct Node *next;
} Node;
int main(void) {
Node *head = NULL;
for (int v = 10; v <= 30; v += 10) { // 依次头插 10、20、30
Node *n = malloc(sizeof(Node));
n->value = v;
n->next = head;
head = n;
}
for (Node *p = head; p != NULL; p = p->next) {
printf("%d ", p->value); // 30 20 10
}
printf("\n");
while (head != NULL) { // 释放整个链表
Node *t = head;
head = head->next;
free(t);
}
return 0;
}
10.2
栈与队列
English
A stack 栈 is LIFO 后进先出 (last in, first out): push and pop at the same end. A queue 队列 is FIFO 先进先出 (first in, first out): add at the back, remove from the front. Both are easy to build on an array with index variables.
Common mistakes
In a linked list, never lose the head pointer, or the whole list is unreachable.
Free every node when you are done, walking the list before you unlink.
Check for an empty list (a NULL head) before you pop.
中文
栈(stack)是 LIFO 后进先出(last in, first out):在同一端入栈和出栈。队列(queue)是 FIFO 先进先出(first in, first out):在尾部加入,从头部移除。两者都可以用数组加索引变量轻松实现。
#include <stdio.h>
int main(void) {
int stack[10];
int top = 0; // 下一个空位
stack[top++] = 1; // 入栈
stack[top++] = 2;
stack[top++] = 3;
while (top > 0) {
printf("%d ", stack[--top]); // 出栈:3 2 1
}
printf("\n");
return 0;
}
#include <stdio.h>
int main(void) {
int queue[10];
int front = 0, back = 0;
queue[back++] = 1; // 入队
queue[back++] = 2;
queue[back++] = 3;
while (front < back) {
printf("%d ", queue[front++]); // 出队:1 2 3
}
printf("\n");
return 0;
}
Linear search 线性查找 checks each element in turn — it works on any array. Binary search 二分查找 is far faster but needs a sorted 已排序 array: it checks the middle and discards half each step. Both return the index, or -1 if missing.
#include <stdio.h>
int linear(int a[], int n, int target) {
for (int i = 0; i < n; i++)
if (a[i] == target) return i;
return -1;
}
int binary(int a[], int n, int target) {
int lo = 0, hi = n - 1;
while (lo <= hi) {
int mid = (lo + hi) / 2;
if (a[mid] == target) return mid;
else if (a[mid] < target) lo = mid + 1;
else hi = mid - 1;
}
return -1;
}
int main(void) {
int a[] = {2, 5, 8, 12, 16, 23};
int n = sizeof(a) / sizeof(a[0]);
printf("%d %d %d\n", linear(a, n, 12), binary(a, n, 12), binary(a, n, 9));
return 0; // 3 3 -1
}
Linear scans all; binary halves a sorted range
11.2
排序
English
Bubble sort 冒泡排序 repeatedly compares neighbours and swaps 交换 any pair that is out of order. After each pass the largest value "bubbles" to the end.
#include <stdio.h>
int main(void) {
int a[] = {5, 2, 9, 1, 7};
int n = sizeof(a) / sizeof(a[0]);
for (int i = 0; i < n - 1; i++) {
for (int j = 0; j < n - 1 - i; j++) {
if (a[j] > a[j + 1]) {
int t = a[j]; a[j] = a[j + 1]; a[j + 1] = t;
}
}
}
for (int i = 0; i < n; i++) printf("%d ", a[i]);
printf("\n"); // 1 2 5 7 9
return 0;
}
11.3
递归
English
Recursion 递归 is a function that calls itself. It needs a base case 基准情形 to stop, and a recursive call that steps toward it. Without a base case it never ends.
Common mistakes
Binary search needs a sorted array; recursion needs a base case.
Each recursive call must move closer to the base case, or the stack overflows.
Bubble and insertion sort are O(n²) — fine to learn, slow at scale.
fopen returns a file pointer 文件指针; the mode 模式 string says what to do — "w" write, "r" read, "a" append. Write with fprintf, read with fscanf, and always fclose when done.
C has no exceptions. Instead a function signals failure with a return code 返回码 — by convention 0 means success and non-zero means an error. The caller checks the code before trusting the result. (fopen follows the same idea: it returns NULL when it fails.)
Common mistakes
Check that fopen did not return NULL before reading or writing.
Always fclose a file when you are done.
Return a non-zero code from main to signal an error to the caller.
中文
C 没有异常机制。函数改用返回码(return code)来报告失败——按惯例 0 表示成功,非零表示错误。调用者在信任结果之前先检查这个码。(fopen 遵循同样的思路:失败时返回 NULL。)
#include <stdio.h>
// 成功返回 0,除数为 0 时返回 -1
int safe_divide(int a, int b, int *result) {
if (b == 0) return -1; // 错误码
*result = a / b;
return 0; // 成功
}
int main(void) {
int r;
if (safe_divide(10, 2, &r) == 0)
printf("10 / 2 = %d\n", r); // 10 / 2 = 5
if (safe_divide(10, 0, &r) != 0)
printf("cannot divide by zero\n"); // 报告错误
return 0;
}
A bit 位 is a single 0 or 1; numbers are stored in binary 二进制. Bitwise 按位 operators work on the bits directly: & AND, | OR, ^ XOR, << shift left (×2 each step), >> shift right (÷2).
Hexadecimal 十六进制 (base 16) writes binary compactly: one hex digit is exactly four bits. Write hex literals with 0x; print with %x:
Run-length encoding (RLE) is a simple compression 压缩 method: replace each run 游程 of repeated characters with a count and the character. It is lossless 无损 — the original is fully recoverable.
Common mistakes
& is bitwise AND and && is logical AND — do not confuse them.
A left shift << 1 doubles a value; a right shift >> 1 halves it.
Bit n has value 1 << n (C has no ** power operator); check a bit with (x >> n) & 1.
#include <stdio.h>
#include <string.h>
int main(void) {
char *s = "aaabbc";
int n = strlen(s);
for (int i = 0; i < n; ) {
char c = s[i];
int run = 0;
while (i < n && s[i] == c) { run++; i++; }
printf("%d%c", run, c); // 3a2b1c
}
printf("\n");
return 0;
}
The preprocessor 预处理器 runs before compiling. #define makes a macro 宏 — plain text replaced everywhere. const makes a constant 常量 that cannot change. static inside a function keeps a variable's value between calls. An enum (enumeration 枚举) names a set of integers starting at 0.
Bigger programs split across files: declarations go in a header 头文件 file (mine.h), included with #include "mine.h" — quotes mean your own file, < > means the standard library. An include guard (#ifndef MINE_H / #define MINE_H / #endif) stops a header being included twice.
Common mistakes
#define does plain text replacement with no type checking; wrap macro bodies and arguments in parentheses.
A const value cannot be changed after it is set.
A static local variable keeps its value between calls to the function.
#include <stdio.h>
#define MAX 100 // macro: text replaced before compiling
enum Color { RED, GREEN, BLUE }; // named integers 0, 1, 2
int next_id(void) {
static int count = 0; // keeps its value between calls
count++;
return count;
}
int main(void) {
const double PI = 3.14; // cannot be changed
printf("%d %.2f\n", MAX, PI); // 100 3.14
printf("%d %d %d\n", RED, GREEN, BLUE); // 0 1 2
int a = next_id();
int b = next_id();
printf("%d %d\n", a, b); // 1 2
return 0;
}