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.

array_basics.c
// 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.

bounds_danger.c
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:

  • arr tuong 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

find_max_min.c
#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.

2d_array.c
// 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)

transpose.c
#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).

jagged.c
#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

string_types.c
// 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

parse_csv.c
#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

safe_input.c
#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

buffer_overflow_demo.c
#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)

reverse_string.c
#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)

palindrome.c
#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)

my_atoi.c
#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};.

📝 Kiem tra kien thuc bai 6
Doan code sau co van de gi?
char *str = "Hello"; str[0] = 'h';

Related Articles

Bài viết liên quan trong series

Lesson 7: Structures (Struct), Union and Typedef in C Bài 7: Cấu trúc Struct, Union & từ khóa Typedef trong C Lesson 5: Functions, Recursion & Variable Scope in C Bài 5: Hàm, Đệ quy & Phạm vi biến trong C Back to C Series Overview Quay lại Lộ trình C Series