Bỏ qua để đến nội dung

Chuỗi C-style và ký tự null terminator

Một chuỗi ký tự C-style (char[]) không “biết” độ dài của chính nó - nó chỉ là một mảng ký tự, và quy ước là chuỗi phải kết thúc bằng ký tự đặc biệt '\0' (null terminator). Nếu thiếu ký tự này, mọi hàm xử lý chuỗi sẽ đọc tràn ra ngoài phạm vi mảng.

#include <cstring>
#include <iostream>
int main() {
char name[5] = {'A', 'l', 'i', 'c', 'e'}; // ĐỦ 5 ký tự, KHÔNG CÒN CHỖ cho '\0'!
std::cout << name << std::endl; // NGUY HIỂM! Đọc tràn ra ngoài mảng để tìm '\0'
std::cout << strlen(name) << std::endl; // Kết quả không xác định
return 0;
}
char correct_name[6] = {'A', 'l', 'i', 'c', 'e', '\0'}; // Đủ chỗ cho null terminator
char easier[] = "Alice"; // Cách viết dễ hơn - trình biên dịch TỰ ĐỘNG thêm '\0'
std::cout << sizeof(easier) << std::endl; // 6, KHÔNG PHẢI 5! (5 ký tự + 1 null terminator)
#include <cstring>
#include <iostream>
int main() {
char text[20] = "Hello";
std::cout << sizeof(text) << std::endl; // 20 - kích thước TOÀN BỘ mảng đã khai báo
std::cout << strlen(text) << std::endl; // 5 - số ký tự THỰC SỰ, tính đến trước '\0'
return 0;
}

Nhầm lẫn giữa hai hàm này là lỗi rất phổ biến khi làm việc với chuỗi C-style: sizeof là thông tin biết được lúc biên dịch (kích thước mảng), còn strlen phải quét từng ký tự lúc chạy chương trình cho đến khi gặp '\0'.

#include <cstring>
char destination[5];
const char* source = "Hello, World!"; // Dài hơn destination rất nhiều
strcpy(destination, source); // TRÀN BỘ ĐỆM (buffer overflow)! Ghi đè ra ngoài mảng destination

Đây là một trong những lỗi bảo mật kinh điển nhất trong lịch sử lập trình C/C++ (buffer overflow) - nhiều lỗ hổng bảo mật nghiêm trọng trong phần mềm thực tế bắt nguồn từ chính lỗi này.

#include <string>
#include <iostream>
int main() {
std::string name = "Alice"; // Tự quản lý bộ nhớ, không cần lo về null terminator
std::string destination;
std::string source = "Hello, World! Bat ke do dai bao nhieu";
destination = source; // Luôn an toàn - std::string tự cấp phát đủ chỗ
std::cout << destination << std::endl;
return 0;
}

std::string xử lý toàn bộ vấn đề cấp phát bộ nhớ và null terminator một cách tự động và an toàn - đây là lý do nó được khuyến khích dùng thay cho char[] trong hầu hết trường hợp.

Chuỗi C-style cần một ký tự '\0' (null terminator) ở cuối để các hàm xử lý chuỗi biết đâu là điểm kết thúc.

  • sizeof cho biết kích thước mảng đã khai báo; strlen đếm số ký tự thực sự đến trước '\0'
  • Copy chuỗi vào một mảng không đủ chỗ gây tràn bộ đệm (buffer overflow) - lỗi bảo mật kinh điển
  • Ưu tiên std::string để tránh toàn bộ lớp lỗi này