Bản Chất Của Ký Tự Và Chuỗi
Trong ngôn ngữ C, ký tự đơn đại diện cho một đơn vị văn bản duy nhất, bao gồm chữ cái, chữ số, dấu câu hoặc ký tự đặc biệt. Ký tự luôn được bao bọc bởi dấu nháy đơn (' '), ví dụ: 'Z', '7', '@'.
Ngược lại, chuỗi là một dãy các ký tự liên tiếp trong bộ nhớ, được kết thúc bằng ký tự null \0. Chuỗi sử dụng dấu nháy kép (" "). Kích thước thực tế của một mảng chuỗi luôn bằng số ký tự hữu ích cộng thêm 1 byte dành cho ký tự kết thúc. Ví dụ, khai báo char text[] = "hello world"; sẽ tạo ra một mảng gồm 11 ký tự nhìn thấy, 1 ký tự khoảng trắng và 1 byte \0, tổng cộng 13 byte.
Thao Tác Với Ký Tự Đơn
Khai Báo Và Xuất Dữ Liệu
Biến kiểu char chiếm 1 byte và có thể hoạt động tương tự như số nguyên. Tùy thuộc vào hệ thống, nó có thể là dạng có dấu (-128 đến 127) hoặc không dấu (0 đến 255). Khi in ra màn hình, định dạng %c sẽ hiển thị ký tự tương ứng, trong khi %d trả về mã ASCII của ký tự đó.
#include <stdio.h>
int main() {
char ky_tu_mau = 'A';
printf("Dang ky tu: %c\n", ky_tu_mau);
printf("Dang so ASCII: %d\n", ky_tu_mau);
return 0;
}
Nhập Dữ Liệu Ký Tự
Việc đọc ký tự từ bàn phím có thể thực hiện qua scanf hoặc getchar. Lưu ý rằng scanf với %c sẽ đọc mọi ký tự, kể cả khoảng trắng hoặc ký tự xuống dòng còn sót lại trong bộ đệm.
#include <stdio.h>
int main() {
char input;
printf("Nhap mot ky tu: ");
scanf(" %c", &input); // Them khoang trang truoc %c de bo qua ky tu trang
printf("Da nhan: %c\n", input);
return 0;
}
Xử Lý Chuỗi Ký Tự
Các Phương Thức Nhập An Toàn
Sử dụng scanf với %s tiềm ẩn rủi ro tràn bộ đệm và không đọc được khoảng trắng. Hàm gets() đã bị loại bỏ hoàn toàn khỏi chuẩn C11 do không thể kiểm soát độ dài đầu vào. Phương án tối ưu là dùng fgets().
#include <stdio.h>
#include <string.h>
int main() {
char bo_dem[50];
printf("Nhap doan van ban: ");
fgets(bo_dem, sizeof(bo_dem), stdin);
// Loai bo ky tu xuong duong neu co
size_t vi_tri = strcspn(bo_dem, "\n");
bo_dem[vi_tri] = '\0';
printf("Chuoi da xu ly: %s\n", bo_dem);
return 0;
}
Quản Lý Tập Hợp Nhiều Chuỗi
Mảng Hai Chiều
Mỗi chuỗi được lưu trong một hàng cố định của ma trận ký tự. Phương pháp này cho phép sửa đổi trực tiếp nội dung từng chuỗi nhưng tốn bộ nhớ nếu độ dài thực tế ngắn hơn kích thước khai báo.
#include <stdio.h>
int main() {
char ds_ten[][12] = {"Nguyen", "Tran", "Le"};
int so_luong = sizeof(ds_ten) / sizeof(ds_ten[0]);
for (int i = 0; i < so_luong; i++) {
printf("Phan tu %d: %s\n", i + 1, ds_ten[i]);
}
return 0;
}
Mảng Con Trỏ Hằng
Cách này lưu trữ địa chỉ của các chuỗi hằng trong một mảng con trỏ. Cú pháp gọn nhẹ và tiết kiệm bộ nhớ, nhưng nội dung chuỗi nằm trong vùng chỉ đọc, không thể thay đổi trực tiếp.
#include <stdio.h>
int main() {
const char *mang_doi_tuong[] = {"Shift", "Control", "Alt"};
int tong_so = sizeof(mang_doi_tuong) / sizeof(mang_doi_tuong[0]);
for (int idx = 0; idx < tong_so; idx++) {
printf("Phim: %s\n", mang_doi_tuong[idx]);
}
return 0;
}
Cấp Phát Động Bộ Nhớ
Phù hợp khi số lượng hoặc độ dài chuỗi chưa xác định tại thời điểm biên dịch. Yêu cầu quản lý bộ nhớ thủ công để tránh rò rỉ tài nguyên.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
char **ds_dong = (char **)malloc(3 * sizeof(char *));
if (!ds_dong) return 1;
const char *tam[] = {"Alpha", "Beta", "Gamma"};
for (int i = 0; i < 3; i++) {
size_t len = strlen(tam[i]) + 1;
ds_dong[i] = (char *)malloc(len * sizeof(char));
if (ds_dong[i]) {
memcpy(ds_dong[i], tam[i], len);
}
}
for (int i = 0; i < 3; i++) {
printf("%s\n", ds_dong[i]);
free(ds_dong[i]);
}
free(ds_dong);
return 0;
}
| Phương pháp | Khả năng sửa đổi | Quản lý bộ nhớ | Trường hợp áp dụng |
|---|---|---|---|
| Mảng con trỏ | Không | Tự động | Dữ liệu tĩnh, chỉ đọc |
| Mảng 2 chiều | Có | Cố định | Cần chỉnh sửa nội dung chuỗi |
| Cấp phát động | Có | Thủ công | Dữ liệu biến đổi theo runtime |
Các Hàm Thao Tác Chuỗi Cơ Bản
Toàn bộ hàm dưới đây đều nằm trong thư viện <string.h>.
strcpy và strcat
strcpy sao chép toàn bộ nội dung nguồn sang đích. strcat nối chuỗi phụ vào đuôi chuỗi chính.
#include <stdio.h>
#include <string.h>
int main() {
char dich[50] = "Xin chao";
char nguon[] = ", moi nguoi!";
strcpy(dich, "Noi dung moi"); // Ghi de
strcat(dich, nguon); // Noi them
printf("%s\n", dich);
return 0;
}
strlen và strcmp
strlen đếm số ký tự thực tế (bỏ qua \0). strcmp so sánh theo giá trị ASCII, trả về 0 nếu trùng khớp, giá trị âm nếu chuỗi thứ nhất nhỏ hơn, và dương nếu lớn hơn.
#include <stdio.h>
#include <string.h>
int main() {
char s1[] = "apple";
char s2[] = "banana";
int so_sanh = strcmp(s1, s2);
printf("Do dai s1: %zu\n", strlen(s1));
if (so_sanh < 0) printf("s1 dung truoc s2 trong tu dien.\n");
return 0;
}
strchr và strstr
strchr tìm vị trí xuất hiện đầu tiên của một ký tự đơn. strstr tìm vị trí xuất hiện đầu tiên của một chuỗi con. Cả hai đều trả về con trỏ trỏ đến vị trí tìm thấy hoặc NULL nếu không có kết quả.
#include <stdio.h>
#include <string.h>
int main() {
char van_ban[] = "Lap trinh C rat manh me";
char ky_tim = 't';
char chuoi_con[] = "manh";
char *ket_qua_ky = strchr(van_ban, ky_tim);
if (ket_qua_ky) printf("Tim thay '%c' tai: %s\n", ky_tim, ket_qua_ky);
char *ket_qua_chuoi = strstr(van_ban, chuoi_con);
if (ket_qua_chuoi) printf("Tim thay chuoi con: %s\n", ket_qua_chuoi);
return 0;
}