Chuỗi trong C
Chuỗi là mảng ký tự kết thúc bằng ký tự null '\0' (ASCII 0).
Các hàm nhập/xuất chính
| Hàm | Header | Đặc điểm | Đọc khoảng trắng? |
|---|---|---|---|
scanf("%s", s) | stdio.h | Đọc đến khoảng trắng/null | Không |
gets(s) | stdio.h | Đọc cả dòng, KHÔNG AN TOÀN (đã xóa C11) | Có |
fgets(s, n, stdin) | stdio.h | Đọc n-1 ký tự, an toàn, giữ newline | Có |
puts(s) | stdio.h | In chuỗi + tự thêm newline | - |
printf("%s", s) | stdio.h | In chuỗi, không thêm newline | - |
Chương trình 1: scanf() - Chỉ đọc từ đơn
#include <stdio.h>
int main() {
char ten[30];
printf("Nhap ten: ");
scanf("%s", ten); // Dừng ở khoảng trắng
printf("Ten ban: %s\n", ten);
return 0;
}
Kết quả
Nhap ten: Nguyen Van A
Ten ban: Nguyen
→ Chỉ đọc được "Nguyen", phần "Van A" bị bỏ qua.
Chương trình 2: gets() - CẢNH BÁO: KHÔNG NÊN DÙNG
#include <stdio.h>
int main() {
char ten[30];
printf("Nhap ten: ");
gets(ten); // ĐỘC HẠI: không kiểm tra giới hạn buffer!
printf("Ten ban: %s\n", ten);
return 0;
}
Lý do không dùng gets(): Không kiểm tra độ dài → Buffer overflow → Lỗ hổng bảo mật nghiêm trọng. Đã bị xóa khỏi chuẩn C11.
Chương trình 3: fgets() - KHUYẾN NGHỊ (An toàn)
#include <stdio.h>
#include <string.h>
int main() {
char ten[30];
printf("Nhap ten: ");
fgets(ten, sizeof(ten), stdin); // Đọc tối đa 29 ký tự + '\0'
// fgets giữ lại '\n' nếu còn chỗ, cần xóa nó
ten[strcspn(ten, "\n")] = '\0';
printf("Ten ban: %s\n", ten);
return 0;
}
Kết quả
Nhap ten: Nguyen Van A
Ten ban: Nguyen Van A
Chương trình 4: puts() vs printf()
#include <stdio.h>
int main() {
char s[] = "Hello World";
printf("printf: ");
printf("%s", s); // Không tự xuống dòng
printf(" <- het\n");
printf("puts: ");
puts(s); // Tự thêm '\n'
printf(" <- het\n");
return 0;
}
Kết quả
printf: Hello World <- het
puts: Hello World
<- het
Bảng so sánh chi tiết
| Tiêu chí | scanf("%s") | gets() | fgets() |
|---|---|---|---|
| Đọc khoảng trắng | Không | Có | Có |
| Kiểm tra buffer | Không (nếu không dùng %29s) | Không | Có (tham số size) |
| Giữ newline | Không | Không (tự bỏ) | Có (nếu đủ chỗ) |
| An toàn | Trung bình | Rất kém | Tốt |
| Chuẩn C | C89 | C90 (xóa C11) | C89 |
Best Practice
// Hàm helper đọc chuỗi an toàn
void docChuoi(char *buf, int size) {
if (fgets(buf, size, stdin)) {
buf[strcspn(buf, "\n")] = '\0'; // Xóa newline
}
}
// Sử dụng
char ten[50];
docChuoi(ten, sizeof(ten));
Luôn dùng fgets() + xóa newline cho nhập chuỗi an toàn, chuyên nghiệp.