Tự tạo một hàm printf thay thế cho hàm printf mặc định trong thư viện stdio.h (phần 2)
Bài viết được sự cho phép của tác giả Lê Xuân Quỳnh
Xin chào mọi người, lại là tôi Xuân Quỳnh đây
trong bài trước, các bạn đã làm quen với kiểu con trỏ qua ví dụ với con trỏ kiểu char. Bài hôm nay anh em sẽ tiến hành đi vào vấn đề chính là viết 1 thủ tục printf thay thế.
Chúng ta quay trở lại với thủ tục đã định nghĩa như sau:
void inramanhinh(char*caicaninra)
Bây giờ tôi sẽ giới thiệu 1 kiến thức mới, đó là thay vì bạn nhâp 1 ký tự thì bạn chơi với 1 chuỗi ký tự. Ví dụ chuỗi “abcd” chẳng hạn. Ký tự thì đặt trong nháy đơn, còn chuỗi thì nháy đôi các bạn nhé. Ví dụ tôi khai báo như sau:
Ở đây tôi sử dụng 2 kiến thức mới:
Mảng: Mảng là gì? Là 1 kiểu tập hợp các phần tử đơn thành tập để tiện quản lý. Ở đây tôi dùng pc[10] nghĩa là tôi tạo mảng 10 phần tử, được đếm từ 0 tới 9. Tôi đồng thời gán luôn 3 giá trị đầu lần lượt là a, b, c. Sau này trở đi, để khai báo mảng bạn dùng cú pháp như sau:
[Kiểu dữ liệu] [tên của biến][Số lượng phần tử]
Ví dụ:
int mangnguyen[100];
float dayso[20];
Vòng for: Vòng for dùng để thực hiện những việc lặp đi lặp lại, để code bạn viết ngắn gọn lại. Nếu ví dụ trên không dùng vòng for, bạn cũng có thể in ra 3 dòng như sau:
Giả sử bạn có 100 chữ cái thì bạn viết ra 100 dòng :)) Vậy bạn đã biết ý nghĩa của vòng for như nào rồi đó.
Cú pháp vòng for như sau:
for (giá trị khởi tạo; điều kiện dừng; nếu đúng điều kiện dừng thì làm việc ở đây)
Tôi sẽ giới thiệu vòng for kỹ hơn vào các bài sau. Tạm thời bạn biết thế đã
Khi cấp phát mảng 10 phần tử bạn hình dung bộ nhớ được phân phối như sau:
Bộ nhớ gồm 2 phần, 1 phần đánh địa chỉ từ 0, trên hình tôi để dạng hexa. Phần 2 là giá trị nằm trên ô nhớ đó. Ở đây tôi dùng kiểu char, cho nên 1 ô nhớ có kích thước đúng bằng sizeof(char). Vậy bạn có 10 ô, được đánh số từ 0 tới 9. Vậy, nếu bạn muốn dùng con trỏ thì làm như nào. Tôi sẽ dùng con trỏ khởi tạo 10 ô tương tự trên nha, đảm bảo không hiểu không tính tiền luôn :))
Ố ồ, nhìn có hoảng không các mem mới Tôi mà mới học tôi cũng hoảng. Nhiều cái mới quá. Ta tiến hành bóc tách từng thứ 1.
Đầu tiên là dòng thứ 2:
#include < stdlib.h >
Đây cũng là 1 thư viện mới. Tôi dùng hàm malloc cho nên tôi cần thư viện này. Nếu bạn build bằng gcc mà quên không include vào thì nó cũng báo cho bạn biết
Dòng này là chủ đạo:
pMangChu = (char*) malloc(sizeof(char) * 10);
Trông nó hoa vện như con giun ấy nhỉ. Để hiểu câu lệnh trên, ta xem hàm nguyên mẫu của các ông bên UK, US định nghĩa nó như nào nhé:
Nguyên mẫu tôi lấy từ trang này.
Bạn dịch hộ tôi cái. Còn tôi sẽ diễn giải đơn giản như này. Bạn thấy từ khóa void. Bạn thấy quen, nhưng ở đây thêm dấu *, nghĩa đây là con trỏ kiểu void. void là 1 kiểu dữ liệu chung chung, muốn trả về kiểu gì cũng được. Phía bên trong hà m malloc ta thấy dòng size_t size, bạn hiểu nó là chứa 1 cái số để cấp phát dữ liệu. Bạn thêm 100 thì nó cấp cho 100 byte, bạn cho 1000 thì nó cấp cho 1000 byte. Số cho vào đó phải là số dương!
tôi ép kiểu (char*) để void* thành char* (vì nó trả về kiểu gì cũng được mà, con trỏ là ok).
Phía trong tôi không cho 100 hay 1000 đâu nha, cho lung tung tạch bộ nhớ máy tính thì chết. Tôi tính kích thước của ông char xem bao nhiêu bằng hàm sizeof(char). Hàm sizeof này trả về đúng kích thước của kiểu, tính theo byte. Ví dụ trong hệ 32 bit thì kiểu int là 2byte, còn trong hệ 64bit là 4byte. Chẳng hạn như thế. Mỗi máy 1 thể loại nên tôi không cho số được mà tôi nhờ máy tính tính hộ cho tôi. Sau đó tôi nhân với 10 để tạo ra 10 ô nhớ liên tiếp. Tổng hợp lại là sizeof(char) * 10
Tiếp theo câu lệnh:
free(pMangChu);
Cái này là giải phóng con trỏ này, chương trình sẽ thu hồi 10 ô nhớ mà bạn vừa cấp phát ở trên. Từ sau lệnh này pMangChu coi như là đã chết :)) Bạn mà dùng lại nó thì sinh ra lỗi ngay. Không tin cứ thử gán *pMangChu = ‘a’ xem
Bạn nhớ vào đầu cho tôi, nói hơi bẩn tí là bạn ăn vào thì bạn phải ỉa ra, bạn nợ tiền ai thì bạn phải trả, đấy là quy luật ở cuộc sống rồi, có vay có trả. Bạn có vay bộ nhớ thì bạn phải trả cho nó. Bạn mà không trả thì chương trình sẽ sinh ra lỗi trong quá trình chạy như thiếu bộ nhớ, hay còn gọi là leak of memory. Cho nên cứ khắc vào đầu cứ malloc thì free. Cứ viết ra như thế cho đỡ quên. Ở các ngôn ngữ khác nó tự xử lý, cho nên năng suất chương trình không bằng C la điều đương nhiên rồi. Bạn chơi với con trỏ, bạn có quyền năng cao hơn java hay C#.
Bây giờ bạn muốn gán giá trị cho từng phần tử thì làm thế nào. Có 2 cách:
Bạn xem dòng 12 tới dòng 13. Tôi gán bình thường như bạn dùng mảng đã nói ở trên. Sau đó tôi in ra ký tự vị trí số 0(đầu tiên) đó.
Dòng 15 tới 16: Bạn nhớ cho tôi 1 điều, để lấy địa chỉ của 1 biến con trỏ, bạn cứ để nguyên tên pMangChu thì nó là địa chỉ. Muốn xem giá trị nằm trên địa chỉ này thì thêm * phía trước. Tôi dùng *(pMangChu) hay *(pMangChu + 0) là phần tử đầu tiên, *(pMangChu + 9) là phần tử thứ 10. Do vậy phép gán dòng 15 cũng tương tự với dòng 12.
Nhớ vào đầu cho tôi:
Dùng * để truy cập giá trị và để nguyên tên của biến con trỏ để truy cập địa chỉ!
Nhớ vào đầu cho tôi nhé các bạn trẻ =))
Bây giờ C nó cung cấp cho 1 thể loại không cần cấp phát bao nhiêu ô nhớ mà nó tự tính. Cụ thể như sau:
Kết quả chạy chương trình:
Quay lại xem code. Dòng thứ 9 bạn thấy rõ ràng C cho phép gán biến con trỏ luôn cho 1 xâu. Ở đây xâu này là mảng gồm các phần tử sau:
A, n, h, dấu cách, n, h, o, dấu cách, e, m, \0 (dấu kết thúc dòng)
Vậy tôi đếm bằng tay thì có 10 phần tử nhìn thấy được, tôi for từ 0 tới 9 để in ra, đó là câu lệnh 10. Cuối chương trình tôi free nó ra :)) Có vậy thôi.
Bạn thấy C ngon chưa. Ngon đúng không? Vậy chén tiếp thôi.
Rồi, nói mãi mãi vẫn chưa tới thủ tục:
void inramanhinh(char*caicaninra)
Bây giờ nhìn thủ tục trên bạn không sợ con trỏ caicaninra nữa rồi. Nó gọi là parameter, là tham số truyền vào thủ tục để làm gì đó, như cái miệng bạn há ra để dốc cơm vào, vào ruột thì nó xử lý thế nào kệ ruột nà.
Tôi viết hàm như sau:
Kết quả như sau:
Đáng yêu chưa =))
Rồi tôi giải thích. Bạn viết thủ tục inramanhinh và bạn phải đặt trước main, thì ông main ông ấy mới hiểu là trên đời này còn có 1 ông sinh ra trước, ông ấy mới cho phép dùng bên trong thân hàm ông ấy được. Có cách như này cũng được nè:
tiền trảm hậu tấu, khai báo trước, định nghĩa sau :))
Để hiểu thủ tục tôi viết, bạn quay về với hàm printf nguyên thủy của C:
Hàm này nhận vào 1 con trỏ kiểu char và kèm các tham số phía sau. Ở đây tôi cũng dùng con trỏ là 1 char* caicaninra nên khi tôi dùng câu lệnh 15 thì nó OK đúng chuẩn ISO rồi nhá
Tôi muốn các bạn muốn học C cho bài bản thì đọc kỹ hướng dẫn các hàm này ở google. Bạn có thể xem ở đây chẳng hạn.
Vậy là bạn đã biết cách viết 1 thủ tục làm việc cho bạn rồi đúng không Bạn thấy tốt cả chứ?
Bài tập cho các bạn:
- Hãy viết 1 hàm in ra tên bạn.
- Viết 1 hàm tráo đổi 2 số
Bạn nào mà coi trọng bài học này thì làm bài tập rồi comment ở dưới cho tôi code của các bạn nha.
Bài tiếp theo tôi sẽ hướng dẫn các bạn dùng makefile để build nhiều file chứ không 1 file như mấy bài đã học. Sẽ vui lắm đó. Toàn kiến thức cơ bản không :))
Happy
Bài viết gốc được đăng tải tại quynhlaptrinhc.wordpress.com
Có thể bạn quan tâm:
- B BenQ RD Series – Dòng Màn Hình Lập Trình 4k+ Đầu Tiên Trên Thế Giới
- i iOS 18 có gì mới? Có nên cập nhật iOS 18 cho iPhone của bạn?
- G Gamma AI là gì? Cách tạo slide chuyên nghiệp chỉ trong vài phút
- P Power BI là gì? Vì sao doanh nghiệp nên sử dụng PBI?
- K KICC HCMC x TOPDEV – Bước đệm nâng tầm sự nghiệp cho nhân tài IT Việt Nam
- T Trello là gì? Cách sử dụng Trello để quản lý công việc
- T TOP 10 SỰ KIỆN CÔNG NGHỆ THƯỜNG NIÊN KHÔNG NÊN BỎ LỠ
- T Tìm hiểu Laptop AI – So sánh Laptop AI với Laptop thường
- M MySQL vs MS SQL Server: Phân biệt hai RDBMS phổ biến nhất
- S SearchGPT là gì? Công cụ tìm kiếm mới có thể đánh bại Google?