This C programming guide is currently only available in Vietnamese. Please toggle the language switch (🇻🇳) in the top navigation to read the full article.
In this sixth part of the series, we cover one-dimensional and multi-dimensional arrays, C strings (null-terminated character arrays), the <string.h> library functions, safe input handling, and advanced string manipulation techniques in C.
Mảng (array) và chuỗi ký tự (string) là hai cấu trúc dữ liệu nền tảng và được sử dụng nhiều nhất trong lập trình C. Hiểu rõ cách chúng hoạt động ở mức bộ nhớ sẽ giúp bạn viết code hiệu quả, tránh các lỗi bảo mật nghiêm trọng như tràn bộ đệm (buffer overflow), và sẵn sàng cho các chủ đề nâng cao hơn như con trỏ và cấp phát bộ nhớ động.
1. Mảng một chiều (1D Arrays)
Khai báo và khởi tạo
Mảng là một tập hợp các phần tử cùng kiểu dữ liệu, được lưu trữ liên tiếp nhau trong
bộ nhớ. Chỉ số (index) bắt đầu từ 0.
// Cach 1: Khai bao roi gan gia tri tung phan tu
int scores[5];
scores[0] = 85;
scores[1] = 92;
scores[2] = 78;
scores[3] = 90;
scores[4] = 88;
// Cach 2: Khoi tao truc tiep khi khai bao
int scores[] = {85, 92, 78, 90, 88}; // Compiler tu dong tinh size = 5
// Cach 3: Khoi tao mot phan (phan tu con lai = 0)
int data[10] = {1, 2, 3}; // data[3]..data[9] deu bang 0
// Cach 4: Khoi tao tat ca bang 0
int zeros[100] = {0};
C KHONG kiem tra gioi han mang (No Bounds Checking)
Khac voi Java hay Python, C khong co bat ky co che kiem tra gioi han mang nao tai thoi diem chay (runtime). Truy cap ngoai pham vi mang la hanh vi khong xac dinh (undefined behavior) — chuong trinh co the chay binh thuong, tra ve gia tri rac (garbage value), hoac crash ngay lap tuc.
int arr[3] = {10, 20, 30};
// NGUY HIEM: Truy cap ngoai pham vi — Undefined Behavior!
printf("%d\n", arr[5]); // Doc gia tri rac tu vung nho khong thuoc mang
arr[-1] = 99; // Ghi de len vung nho khong thuoc mang — co the crash
Array decay thanh con tro
Khi truyen mang vao ham, mang tu dong "suy bien" (decay) thanh con tro toi phan tu dau tien. Dieu nay co nghia la:
arrtuong duong voi&arr[0]khi truyen vao ham.-
Ham nhan mang khong biet duoc kich thuoc cua mang — ban phai truyen them tham so
size. -
Meo
sizeof(arr)/sizeof(arr[0])chi hoat dong trong pham vi khai bao mang, khong hoat dong trong ham nhan mang.
Tim gia tri lon nhat va nho nhat trong mang
#include <stdio.h>
void findMaxMin(int arr[], int size, int *max, int *min) {
*max = arr[0];
*min = arr[0];
for (int i = 1; i < size; i++) {
if (arr[i] > *max) *max = arr[i];
if (arr[i] < *min) *min = arr[i];
}
}
int main() {
int numbers[] = {42, 17, 93, 8, 56, 71, 3, 88};
int size = sizeof(numbers) / sizeof(numbers[0]); // = 8, chi dung o day
int max, min;
findMaxMin(numbers, size, &max, &min);
printf("Max = %d, Min = %d\n", max, min); // Max = 93, Min = 3
return 0;
}
2. Mảng nhiều chiều (Multi-dimensional Arrays)
Khai bao mang 2 chieu
Mang 2 chieu thuc chat la mang cua cac mang. Trong bo nho, cac phan tu duoc luu theo thu tu hang truoc (row-major order) — toan bo hang 0 luu truoc, roi den hang 1, hang 2, v.v.
// Khai bao ma tran 3x4
int matrix[3][4] = {
{1, 2, 3, 4}, // Hang 0
{5, 6, 7, 8}, // Hang 1
{9, 10, 11, 12} // Hang 2
};
// Truy cap phan tu: matrix[hang][cot]
printf("%d\n", matrix[1][2]); // In ra: 7
Bo cuc bo nho (Memory Layout)
Mang 2 chieu int m[3][4] thuc te duoc luu lien tiep trong bo nho nhu sau:
Dia chi bo nho (row-major order):
┌────┬────┬────┬────┬────┬────┬────┬────┬────┬────┬────┬────┐
│m[0]│m[0]│m[0]│m[0]│m[1]│m[1]│m[1]│m[1]│m[2]│m[2]│m[2]│m[2]│
│[0] │[1] │[2] │[3] │[0] │[1] │[2] │[3] │[0] │[1] │[2] │[3] │
├────┼────┼────┼────┼────┼────┼────┼────┼────┼────┼────┼────┤
│ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 10 │ 11 │ 12 │
└────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┘
◄──── Hàng 0 ────► ◄──── Hàng 1 ────► ◄──── Hàng 2 ────►
Chuyen vi ma tran (Matrix Transpose)
#include <stdio.h>
#define ROWS 3
#define COLS 4
int main() {
int matrix[ROWS][COLS] = {
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12}
};
int transposed[COLS][ROWS]; // Dao kich thuoc
for (int i = 0; i < ROWS; i++) {
for (int j = 0; j < COLS; j++) {
transposed[j][i] = matrix[i][j];
}
}
printf("Ma tran chuyen vi:\n");
for (int i = 0; i < COLS; i++) {
for (int j = 0; j < ROWS; j++) {
printf("%3d ", transposed[i][j]);
}
printf("\n");
}
return 0;
}
Mang rang cua (Jagged Arrays) bang mang con tro
C khong ho tro mang rang cua truc tiep nhu Java. Tuy nhien, ban co the mo phong bang
mang cac con tro, trong do moi con tro tro toi mot mang co kich thuoc khac nhau (duoc
cap phat dong bang malloc).
#include <stdio.h>
#include <stdlib.h>
int main() {
int *rows[3]; // Mang 3 con tro
int sizes[] = {2, 5, 3}; // Moi hang co kich thuoc khac nhau
for (int i = 0; i < 3; i++) {
rows[i] = (int*) malloc(sizes[i] * sizeof(int));
for (int j = 0; j < sizes[i]; j++) {
rows[i][j] = (i + 1) * 10 + j; // Gan gia tri vi du
}
}
// In ra mang rang cua
for (int i = 0; i < 3; i++) {
printf("Hang %d (%d phan tu): ", i, sizes[i]);
for (int j = 0; j < sizes[i]; j++) {
printf("%d ", rows[i][j]);
}
printf("\n");
free(rows[i]); // Giai phong bo nho
}
return 0;
}
3. Chuỗi ký tự trong C (C Strings)
Trong C, khong co kieu du lieu string rieng biet nhu Java hay Python. Chuoi ky tu
trong C don gian la mang cac ky tu (char array) ket thuc bang ky tu null
'\0' (co gia tri ASCII la 0).
String literal vs char array
// Cach 1: Con tro tro toi string literal (CHI DOC — nam trong text segment)
char *greeting = "Hello";
// greeting[0] = 'h'; // NGUY HIEM! Undefined behavior — co the crash
// Cach 2: Mang ky tu (CO THE GHI — nam tren stack)
char name[] = "Hello";
name[0] = 'h'; // OK! name bay gio la "hello"
// Cach 3: Khai bao tuong minh voi null terminator
char city[6] = {'H', 'a', 'N', 'o', 'i', '\0'};
// Cach 4: Khai bao mang voi kich thuoc lon hon
char buffer[100] = "Hello"; // 5 ky tu + '\0', 94 byte con lai = 0
Bieu do bo nho cua chuoi ky tu
char name[] = "Hello";
Chi so: [0] [1] [2] [3] [4] [5]
┌─────┬─────┬─────┬─────┬─────┬─────┐
│ 'H' │ 'e' │ 'l' │ 'l' │ 'o' │ '\0'│
│ 72 │ 101 │ 108 │ 108 │ 111 │ 0 │ ← Gia tri ASCII
└─────┴─────┴─────┴─────┴─────┴─────┘
▲
Null terminator
(Danh dau ket thuc chuoi)
⚠️ Loi thuong gap:
-
Quen null terminator:
char s[5] = {'H','e','l','l','o'};— khong co'\0', cac ham xu ly chuoi se doc tran ra ngoai mang. - Tran bo dem (Buffer overflow): Sao chep chuoi dai vao mang ngan ma khong kiem tra kich thuoc.
4. Thu vien <string.h> — Cac ham xu ly chuoi
Thu vien <string.h> cung cap cac ham co ban de thao tac voi chuoi va vung nho. Duoi
day la bang tham khao nhanh:
| Ham | Mo ta | Luu y |
|---|---|---|
strlen(s)
|
Tra ve do dai chuoi (khong tinh '\0') | O(n) — phai duyet het chuoi |
strcpy(dest, src)
|
Sao chep src vao dest | ⚠️ Khong kiem tra kich thuoc dest! |
strncpy(dest, src, n)
|
Sao chep toi da n ky tu | Khong tu dong them '\0' neu src >= n |
strcat(dest, src)
|
Noi src vao cuoi dest | ⚠️ dest phai du cho |
strncat(dest, src, n)
|
Noi toi da n ky tu | Luon them '\0' sau khi noi |
strcmp(s1, s2)
|
So sanh 2 chuoi (tra ve 0 neu bang) | Phan biet hoa thuong |
strncmp(s1, s2, n)
|
So sanh toi da n ky tu dau | |
strchr(s, c)
|
Tim ky tu c dau tien trong s | Tra ve con tro hoac NULL |
strstr(haystack, needle)
|
Tim chuoi con needle trong haystack | Tra ve con tro hoac NULL |
strtok(s, delim)
|
Tach chuoi thanh cac token | ⚠️ Thay doi chuoi goc! Khong thread-safe |
memcpy(dest, src, n)
|
Sao chep n byte tu src sang dest | Khong an toan neu vung nho chong cheo |
memmove(dest, src, n)
|
Sao chep n byte (an toan khi chong cheo) | Cham hon memcpy mot chut |
memset(s, c, n)
|
Dat n byte dau tien cua s thanh gia tri c | Thuong dung de xoa mang ve 0 |
Canh bao bao mat
⚠️ Tai sao gets() bi xoa khoi C11? Ham gets() doc toan bo
dong nhap ma khong kiem tra kich thuoc buffer, gay ra lo hong
buffer overflow nghiem trong. Day la mot trong nhung lo hong bao mat bi khai thac
nhieu nhat trong lich su phan mem. Thay vao do, luon su dung fgets().
Vi du: Phan tich dong CSV bang strtok
#include <stdio.h>
#include <string.h>
int main() {
char csv_line[] = "Nguyen Van A,25,Ha Noi,Developer";
char *token;
int field = 0;
const char *labels[] = {"Ten", "Tuoi", "Thanh pho", "Nghe nghiep"};
// Lan goi dau tien: truyen chuoi goc
token = strtok(csv_line, ",");
while (token != NULL) {
printf("%s: %s\n", labels[field], token);
field++;
// Cac lan goi tiep theo: truyen NULL de tiep tuc tach
token = strtok(NULL, ",");
}
return 0;
}
// Ket qua:
// Ten: Nguyen Van A
// Tuoi: 25
// Thanh pho: Ha Noi
// Nghe nghiep: Developer
5. Mang ky tu va nhap chuoi an toan
So sanh cac phuong phap nhap chuoi
| Phuong phap | Doc dau cach? | Kiem tra buffer? | An toan? |
|---|---|---|---|
gets(buf)
|
Co | KHONG | ❌ Da bi xoa khoi C11 |
scanf("%s", buf)
|
Khong (dung tai dau cach) | KHONG (mac dinh) | ⚠️ Nguy hiem |
fgets(buf, size, stdin)
|
Co | CO (tham so size) | ✅ Khuyen dung |
Mau nhap lieu an toan voi fgets
#include <stdio.h>
#include <string.h>
int main() {
char name[50];
printf("Nhap ho ten cua ban: ");
// fgets doc toi da 49 ky tu + '\0', bao gom ca dau cach
if (fgets(name, sizeof(name), stdin) != NULL) {
// Xoa ky tu xuong dong '\n' o cuoi (fgets giu lai no)
size_t len = strlen(name);
if (len > 0 && name[len - 1] == '\n') {
name[len - 1] = '\0';
}
printf("Xin chao, %s!\n", name);
}
// Mau an toan: fgets + sscanf de doc so
char line[100];
int age;
printf("Nhap tuoi: ");
if (fgets(line, sizeof(line), stdin) != NULL) {
if (sscanf(line, "%d", &age) == 1) {
printf("Tuoi cua ban la: %d\n", age);
} else {
printf("Du lieu nhap khong hop le!\n");
}
}
return 0;
}
Minh hoa lo hong Buffer Overflow
#include <stdio.h>
#include <string.h>
int main() {
char password[8] = "secret";
char input[8];
printf("Nhap mat khau: ");
// NGUY HIEM: gets() khong kiem tra kich thuoc
// Neu nguoi dung nhap hon 7 ky tu, se tran sang bien password!
// gets(input); // KHONG BAO GIO DUNG HAM NAY!
// AN TOAN: Dung fgets thay the
fgets(input, sizeof(input), stdin);
size_t len = strlen(input);
if (len > 0 && input[len-1] == '\n') input[len-1] = '\0';
if (strcmp(input, password) == 0) {
printf("Truy cap thanh cong!\n");
} else {
printf("Sai mat khau!\n");
}
return 0;
}
6. Ky thuat xu ly chuoi nang cao
Dao nguoc chuoi tai cho (Two-pointer technique)
#include <stdio.h>
#include <string.h>
void reverseString(char *str) {
int left = 0;
int right = strlen(str) - 1;
while (left < right) {
// Hoan doi 2 ky tu o 2 dau
char temp = str[left];
str[left] = str[right];
str[right] = temp;
left++;
right--;
}
}
int main() {
char word[] = "Hello World";
printf("Truoc: %s\n", word);
reverseString(word);
printf("Sau: %s\n", word); // "dlroW olleH"
return 0;
}
Kiem tra Palindrome (chuoi doi xung)
#include <stdio.h>
#include <string.h>
#include <ctype.h>
int isPalindrome(const char *str) {
int left = 0;
int right = strlen(str) - 1;
while (left < right) {
// Bo qua ky tu khong phai chu/so
while (left < right && !isalnum(str[left])) left++;
while (left < right && !isalnum(str[right])) right--;
// So sanh khong phan biet hoa thuong
if (tolower(str[left]) != tolower(str[right])) {
return 0; // Khong phai palindrome
}
left++;
right--;
}
return 1; // La palindrome
}
int main() {
printf("'racecar': %s\n", isPalindrome("racecar") ? "Palindrome" : "Khong");
printf("'hello': %s\n", isPalindrome("hello") ? "Palindrome" : "Khong");
printf("'A man a plan a canal Panama': %s\n",
isPalindrome("A man a plan a canal Panama") ? "Palindrome" : "Khong");
return 0;
}
Tu cai dat ham atoi (chuyen chuoi thanh so nguyen)
#include <stdio.h>
#include <ctype.h>
int myAtoi(const char *str) {
int result = 0;
int sign = 1;
int i = 0;
// Bo qua khoang trang o dau
while (isspace(str[i])) i++;
// Xu ly dau + hoac -
if (str[i] == '+' || str[i] == '-') {
sign = (str[i] == '-') ? -1 : 1;
i++;
}
// Chuyen tung ky tu so thanh gia tri
while (isdigit(str[i])) {
result = result * 10 + (str[i] - '0');
i++;
}
return sign * result;
}
int main() {
printf("' -42' => %d\n", myAtoi(" -42")); // -42
printf("'12345' => %d\n", myAtoi("12345")); // 12345
printf("'+100' => %d\n", myAtoi("+100")); // 100
printf("'abc' => %d\n", myAtoi("abc")); // 0
return 0;
}
Thao tac ky tu voi <ctype.h>
Thu vien <ctype.h> cung cap cac ham tien ich de kiem tra va chuyen doi ky tu:
| Ham | Mo ta | Vi du |
|---|---|---|
toupper(c)
|
Chuyen thanh chu hoa |
toupper('a') → 'A'
|
tolower(c)
|
Chuyen thanh chu thuong |
tolower('Z') → 'z'
|
isdigit(c)
|
Kiem tra co phai chu so (0-9)? |
isdigit('5') → true
|
isalpha(c)
|
Kiem tra co phai chu cai? |
isalpha('x') → true
|
isalnum(c)
|
Kiem tra chu cai hoac chu so? |
isalnum('@') → false
|
isspace(c)
|
Kiem tra khoang trang? |
isspace(' ') → true
|
📥 Tai ve ma nguon mau: arrays_strings.c
Thu thach nho danh cho ban:
Hay viet mot chuong trinh dem so lan xuat hien cua moi ky tu (khong phan biet hoa thuong) trong mot
chuoi nhap tu ban phim, su dung mang dem int count[26] = {0};.
char *str = "Hello"; str[0] = 'h';
Comments
Bình luận