This page needs a recent browser (with SharedArrayBuffer support). Please update Chrome, Edge, Firefox or Safari to the latest version. · Trang này cần trình duyệt gần đây (hỗ trợ SharedArrayBuffer). Vui lòng cập nhật Chrome, Edge, Firefox hoặc Safari lên phiên bản mới nhất.
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".
Tiếng Việt
Mọi chương trình C đều bắt đầu tại main. #include <stdio.h> đưa vào một header để bạn có thể sử dụng printf để in. Một hàm là một khối có tên mà bạn có thể gọi; \n bắt đầu một dòng mới. main trả về 0 để biểu thị "thành công".
#include <stdio.h>
void greet(void) {
printf("Hello, world!\n");
}
int main(void) {
greet();
printf("I am learning C.\n");
return 0;
}
Mọi chương trình C đều chạy từ main sau khi bao gồm headers
1.2
Biến, kiểu & toán học
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:
Tiếng Việt
C yêu cầu một kiểu cho mỗi biến: int (số nguyên), double (số thực), char (một ký tự). printf sử dụng một định dạng specifier — %d, %f, %c — cho mỗi giá trị. Phép chia / giữa hai int là phép chia nguyên; phép chia % là phần dư.
#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;
}
Chuỗi định dạng điều khiển cả việc in và đọc:
Specifier
Type
Ví dụ output
%d
int
17
%f
double (%.2f = 2 chữ số thập phân)
3.14
%c
char
A
%s
một chuỗi
Mei
%zu
một kích thước (từ sizeof / strlen)
3
%x
int, in theo cơ số 16
ff
scanf("%d", &n) đọc dữ liệu nhập từ bàn phím với cùng các specifiers (lưu ý dấu &). Anh em sinh đôi của nó là sscanf parse các giá trị ra khỏi một chuỗi, vì vậy nó có thể chạy ở bất cứ đâu:
#include <stdio.h>
int main(void) {
char line[] = "Mei 88";
char name[20];
int score;
sscanf(line, "%19s %d", name, &score); // parse text -> values
printf("%s scored %d\n", name, score); // Mei scored 88
return 0;
}
1.3
Bình luận & phong cách
English
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.
Tiếng Việt
Một bình luận là ghi chú dành cho con người; trình biên dịch bỏ qua nó. Sử dụng // cho một dòng và /* ... */ cho một khối. Indentation tốt và tên rõ ràng giúp code dễ đọc.
#include <stdio.h>
int main(void) {
// a single-line comment
/* a block
comment */
int total = 3 + 4; // clear names help
printf("%d\n", total); // 7
return 0;
}
Lỗi thường gặp
Mỗi câu lệnh đều kết thúc bằng ;, và main nên trả về int (return 0;).
printf cần một chuỗi định dạng: %d cho int, %f cho double, và \n cho một dòng mới.
Một định dạng sai như %d cho một double sẽ in ra dữ liệu rác — hãy khớp đúng loại.
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 ==, !=, <, >, <=, >=.
Tiếng Việt
Một if chạy một khối khi điều kiện là true. C không có kiểu boolean thực sự mặc định: 0 là false và bất kỳ giá trị nào khác 0 đều là true. Xếp chồng lựa chọn với else if và else. So sánh với ==, !=, <, >, <=, >=.
#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 chọn nhánh true; else chọn nhánh false
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.
Tiếng Việt
Một switch chọn một case dựa trên một giá trị số nguyên. Mỗi case cần một break để thoát ra — nếu không, C sẽ "rơi xuống" case tiếp theo. default chạy khi không có gì khớp.
#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;
}
Lỗi thường gặp
if (x = 5) gán và luôn là true; hãy dùng == để so sánh.
Mỗi trường hợp switch cần một break;, nếu không sẽ rơi vào case tiếp theo.
0 là false và bất kỳ giá trị nào khác 0 đều là true trong một điều kiện.
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.
Tiếng Việt
Vòng lặp lặp lại một khối lệnh. Một vòng lặp while chạy chừng nào điều kiện còn đúng. Một vòng lặp for gộp phần khởi tạo, kiểm tra và tăng (i++) thành một dòng — tốt nhất khi bạn biết số lần lặp.
#include <stdio.h>
int main(void) {
int i = 1;
while (i <= 3) {
printf("%d ", i);
i++;
}
printf("\n"); // 1 2 3
for (int j = 0; j < 5; j++) {
printf("%d ", j);
}
printf("\n"); // 0 1 2 3 4
return 0;
}
while kiểm tra điều kiện; for đếm với bước nhảy
3.2
Tích lũy
English
A common pattern: start an accumulator 累加器 at 0, then add to it inside a loop. The same idea counts items or finds a running total.
Tiếng Việt
Một mẫu phổ biến: bắt đầu bộ tích lũy ở 0, sau đó cộng thêm vào bên trong vòng lặp. Ý tưởng tương tự dùng để đếm số lượng hoặc tìm tổng tích lũy.
#include <stdio.h>
int main(void) {
int total = 0;
for (int i = 1; i <= 5; i++) {
total += i; // 1 + 2 + 3 + 4 + 5
}
printf("%d\n", total); // 15
return 0;
}
3.3
Vòng lặp lồng nhau & mô hình
English
A loop inside another loop is a nested 嵌套 loop. The inner loop finishes fully for each step of the outer loop — perfect for grids and patterns.
Common mistakes
for (i = 0; i < n; i++) runs n times; a semicolon right after for (...) makes an empty loop.
A while whose condition never becomes false loops forever.
Declare the counter (int i) before or inside the for header.
Tiếng Việt
Vòng lặp nằm trong một vòng lặp khác là vòng lặp lồng nhau. Vòng lặp trong chạy hoàn toàn cho mỗi bước của vòng lặp ngoài — lý tưởng cho lưới và mô hình.
#include <stdio.h>
int main(void) {
for (int row = 0; row < 3; row++) {
for (int col = 0; col < 3; col++) {
printf("*");
}
printf("\n");
}
return 0;
}
Lỗi thường gặp
for (i = 0; i < n; i++) chạy n lần; dấu chấm phẩy ngay sau for (...) tạo ra một vòng lặp rỗng.
Một while mà điều kiện chưa bao giờ trở thành false sẽ lặp vô hạn.
Khai báo biến đếm (int i) trước hoặc trong header for.
A function takes parameters 参数 (inputs) and gives back a return value 返回值. The return type comes first (int, double, …); void means it returns nothing.
Tiếng Việt
Hàm nhận tham số (đầu vào) và trả về một giá trị. Kiểu trả về xuất hiện đầu tiên (int, double, …); void có nghĩa là hàm không trả về gì.
#include <stdio.h>
int square(int x) { // x is a parameter
return x * x; // hand back a value
}
int main(void) {
int r = square(5);
printf("%d\n", r); // 25
return 0;
}
Tham số vào; giá trị trả về ra
4.2
Mẫu khai báo & phạm vi
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.
Tiếng Việt
C đọc từ trên xuống dưới, nên một hàm phải được biết đến trước khi nó được gọi. Một mẫu khai báo — gồm dòng header plus ; — khai báo sớm để bạn có thể đặt main lên đầu. Phạm vi của một biến là khối lệnh chứa nó: nó chỉ tồn tại cục bộ và biến mất khi khối kết thúc.
#include <stdio.h>
int add(int a, int b); // prototype: declared before use
int main(void) {
printf("%d\n", add(3, 4)); // 7
return 0;
}
int add(int a, int b) { // definition comes later
int sum = a + b; // sum is local to add
return sum;
}
Lỗi thường gặp
Một hàm được sử dụng trước khi định nghĩa cần có bản mẫu ở trên main.
Đối số được truyền THEO GIÁ TRỊ — thay đổi bên trong hàm không ảnh hưởng đến nơi gọi trừ khi bạn truyền con trỏ.
Biến được khai báo bên trong hàm thì thuộc tính cục bộ của hàm đó.
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]).
Tiếng Việt
Mảng chứa nhiều giá trị cùng một kiểu. Chỉ mục bắt đầu từ 0. C không lưu độ dài mảng, vì vậy một mẹo phổ biến là tính số phần tử: 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;
}
Các ô trong mảng được chỉ mục từ 0
5.2
Thuật toán mảng
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.
Tiếng Việt
Duyệt qua mảng bằng vòng lặp for để tìm giá trị lớn nhất, tổng, hoặc số lượng. Luôn lặp từ 0 đến 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
Mảng 2 chiều
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.
Tiếng Việt
Mảng 2 chiều là lưới các hàng và cột: grid[row][col]. Sử dụng hai vòng lặp lồng nhau để truy cập mọi ô.
#include <stdio.h>
int main(void) {
int grid[2][3] = {{1, 2, 3}, {4, 5, 6}};
printf("%d\n", grid[1][2]); // 6
for (int r = 0; r < 2; r++) {
for (int c = 0; c < 3; c++) {
printf("%d ", grid[r][c]);
}
}
printf("\n"); // 1 2 3 4 5 6
return 0;
}
Lỗi thường gặp
C KHÔNG kiểm tra giới hạn mảng: việc ghi ⟨a[n]⟩ vào mảng kích thước ⟨n⟩ là hành vi không xác định.
Các chỉ mục hợp lệ chạy từ 0 đến n - 1.
Tên mảng là con trỏ tới phần tử đầu tiên; sizeof chỉ cung cấp kích thước trong phạm vi riêng của mảng.
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.
Tiếng Việt
Con trỏ lưu địa chỉ của một biến. &x trả về địa chỉ của x; *p giải tham chiếu con trỏ — nó đọc hoặc ghi giá trị được lưu tại đó. Truyền con trỏ cho phép hàm thay đổi biến của nơi gọi (truyền theo con trỏ).
Con trỏ lưu một địa chỉ; giải tham chiếu đi theo mũi tên đến giá trị
#include <stdio.h>
void addOne(int *p) { // p holds an address
*p = *p + 1; // change the value at that address
}
int main(void) {
int x = 10;
int *ptr = &x; // & takes the address of x
printf("%d\n", *ptr); // * reads the value: 10
addOne(&x);
printf("%d\n", x); // 11 — changed through the pointer
return 0;
}
Ứng dụng kinh điển là hàm hoán đổi. Truyền chỉ giá trị thì chỉ sao chép chúng — sự hoán đổi bị mất. Truyền con trỏ cho phép hàm truy cập biến của nơi gọi:
#include <stdio.h>
void swap_values(int a, int b) { // copies: the caller sees nothing
int t = a; a = b; b = t;
}
void swap_pointers(int *a, int *b) { // addresses: the swap is real
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 (unchanged!)
swap_pointers(&x, &y);
printf("%d %d\n", x, y); // 2 1 (swapped)
return 0;
}
Tên mảng hoạt động như con trỏ tới phần tử đầu tiên, và toán học con trỏ bước nhảy theo từng phần tử nguyên vẹn:
#include <stdio.h>
int main(void) {
int a[] = {10, 20, 30};
int *p = a; // same as &a[0]
printf("%d\n", *p); // 10
printf("%d\n", *(p + 2)); // 30 — same as a[2]
return 0;
}
NULL là con trỏ không trỏ vào đâu — hãy kiểm tra nó trước khi giải tham chiếu.
Lỗi thường gặp
&x là địa chỉ của x; *p là giá trị được lưu tại p.
Tuyệt đối không sử dụng *p trên con trỏ chưa khởi tạo hoặc NULL — nó gây crash hoặc làm hỏng bộ nhớ.
Để thay đổi biến của nơi gọi, truyền địa chỉ của nó và ghi thông qua con trỏ.
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'.
Tiếng Việt
Chuỗi C là mảng char. Mọi chuỗi đều kết thúc bằng ký kết thúc null ẩn '\0', đánh dấu nơi nó dừng lại. strlen (từ <string.h>) đếm số ký tự cho đến '\0' đó. In cả chuỗi với %s và một ký tự với %c.
#include <stdio.h>
#include <string.h>
int main(void) {
char name[] = "Mei"; // 'M', 'e', 'i', '\0'
printf("%s\n", name); // Mei
printf("%zu\n", strlen(name)); // 3 (stops at '\0')
printf("%c\n", name[0]); // M
return 0;
}
Chuỗi C là mảng char kết thúc bằng ký kết thúc null ẩn
Bạn có thể duyệt chuỗi bằng cách lặp cho đến khi gặp '\0'.
#include <stdio.h>
int main(void) {
char word[] = "banana";
int count = 0;
for (int i = 0; word[i] != '\0'; i++) {
if (word[i] == 'a') count++;
}
printf("%d\n", count); // 3
return 0;
}
7.2
Thư viện string.h
English
#include <string.h> gives you the everyday string tools:
Function
Does
strlen(s)
the length, not counting the terminator
strcpy(dst, src)
copy — dst must be big enough
strcat(dst, src)
append src onto the end of dst
strcmp(a, b)
compare: 0 when equal, else negative / positive
strcmp returns 0 for equal, so the test is strcmp(a, b) == 0 — never a == b.
The destination array must have room for the result plus the terminator.
Common mistakes
A C string needs one extra byte for the \0 terminator: a 5-letter word needs char[6].
Copy and compare strings with strcpy / strcmp, not = / ==.
Forgetting the \0 makes printf("%s", ...) run past the end of the text.
Tiếng Việt
#include <string.h> cung cấp cho bạn các công cụ xử lý chuỗi cơ bản:
Hàm
Tác dụng
strlen(s)
độ dài, không tính ký kết thúc
strcpy(dst, src)
sao chép — dst phải đủ rộng
strcat(dst, src)
nối src vào cuối dst
strcmp(a, b)
so sánh: 0 khi bằng nhau, ngược lại âm / dương
#include <stdio.h>
#include <string.h>
int main(void) {
char full[40];
strcpy(full, "Mei"); // full is now "Mei"
strcat(full, " Chen"); // append -> "Mei Chen"
printf("%s (%zu)\n", full, strlen(full)); // Mei Chen (8)
printf("%d\n", strcmp("apple", "apple") == 0); // 1 (equal)
return 0;
}
strcmp trả về 0 khi bằng nhau, vì thế phép kiểm tra là strcmp(a, b) == 0 — tuyệt đối không dùng a == b.
Mảng đích phải có chỗ cho kết quả cùng với ký kết thúc.
Lỗi thường gặp
Một chuỗi C cần một byte dư cho ký kết thúc \0: một từ 5 chữ cái cần char[6].
sao chép và so sánh chuỗi với strcpy / strcmp, không phải = / ==.
Quên \0 khiến printf("%s", ...) vượt quá cuối văn bản.
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.
Tiếng Việt
struct nhóm các giá trị liên quan thành một kiểu. Mỗi giá trị là một thành viên. typedef đặt tên ngắn cho struct để bạn có thể viết Dog thay vì struct Dog. Dùng . trên giá trị struct, nhưng -> trên con trỏ tới struct.
#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 (. on a value)
Dog *p = &d;
p->age = 4; // -> on a pointer
printf("%s is %d\n", d.name, d.age); // Rex is 4
return 0;
}
Lỗi thường gặp
Sử dụng . trên giá trị struct và -> trên con trỏ struct.
typedef cho phép bạn bỏ qua từ struct khi khai báo; nếu không có nó, bạn phải viết struct Point p;.
Gán một struct cho cấu trúc khác sẽ sao chép tất cả các trường của nó.
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.
Tiếng Việt
malloc cấp phát bộ nhớ trên heap tại thời điểm chạy và trả về con trỏ trỏ đến đó. realloc thay đổi kích thước khối bộ nhớ đó; free giải phóng nó lại. Mỗi lần gọi malloc đều cần một call tương ứng của free, nếu không sẽ gây rò rỉ bộ nhớ. Những hàm này nằm trong <stdlib.h>.
Locals live on the stack; malloc memory lives on the heap until you free it
#include <stdio.h>
#include <stdlib.h>
int main(void) {
int n = 3;
int *a = malloc(n * sizeof(int)); // room for 3 ints
for (int i = 0; i < n; i++) a[i] = i * 10;
a = realloc(a, 4 * sizeof(int)); // grow to 4 ints
a[3] = 30;
for (int i = 0; i < 4; i++) printf("%d ", a[i]);
printf("\n"); // 0 10 20 30
free(a); // hand the memory back
return 0;
}
calloc(n, size) allocates an array like malloc AND fills it with zeros.
Lỗi thường gặp
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.
Tiếng Việt
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.
A linked list: each node holds a value and a pointer to the next, ending at NULL
#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) { // prepend 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) { // free the whole list
Node *t = head;
head = head->next;
free(t);
}
return 0;
}
10.2
Stacks & queues
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.
Tiếng Việt
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.
#include <stdio.h>
int main(void) {
int stack[10];
int top = 0; // next free slot
stack[top++] = 1; // push
stack[top++] = 2;
stack[top++] = 3;
while (top > 0) {
printf("%d ", stack[--top]); // pop: 3 2 1
}
printf("\n");
return 0;
}
#include <stdio.h>
int main(void) {
int queue[10];
int front = 0, back = 0;
queue[back++] = 1; // enqueue
queue[back++] = 2;
queue[back++] = 3;
while (front < back) {
printf("%d ", queue[front++]); // dequeue: 1 2 3
}
printf("\n");
return 0;
}
Lỗi thường gặp
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.
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.
Tiếng Việt
Tìm kiếm tuyến tính (linear search) kiểm tra từng phần tử một — nó hoạt động trên mọi loại mảng. Tìm kiếm nhị phân (binary search) nhanh hơn nhiều nhưng cần mảng đã sắp xếp: nó kiểm tra phần tử ở giữa và loại bỏ một nửa mỗi bước. Cả hai đều trả về chỉ số, hoặc -1 nếu không tìm thấy.
#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
Sorting
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.
Tiếng Việt
Sắp xếp bong bóng (bubble sort) liên tục so sánh các phần tử kề nhau và hoán đổi bất kỳ cặp nào không đúng thứ tự. Sau mỗi lượt duyệt, giá trị lớn nhất sẽ "nổi" lên cuối mảng.
#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
Đệ quy
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.
Tiếng Việt
Đệ quy (recursion) là một hàm gọi chính nó. Nó cần một trường hợp cơ sở (base case) để dừng lại, và một lời gọi đệ quy tiến gần về phía trường hợp đó. Không có base case thì nó sẽ không bao giờ kết thúc.
#include <stdio.h>
int factorial(int n) {
if (n <= 1) return 1; // base case
return n * factorial(n - 1); // recursive call
}
int main(void) {
printf("%d\n", factorial(5)); // 120
return 0;
}
Lỗi thường gặp
Tìm kiếm nhị phân cần một mảng đã sắp xếp; đệ quy cần một base case.
Mỗi lần gọi đệ quy phải tiến gần hơn đến trường hợp cơ sở, nếu không thì stack sẽ bị tràn.
Sắp xếp nổi và sắp xếp chèn có độ phức tạp O(n²) — dễ học nhưng chậm khi xử lý dữ liệu lớn.
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.
Tiếng Việt
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.
#include <stdio.h>
int main(void) {
FILE *out = fopen("scores.txt", "w"); // open for writing
fprintf(out, "Alice 80\nBob 95\n");
fclose(out);
FILE *in = fopen("scores.txt", "r"); // open for reading
char name[20];
int score;
while (fscanf(in, "%s %d", name, &score) == 2) {
printf("%s -> %d\n", name, score);
}
fclose(in);
return 0;
}
fopen → read/write → fclose
12.2
Error handling with return codes
English
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.
Tiếng Việt
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.)
#include <stdio.h>
// returns 0 on success, -1 if the divisor is 0
int safe_divide(int a, int b, int *result) {
if (b == 0) return -1; // error code
*result = a / b;
return 0; // success
}
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"); // error reported
return 0;
}
Lỗi thường gặp
Kiểm tra xem fopen có trả về NULL hay không trước khi đọc hoặc ghi.
Always fclose a file when you are done.
Return a non-zero code from main to signal an error to the caller.
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:
Tiếng Việt
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).
#include <stdio.h>
int main(void) {
int a = 12; // 1100
int b = 10; // 1010
printf("%d\n", a & b); // 8 AND -> 1000
printf("%d\n", a | b); // 14 OR -> 1110
printf("%d\n", a ^ b); // 6 XOR -> 0110
printf("%d\n", a << 1); // 24 left shift
printf("%d\n", a >> 1); // 6 right shift
return 0;
}
Hexadecimal (base 16) writes binary compactly: one hex digit is exactly four bits. Write hex literals with 0x; print with %x:
#include <stdio.h>
int main(void) {
printf("%d\n", 0xFF); // 255 (0x marks a hex literal)
printf("%x\n", 255); // ff
printf("%x\n", 12); // c
return 0;
}
Bitwise operators work on the bits of integers
13.2
Mã hóa độ dài chuỗi (Run-length encoding)
English
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.
Tiếng Việt
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.
#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;
}
Lỗi thường gặp
& là AND bit và && là AND logic — đừng nhầm lẫn.
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.
14Preprocessor & qualifiers · Tiền xử lý & bộ修饰符
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.
Tiếng Việt
Tiền trình viên chạy trước khi biên dịch. #define tạo macro — văn bản thường được thay thế ở mọi nơi. const tạo hằng số không thể thay đổi. static bên trong hàm giữ nguyên giá trị biến giữa các lần gọi. Một ENUM (enum) đặt tên cho một tập hợp các số nguyên bắt đầu từ 0.
#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;
}
Các chương trình lớn hơn chia sẻ nhiều file: phần khai báo nằm trong file header (mine.h), được bao gồm bằng #include "mine.h" — dấu ngoặc kép có nghĩa là file của bạn, < > có nghĩa là thư viện tiêu chuẩn. Một include guard (#ifndef MINE_H / #define MINE_H / #endif) ngăn file header được bao gồm hai lần.
Lỗi thường gặp
#define thực hiện thay thế văn bản thường mà không có kiểm tra kiểu; hãy bọc thân macro và đối số trong dấu ngoặc.
Giá trị const không thể thay đổi sau khi đã được gán.
Biến cục bộ static giữ nguyên giá trị giữa các lần gọi đến hàm.
Pick one and the site follows you — notes, papers, videos and practice all open on it. · Chọn một môn và trang sẽ điều hướng theo — ghi chú, tài liệu, video và bài tập đều mở ở đó.
Type to search notes, lessons, code, vocabulary and past-paper questions across every subject. · Nhập để tìm ghi chú, bài học, mã, từ vựng và câu hỏi đề thi cũ trên mọi môn học.