[Video] Tìm hiểu string trong lập trình C - Lập Trình C
Mở bài
Trong ngôn ngữ lập trình C, string (chuỗi ký tự) là một trong những khái niệm nền tảng nhưng cũng dễ gây nhầm lẫn cho người mới học. Khác với nhiều ngôn ngữ hiện đại như Python, Java hay JavaScript – nơi chuỗi được xem như một kiểu dữ liệu có sẵn (built-in type) – thì trong C, string thực chất chỉ là một mảng các ký tự kết thúc bằng ký tự đặc biệt '\0'. Chính vì cách tổ chức “thủ công” này mà lập trình viên cần hiểu rõ cách hoạt động của bộ nhớ, con trỏ, và thao tác với mảng để sử dụng string hiệu quả, tránh lỗi tràn bộ nhớ hay truy cập vùng nhớ không hợp lệ.
Hiểu rõ string trong lập trình C giúp bạn làm chủ các thao tác nhập – xuất dữ liệu, xử lý chuỗi, và xây dựng ứng dụng có tính ổn định cao. Bài viết này sẽ hướng dẫn chi tiết từ khái niệm cơ bản đến các thao tác nâng cao như khai báo, gán giá trị, duyệt chuỗi, nối chuỗi, so sánh chuỗi, cũng như sử dụng thư viện <string.h>. Ngoài ra, bạn còn học được các lời khuyên thực tế giúp hạn chế lỗi và tối ưu chương trình.
Cùng bắt đầu khám phá thế giới string trong ngôn ngữ C – nơi mỗi ký tự đều có ý nghĩa, và việc hiểu sâu về chúng sẽ giúp bạn trở thành lập trình viên C thực thụ!
Khái niệm cơ bản về string trong C
Trong ngôn ngữ C, string (chuỗi) không phải là kiểu dữ liệu riêng biệt, mà được biểu diễn bằng một mảng các ký tự (char array). Mỗi ký tự chiếm 1 byte trong bộ nhớ, và chuỗi luôn kết thúc bằng ký tự đặc biệt '\0' – được gọi là null terminator, đánh dấu kết thúc chuỗi.
Ví dụ:
Trong bộ nhớ, chuỗi này được lưu như sau:
| D | i | e | p | \0 |
Tổng cộng 5 byte, trong đó 4 ký tự và 1 ký tự kết thúc. Nếu bạn quên '\0', C sẽ không biết nơi kết thúc chuỗi, dẫn đến lỗi hoặc hiển thị ký tự rác.
Một cách khai báo khác:
Ở đây, mảng name có 10 ô, nhưng chỉ 5 ô được dùng. Phần còn lại chứa giá trị không xác định. Đây là cách phổ biến khi bạn muốn để sẵn không gian cho chuỗi dài hơn trong tương lai.
Điểm quan trọng:
-
Mỗi chuỗi là một mảng char.
-
Luôn có
'\0'ở cuối. -
Nếu quên ký tự kết thúc, chương trình có thể chạy sai hoặc bị crash.
Cách khai báo và khởi tạo string
Có nhiều cách để khai báo và khởi tạo chuỗi trong C. Dưới đây là những hình thức phổ biến nhất:
1. Khai báo trực tiếp bằng chuỗi ký tự
C tự động tính độ dài và thêm '\0'. Đây là cách an toàn, ngắn gọn.
2. Khai báo với kích thước xác định
Ưu điểm: bạn có thể thay đổi giá trị chuỗi sau này.
Nhược điểm: phải đảm bảo kích thước đủ lớn để chứa toàn bộ chuỗi + '\0'.
3. Khai báo từng ký tự
Ít dùng hơn, nhưng giúp bạn hiểu rõ cách string được lưu trữ.
4. Con trỏ tới chuỗi hằng
Ở đây, "Hello" là chuỗi hằng (constant string). Bạn không được phép sửa nội dung của chuỗi này, nếu làm vậy sẽ gây lỗi runtime.
Ví dụ sai:
Lời khuyên:
Khi cần thay đổi nội dung chuỗi, hãy dùng mảng. Khi chỉ cần đọc, có thể dùng con trỏ để tiết kiệm bộ nhớ.
Nhập và xuất chuỗi trong C
Để làm việc thực tế, bạn phải biết cách nhập dữ liệu chuỗi từ bàn phím và hiển thị ra màn hình.
1. Sử dụng printf và scanf
Tuy nhiên, scanf("%s", ...) không đọc được chuỗi có khoảng trắng, vì nó dừng khi gặp dấu cách hoặc xuống dòng.
2. Sử dụng fgets
Ưu điểm: đọc được cả chuỗi có khoảng trắng.
Nhược điểm: lưu cả ký tự xuống dòng \n, cần xóa thủ công.
3. In chuỗi
Lưu ý khi nhập chuỗi:
-
Không dùng
gets()vì hàm này không kiểm soát độ dài, dễ gây tràn bộ nhớ (buffer overflow). -
Dùng
fgets()là lựa chọn an toàn hơn.
Các hàm xử lý chuỗi trong thư viện <string.h>
Thư viện <string.h> cung cấp hàng chục hàm giúp bạn thao tác với chuỗi nhanh chóng và an toàn hơn.
1. strlen() – tính độ dài chuỗi
Không tính ký tự '\0'.
2. strcpy() – sao chép chuỗi
Chú ý: mảng dest phải đủ lớn để chứa toàn bộ chuỗi.
3. strcat() – nối chuỗi
4. strcmp() – so sánh chuỗi
Trả về:
-
0: hai chuỗi giống nhau
-
<0: chuỗi 1 nhỏ hơn chuỗi 2
-
0: chuỗi 1 lớn hơn chuỗi 2
5. strchr() và strstr()
-
strchr(s, c)→ tìm ký tựctrong chuỗis. -
strstr(s1, s2)→ tìm chuỗis2trongs1.
Ví dụ:
6. strncpy, strncat, strncmp
Đây là các phiên bản an toàn hơn, có giới hạn độ dài. Nên ưu tiên dùng để tránh tràn bộ nhớ.
Duyệt và xử lý từng ký tự trong chuỗi
Đôi khi, bạn muốn thao tác từng ký tự – ví dụ đếm chữ cái, đổi hoa thường, xóa ký tự đặc biệt,...
Ví dụ duyệt từng ký tự:
Ví dụ đếm số ký tự viết hoa:
Biến đổi chữ thường thành chữ hoa:
Các thao tác kiểu này giúp bạn rèn luyện tư duy xử lý chuỗi và hiểu rõ cách chuỗi hoạt động ở mức byte.
String và con trỏ: mối quan hệ mật thiết
Chuỗi và con trỏ trong C có mối liên hệ chặt chẽ, bởi tên mảng thường được coi là con trỏ tới phần tử đầu tiên.
Ví dụ:
Bạn có thể duyệt chuỗi bằng con trỏ thay vì chỉ số:
Khi hiểu con trỏ, bạn có thể viết các hàm xử lý chuỗi mạnh mẽ hơn, ví dụ:
Lưu ý: con trỏ chuỗi phải trỏ đến vùng nhớ hợp lệ. Nếu trỏ tới NULL hoặc vùng chưa cấp phát, chương trình sẽ lỗi.
Tránh lỗi thường gặp khi làm việc với string
Khi mới học C, lập trình viên thường gặp các lỗi liên quan đến chuỗi. Dưới đây là danh sách lỗi phổ biến và cách khắc phục:
-
Quên ký tự kết thúc
'\0'
→ Chuỗi không xác định điểm kết thúc, dễ crash. -
Tràn bộ nhớ (buffer overflow)
→ Dùngscanf("%s", str)nhưng không kiểm soát độ dài.
✅ Giải pháp: dùngfgets(str, sizeof(str), stdin)hoặcscanf("%49s", str). -
Gán chuỗi cho con trỏ không hợp lệ
-
So sánh chuỗi bằng
==
→ So sánh địa chỉ, không so nội dung.
✅ Dùngstrcmp(str1, str2). -
Không giải phóng bộ nhớ khi dùng
malloc
→ Dẫn đến rò rỉ bộ nhớ.
✅ Luônfree()sau khi dùng.
Ví dụ thực tế: đếm số từ trong chuỗi
Một bài tập kinh điển khi học string trong lập trình C là đếm số từ có trong chuỗi nhập vào:
Giải thích:
-
Duyệt từng ký tự, kiểm tra có phải khoảng trắng không.
-
Mỗi khi bắt đầu một từ mới (chữ không phải khoảng trắng sau khoảng trắng), tăng
count.
Bài này giúp bạn rèn luyện kỹ năng thao tác chuỗi, hiểu rõ ký tự và cách dùng hàm isspace() trong <ctype.h>.
Mở rộng: string động với malloc
Khi không biết trước độ dài chuỗi, bạn có thể cấp phát động:
Cấp phát động giúp bạn linh hoạt với dữ liệu lớn hoặc đọc file. Tuy nhiên, cần quản lý bộ nhớ cẩn thận để tránh memory leak.
Kết luận
Chuỗi (string) là linh hồn của xử lý dữ liệu trong lập trình C. Dù C không có kiểu dữ liệu “chuỗi” sẵn như các ngôn ngữ bậc cao khác, việc hiểu và làm chủ string trong lập trình C mang lại cho bạn quyền kiểm soát tuyệt đối với bộ nhớ, hiệu suất và cấu trúc chương trình. Qua bài viết này, bạn đã nắm được:
-
Cách khai báo, khởi tạo, nhập và xuất chuỗi.
-
Các hàm xử lý chuỗi phổ biến trong
<string.h>. -
Cách kết hợp con trỏ và mảng để thao tác linh hoạt.
-
Các lỗi thường gặp và cách phòng tránh.
Bây giờ, hãy tự mình thử viết chương trình xử lý chuỗi: nối chuỗi, đảo chuỗi, đếm ký tự,… Mỗi lần thực hành, bạn sẽ hiểu sâu hơn cách C hoạt động với bộ nhớ và con trỏ. Thành thạo chuỗi không chỉ giúp bạn học tốt lập trình C, mà còn mở đường để làm việc với các ngôn ngữ hệ thống khác như C++ hay Rust.
Hãy bắt đầu viết, thử sai, và khám phá sức mạnh thật sự của string trong lập trình C!