Academia.eduAcademia.edu
Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 1) Modular Programming (Mô-đun L p trình)  Prototypes.  Headers.  Separate Compilation.  Ph m Vi Sử D ng (scope) c a functions và bi n số. 5) Preprocessor ậ Tiền xử lý  #include.  #define.  Macro.  Condition. 6) T o ra những bi n kiểu riêng c a b n  C u trúc (struct).  M ng c u trúc.  Typedef. 2) Pointer (Con trỏ)  V n đề nan gi i.  Địa chỉ trong b nh .  Cách sử d ng pointer (con trỏ).  Cách sử d ng con trỏ trong m t function. 7) Những thao tác làm việc v i t p tin (file)  M và đóng t p tin.  Ghi dữ liệu vào t p tin.  Đọc dữ liệu trong t p tin.  Di chuyển t p tin.  Đổi tên và xóa t p tin. 3) Arrays (M ng)  Các Arrays trong b nh .  Cách t o m t array.  Liệt kê các giá trị trong array.  T o m t function để liệt kê các giá trị trong array.  Bài t p thực hành. 8) C p phát đ ng  Kích th c c a bi n.  C p phát b nh đ ng.  Gi i phóng b nh . 4) Chuỗi ký tự  Bi n kiểu Char.  String hay còn gọi là m ng ký tự.  Các thao tác v i chuỗi ký tự. Dịch giả: Mr. Hung daihung.pham@yahoo.fr 9) Test Program: Ng i Treo Cổ  M t số chỉ d n.  Gi i pháp 1:  Gi i pháp 2:  Ý t ng c i ti n. -1- Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 Bài 1: Modular Programming Mô-đun Lập Trình Trong ch ơng th hai c a bài h C cao hơn. ng d n, b n s khám phá những khái niệm l p trình ngôn ngữ Cho đ n lúc này, các b n cǜng chỉ làm việc trên m t file duy nh t tên là "main.c" Điều này có thể t m ch p nh n trong giai đo n hiện t i vì ch ơng trình c a chúng ta v n còn khá ngắn, nh ng sắp t i ch ơng trình c a chúng ta s ch a hàng ch c, th m chí hàng trăm functions, n u b n đ t t t c chúng trong cùng m t file thì s r t dài ! Cǜng chính vì lí do đó mà ng i ta đư sáng t o ra cái gọi là modular programming. Về m t nguyên tắc thì nghe có v khá ngu ngốc: thay vì đ t t t c các dòng code trong m t file duy nh t (main.c), chúng ta s chia ra thành nhiều file nhỏ hơn. Chú ý: tôi s không đ t instruction system ("PAUSE") vào phía cuối c a main ( ) nữa. Hãy thêm vào n u b n cần nó. Và tôi v n khuyên b n sử d ng IDE Code::Blocks. Vì IDE này đư đ c update để không ph i đ t instruction system ("PAUSE") phía cuối main ( ) nữa. Prototypes Những bài h ng d n tr c, tôi yêu cầu b n đ t các functions tr c main. T i sao v y? T i vì th tự sắp x p có m t tầm quan trọng: Khi b n đ t function tr c main, máy tính s đọc và nh nó. Khi đ c gọi l i trong main, máy tính s bi t ph i ki m l i function đó đâu. Nh ng n u b n đ t sau main, ch ơng trình s không ho t đ ng vì máy tính v n không bi t function đó là gì. Hưy test thử, b n s th y ngay ! Nh ng v n đề này khá b t l i, đúng không? Tôi đồng ý v i b n về điểm này ! Nh ng b n hưy yên tâm, những nhà l p trình tr khắc ph c nó. Dịch giả: Mr. Hung daihung.pham@yahoo.fr -2- c cǜng g p điều t ơng tự và họ đư tìm cách Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 Nh vào những gì tôi sắp chỉ cho b n sau đây, b n s có thể đ t các functions theo b t kì th tự nào b n muốn trong code source. Prototype để báo trước một function Chúng ta bắt đầu việc báo tr prototypes. c cho máy tính những function c a chúng ta bằng cách vi t các Tôi bi t b n đang nghĩ t ngữ mang đ m ch t “high-tech” prototypes này là th gì đó ghê g m lắm nh ng th t ra nó là m t th hoàn toàn ngu ngốc. Hưy cùng xem đo n code đầu tiên c a function dientichHinhChuNhat: C code: double dientichHinhChuNhat (double chieuRong, double chieuDai) { return chieuRong * chieuDai; } Hưy copy l i dòng đầu tiên (double dientichHinhChuNhat...) và chép vào phần đầu c a file source c a b n (sau những dòng #include). Và thêm vào m t d u ch m phẩy cuối cùng. V y là xong ! Bây gi b n có thể đ t function sau main n u b n muốn. Dịch giả: Mr. Hung daihung.pham@yahoo.fr -3- Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 Và đo n code s thay đổi nh sau: C code: #include <stdio.h> #include <stdlib.h> // Doan code sau chinh la prototype cua function dientichHinhChuNhat : double dientichHinhChuNhat (double chieuRong, double chieuDai); int main (int argc, char *argv[ ]) { printf ("Hinh chu nhat voi chieu rong 5 va chieu dai 10 co dien tich la %f\n", dientichHinhChuNhat(5, 10)); printf ("Hinh chu nhat voi chieu rong 2.5 va chieu dai 3.5 co dien tich la %f\n", dientichHinhChuNhat(2.5, 3.5)); printf ("Hinh chu nhat voi chieu rong 4.2 va chieu dai 9.7 co dien tich la %f\n", dientichHinhChuNhat(4.2, 9.7)); return 0; } // function dientichHinhChuNhat bay gio co the dat o bat ki vi tri nao trong code source double dientichHinhChuNhat (double chieuRong, double chieuDai) { return chieuRong * chieuDai; } Và điều thay đổi đây là, dòng prototype đ c thêm vào phần đầu code source. M t prototype th t ra là l i chỉ d n cho máy tính. Nó s thông báo v i máy tính có sự tồn t i c a function (dientichHinhChuNhat) v i những tham số (parameters) cần đ a vào và type giá trị s xu t ra. Nh v y mà máy tính có thể tự sắp x p. Và cǜng nh vào dòng code này, b n không còn đau đầu khi chọn vị trí đ t function nữa. Hưy luôn vi t prototypes c a các functions có trong ch ơng trình. Ch ơng trình c a b n s không hề bị ch m hơn khi sử d ng nhiều function đâu: và b n nên t p m t thói quen tốt kể t bây gi , hưy đ t prototype cho mỗi functions b n vi t. Chắc b n cǜng th y function main không có prototype. Và đây cǜng là function duy nh t không cần prototype, b i vì máy tính đư bi t rõ nó là gì rồi (t t c các ch ơng trình đều dùng đ n mà, nó bắt bu c ph i bi t thôi). Dịch giả: Mr. Hung daihung.pham@yahoo.fr -4- Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 Để cho chính xác hơn, b n cần bi t thêm: dòng code prototype không cần thi t ph i vi t l i tên c a các bi n số cần cho parameter. Máy tính chỉ cần bi t type c a các bi n số đó thôi. Vì v y đơn gi n hơn ta có thể vi t nh sau: C code: double dientichHinhChuNhat (double , double); Và v i 2 cách vi t đó, ch ơng trình đều ch y tốt, nh ng l i ích c a cách vi t đầu tiên là b n có thể copy-paste nhanh chóng và chỉ thêm vào mỗi d u ch m phẩy cuối. Đ NG QUÊN đ t d u ch m phẩy cuối m t prototype. Vì nó giúp cho máy tính có thể nh n ra sự khác nhau giữa prototype và function. N u b n không làm v y, b n s mắc lỗi khi biên dịch ch ơng trình. Headers Cho đ n lúc này, b n cǜng chỉ sử d ng duy nh t m t file source cho project c a b n. Và tôi yêu cầu b n gọi file source này là main.c Cách sử d ng nhiều files trong cùng m t project Trong thực t , ch ơng trình s không đ c vi t h t toàn b trong mỗi file main.c Chắc chắn là chúng ta cǜng có thể làm v y nh ng việc mò m m trong m t file ch a 10000 dòng code th t sự không thi t thực chút nào. Chính vì v y, thông th ng, m t project s đ c t o b i nhiều files. Nh ng mà project là gì v y? Không ph i v y ch , b n quên nó rồi à ? Dịch giả: Mr. Hung daihung.pham@yahoo.fr -5- Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 Ok, không sao, tôi s gi i thích l i cho b n, việc chúng ta thống nh t chung về khái niệm th t sự cần thi t. Project là t p h p những files source c a ch ơng trình. Trong th i điểm hiện t i, ch ơng trình c a chúng ta chỉ ch a mỗi m t file duy nh t. Hưy nhìn vào IDE, phía bên trái: B n th y trong hình ch p phía trên, bên trái, project này chỉ ch a duy nh t mỗi file main.c Sau đây, tôi s cho b n xem hình nh m t ch ơng trình th t sự, ch ơng trình mà b n s thực hiện trong những bài sau: trò chơi Sokoban B n th y đ y, có khá nhiều files khác nhau. M t ch ơng trình bình th b n s th y nhiều files đ c liệt kê c t bên trái. ng s giống nh trên: B n cǜng tìm th y file main.c: bên trong ch a function main. Và hầu nh mọi ch ơng trình tôi vi t, tôi đều để function main trong file main.c (điều này không bắt bu c, mỗi ng i có cách sắp x p khác nhau, nh ng theo tôi tốt nh t b n nên thực hiện giống tôi điểm này). Dịch giả: Mr. Hung daihung.pham@yahoo.fr -6- Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 Nh ng t i sao ph i t o ra nhiều files nh v y? V y thì tôi có thể t o tối đa bao nhiêu file cho mỗi project ? Điều đó tùy thu c vào b n. Bình th ng, ng i ta th ng hay sắp x p những function có cùng ch đề vào chung v i nhau. Trong hình v trên, trong file editor.c tôi t p h p những functions liên quan đ n việc thay đổi c p đ c a trò chơi, trong file game.c, tôi t p h p những functions liên quan đ n trò chơi,... Các files .h và .c B n th y trong hình v trên, có 2 lo i file khác nhau:   những file .h: gọi là file headers. Những file này ch a prototype c a các functions. những file .c: là những file source. Những file này ch a n i dung c a các functions. Bình th ng, ng i ta r t ít khi để những prototypes trong các file .c giống nh v a rồi chúng ta đư làm trong file main.c (chỉ tr khi ch ơng trình đó quá nhỏ). Mỗi file .c t ơng ng v i m t file .h trong đó ch a những prototype c a những functions. Xem l i hình trên m t lần nữa:    Có file editor.c (ch a C code c a các functions) và file editor.h (ch a prototype các functions đó) T ơng tự nh v y ta có các file game.c và file game.h ... Làm th nào máy tính có thể bi t đ file .c ? c các prototypes nằm m t file khác ngoài Ta thêm file .h vào ch ơng trình nh vào m t chỉ thị tiền xử lý (preprocessor directive). T p trung nhé, b n cần chuẩn bị tinh thần để hiểu bi t thêm khá nhiều th đ y ! Làm sao thêm vào m t file header ?... B n bi t cách mà, b n đư làm r t nhiều lần rồi nh không? Dịch giả: Mr. Hung daihung.pham@yahoo.fr -7- Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 Chúng ta hưy xem ví d đo n đầu c a file jeu.c tôi vi t: C code: #include <stdio.h> #include <stdlib.h> #include "game.h" void play (SDL_Surface* screen) { //.... Và bây gi b n đư bi t cách thêm vào là sử d ng các chỉ thị tiền xử lý (preprocessor directive) #include. Bây gi , chú ý những dòng đầu tiên c a đo n code trên: C code: #include <stdio.h> #include <stdlib.h> #include "game.h" // them vao file game.h Tôi thêm vào 3 files .h: stdio, stdlib và game. Có một sự khác biệt ở đây: Những file mà b n để chung trong folder project ph i đ c vi t trong ngo c kép "..." ("game.h") và những file th viện (đư đ c cài đ t tr c, bình th ng nằm trong folder IDE c a b n) ph i đ c vi t trong ngo c nhọn <...> (<stdio.h>). Tóm l i, thông th   ng ta sử d ng: Ngo c nhọn < > để thêm vào m t file th viện tìm th y trong folder c a IDE Ngo c kép " " để thêm vào m t file tìm th y trong folder project c a b n ( bên c nh những file .c) Dịch giả: Mr. Hung daihung.pham@yahoo.fr -8- Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 Lệnh #include yêu cầu thêm vào n i dung c a file .h vào file .c. Giống nh b n yêu cầu "Hãy thêm vào đây nội dung của file game.h" V y n i dung c a file game.h là gì? Chúng ta s tìm th y trong đó những prototype c a các functions nằm trong file game.c ! C code: /* game.h ----Noi dung : prototypes of functions in game. */ void play (SDL_Surface* screen); void placingPlay (int card[ ][NB_BLOCK_HEIGHT], SDL_Rect *post, int direction); void placingFund (int *firstCase, int *secondCase); Và đó là cách m t project th t sự ho t đ ng ! V y l i ích c a việc đ t các prototypes vào file .h là gì ? Lí do cǜng khá đơn gi n. Khi code source b n vi t yêu cầu gọi m t function, máy tính c a b n bắt bu c ph i bi t tr c function đó là gì, nó cần bao nhiêu tham số (parameter),... Vì th ta cần đ n prototype: giống nh m t b ng h ng d n sử d ng tr c khi dùng function cho máy tính. Câu hỏi trên t ơng ng v i câu hỏi về th tự ch y ch ơng trình: n u b n đ t các prototypes trong những file .h (headers) #include phần đầu những file .c , máy tính c a b n s hiểu đ c cách sử d ng t t c các function kể t giai đo n bắt đầu ch y ch ơng trình. Và điều đó giúp b n ít b n tâm hơn về th tự đ t các function trong các files .c. Hiện gi , b n chỉ vi t m t số ch ơng trình ch a kho ng hai ho c ba functions, b n v n ch a th y rõ ích l i c a những prototypes nh ng về sau, khi b n đư có thể vi t nhiều functions hơn rồi, n u b n không đ t các prototypes trong những file .h, b n s th ng xuyên g p lỗi trong việc dịch ch ơng trình. Dịch giả: Mr. Hung daihung.pham@yahoo.fr -9- Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 N u b n gọi m t function (đ c vi t trong file functions.c) t file main.c thì b n cần ph i thêm các prototypes c a functions.c trong file main.c. Bằng cách t o m t #include "functions.h" đầu main.c B n cần nh : c mỗi lần b n gọi m t function X trong m t file, b n cần ph i thêm các prototypes c a function này vào file đó. Điều này s giúp trình biên dịch kiểm tra l i xem b n có gọi đúng cách hay không. V y làm cách nào tôi có thể thêm vào project những file .h và .c ? Điều này ph thu c vào IDE b n sử d ng nh ng về tổng quát thì qui trình này t ơng tự nhau: File/New/ Empty file Việc này s t o m t file trống. File này v n ch a có d ng .h hay .c, vì th b n cần l u l i để thông báo điều đó. C l u l i (m c dù đó là m t file trống !). Máy tính s hỏi b n tên c a file muốn l u l i là gì và lúc này b n có thể lựa chọn giữa .h và .c :  N u b n đ t tên là [tênfile].c thì nó có d ng .c  N u b n đ t tên là [tênfile].h thì nó có d ng .h Đơn gi n là nh v y L u l i file trong folder ch a những files khác dùng cho project (folder ch a file main.c). Thông th ng, tất cả những file .h và .c sẽ được lưu lại trong cùng một folder. Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 10 - Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 Folder project về sau s t ơng tự nh hình ch p sau, b n th y những file .c và .h đ v i nhau: c đ t chung File b n v a t o đư đ c l u l i nh ng nó v n ch a đ c thêm vào project ! Để thêm m t file vào project, nh n chu t ph i vào c t bên trái c a IDE (nơi b n tìm th y danh sách những file dùng cho project) và chọn Add files (Xem hình d i). M t cửa sổ hiện ra và yêu cầu b n cần thêm vào project những files nào. Chọn file b n v a t o. Và bây gi file này đư nằm trong project và xu t hiện trong danh sách bên trái. Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 11 - Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 Cách thêm vào những thư viện chuẩn (standard library) B n đư t ng khá thắc mắc v n đề này đúng không ? N u nh ta thêm vào những file stdio.h và stdlib.h, v y thì những file này nằm đâu đó và chúng ta có thể tìm th y chúng ph i không? Chính xác là v y đó ! Thông th ng nó đư đ c cài đ t chung v i IDE c a b n. IDE tôi sử d ng là Code::Blocks, những files đó nằm đây: C:\Program Files\CodeBlocks\MinGW\include Và bình th ng chúng nằm trong folder include. Và cǜng trong folder đó b n s tìm th y khá nhiều files khác. Chúng là những headers (.h) c a các th viện chuẩn (standard libraries), những th viện này đều đư đ c vi t s n (trong Windows, Mac, Linux...), và t i đây b n cǜng tìm th y file stdio.h và stdlib.h. M chúng ra mà xem n u b n muốn nh ng có thể b n s th y hơi choáng đ y, chúng r t ph c t p, có quá nhiều th b n v n ch a bi t. N u b n chú ý, thì chúng ch a đầy những prototypes c a các functions standard, l y ví d nh printf. Ok, bây gi tôi đư bi t cách tìm th y những prototypes c a các functions standard. Nh ng làm cách nào tôi có thể xem những function đó vi t nh th nào? Những file .c này nằm đâu ? B n không thể có đ c chúng đâu vì chúng đư đ c dịch và ch a trong folder lib (vi t tắt c a library có nghĩa là th viện). Trong máy tính c a tôi, chúng nằm trong folder: C:\Program Files\CodeBlocks\MinGW\lib Những file th viện đư đ c dịch có đuôi là .a n u sử d ng Code::Blocks (đ c biên dịch b i compiler mingw) và có đuôi là .lib n u dùng Visual C++(dịch b i compiler Visual). Đ ng cố gắng m ra để đọc chúng, vì chúng không dành cho ng i bình th ng nh chúng ta. Tóm tắt l i, trong những file .c, b n cần ph i thêm những file .h c a các th viện chuẩn để có thể dùng những function standard nh printf. Và nh đó máy tính c a b n có đ c b n h ng d n sử d ng tr c khi dùng và có thể kiểm tra xem b n có gọi function đúng cách hay không, ví d b n quên m t vài parameters dùng cho function X. Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 12 - Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 Phân tích quá trình Compilation (separate compilation) B n đư bi t m t project đ c c u thành b i nhiều files source, chúng ta s tìm hiểu sâu hơn về cách ho t đ ng c a quá trình biên dịch (compilation). Các b n đư đ c xem qua tr c đây m t biểu đồ khá đơn gi n về compilation, để hiểu chi ti t hơn các b n xem hình v phía d i, biểu đồ m i này các b n nên hiểu rõ và ph i học thu c lòng đ y! Hình vẽ minh họa quá trình Compilation Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 13 - Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 Tôi s phân tích biểu đồ trên cho b n hiểu rõ  Tiền xử lý (preprocessor): là m t ch ơng trình khởi động trước khi compilation. Nhiệm v c a nó là thực hiện những instructions đ c biệt mà chúng ta đ a vào trong những chỉ thị tiền xử lý, là những dòng bắt đầu bằng dấu #. Trong th i điểm hiện t i, những chỉ thị tiền xử lý duy nh t mà b n bi t đó là #include, cho phép thêm file khác vào file hiện t i. Những ch ơng trình tiền xử lý còn làm đ c nhiều điều khác mà ta học những bài sau nh ng #include v n là cái quan trọng nh t cần bi t. Ch ơng trình tiền xử lý s thay th những dòng #include bằng những file chỉ định. Nó s đ t n i dung c a những file .h ta yêu cầu vào file .c đang dùng. Và ngay lúc này, những file .c đ c hoàn chỉnh, nó ch a t t c những prototypes c a các functions b n dùng ( file .c c a b n lúc này nó s to hơn bình th ng).  Compilation: Giai đo n này r t quan trọng, nó s bi n đổi n i dung trong file source c a b n thành code nhị phân mà máy tính có thể hiểu đ c. Trình biên dịch (Compiler) s lần l t dịch t t c các file .c, quan trọng là những file này đư đ c thêm vào project c a b n (nó ph i xu t hiện trong c t danh sách bên trái IDE mà tôi đư gi i thiệu trên). Compiler s t o ra những file .o (ho c .obj, điều này ph thu c vào lo i compiler) tùy theo t ng file .c. Đây là các files nhị phân tồn t i t m th i, và thông th ng chúng s bị xóa đi cuối quá trình biên dịch, b n có thể tùy chỉnh l i IDE để l u l i chúng. L i ích c a việc l u l i là, l y ví d b n muốn dịch l i 1 trong 10 files .c có trong project thì b n chỉ cần dịch l i mỗi file đó. Những file khác đư có những file .o đư đ c dịch t tr c.  Link editor (linker): là m t ch ơng trình tổng h p l i những file .o thành m t file l n cuối cùng: executable. Những file executable có đuôi .exe d i Windows. (Có đuôi khác n u b n sử d ng m t hệ điều hành khác). B n bi t chúng ho t đ ng nh th nào rồi đó ! Tôi v n xin nhắc l i, biểu đồ này r t quan trọng. Nó t o nên sự khác biệt giữa m t ng i l p trình chuyên chép l i code mà không hiểu n i dung v i m t ng i l p trình hiểu và bi t họ đang làm gì. Phần l n lỗi x y ra trong quá trình compilation, nh ng đôi khi có những những lỗi x y ra đo n linker. Nghĩa là linker không tổng h p đ c những file .o Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 14 - giai Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 Hình v trên v n còn thi u đôi chút, những file th viện đâu ? Điều gì s x y ra n u nh ta có thêm những th viện ? Trong tr ng h p này, giai đo n đầu biểu đồ không thay đổi, nh ng giai đo n linker thì máy tính ph i làm việc nhiều hơn m t tí. Nó ph i tổng h p l i các file. (t m th i) v i các th viện mà chúng ta cần (.a .ou .lib tùy theo compiler). Xem hình sau: Và đây là biểu đồ hoàn chỉnh Những file .o và . a (ho c .lib) đ c tổng h p l i thành file exécutable. Và đó là cách mà chúng ta có đ c m t ch ơng trình hoàn chỉnh 100%, ch a t t c những instructions cần thi t cho máy tính, kể c các instruction yêu cầu hiển thị m t tin nhắn ra màn hình, ví d nh printf, ch a trong file .a cǜng đ c tổng h p vào trong file executable. Trong ch ơng 3, các b n s đ c học cách sử d ng những th viện đồ họa (Graphics library).Nó cǜng có đuôi .a và ch a những instruction yêu cầu máy tính hiển thị m t cửa sổ chẳng h n. Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 15 - Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 Phạm vi sử dụng (scope) của functions và biến số Tr c khi k t thúc bài h ng d n, chúng ta s tìm hiểu khái niệm về ph m vi sử d ng c a functions và bi n số. Chúng ta s xem khi nào những bi n số và function có thể sử d ng, có nghĩa là chúng ta có thể gọi đ c chúng. Các biến số riêng của functions Khi b n khai báo m t bi n số trong m t function, nó s bị xóa khi function k t thúc: C code: int triple (int soHang) { int ketqua= 0; // Bi n số ketqua đ c l u l i trong b nh ketqua= 3 * soHang; return ketqua; } // Function k t thúc, bi n số ketqua bị xóa khỏi b nh M t bi n số đ c khai báo trong function chỉ tồn t i khi function đó đang đ c sử d ng. Điều này có nghĩa nh th nào? Chúng ta không thể dùng nó cho m t function khác! C code: int triple (int soHang); int main (int argc, char *argv[ ]) { printf ("triple cua 15 la %d\n", triple (15)); printf ("triple cua 15 la %d\n", ketqua); // Error - Loi return 0; } int triple (int soHang) { int ketqua= 0; ketqua = 3 * soHang; return ketqua; } Trong main, tôi thử sử d ng bi n số ketqua. Bi n số ketqua này đ triple, nó không sử d ng đ c trong function main ! c khai báo trong function Nắm vững: m t bi n số khai báo trong function nào thì chỉ có thể dùng bên trong function đó. Ng i ta gọi đó là biến cục bộ (local variable). Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 16 - Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 Các global variables (Biến toàn cục): cần tránh sử dụng Global variable có thể được sử dụng trong tất cả các files Chúng ta có thể khai báo những bi n số dùng chung cho t t c các functions ch a trong t t c các file c a project. Tôi s chỉ cho các b n cách t o ra nó, nh ng cần tránh sử d ng. Việc sử d ng nó có thể giúp b n đơn gi n hóa code source lúc ban đầu nh ng về sau khi b n có m t số l ng l n các bi n số, nó s dễ dàng khi n b n nhầm l n và gây ra lỗi không đáng có. Để có thể khai báo m t bi n số ấ global » sử d ng chung cho t t c , chúng ta chỉ cần khai báo ngoài những functions. B n khai báo chúng đo n đầu c a file, sau những dòng #include. C code: #include <stdio.h> #include <stdlib.h> int ketqua = 0; // khai báo m t bi n số global void triple (int soHang); //prototype cua function int main (int argc, char *argv[ ]) { triple (15); // ta goi function triple, no se thay doi gia tri cua bien so ketqua printf ("triple cua 15 la %d\n", ketqua); // Hien thi gia tri cua ketqua return 0; } void triple (int soHang) { ketqua = 3 * soHang; } Trong ví d này, function triple không tr về giá trị nào (void). Function triple thay đổi giá trị bi n số global ketqua và function main có thể l u l i giá trị đó. Bi n số ketqua có thể s đ c sử d ng cho t t c các file trong project, nó có thể đ c gọi l i trong T T C các functions có trong ch ơng trình. D ng bi n số này cần tránh sử d ng. Cách tốt nh t là sử d ng return mỗi khi muốn function tr về m t giá trị. Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 17 - Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 Global variable sử dụng riêng cho một file Bi n số global mà chúng ta v a th y sử d ng đ c cho t t c các file trong project. Chúng ta có thể t o ra những bi n số dùng riêng cho file ch a nó. Bi n số này có thể đ c sử d ng cho các functions xu t hiện trong file đó, không dùng đ c chung cho t t c các functions có trong ch ơng trình. Để t o m t bi n số nh v y, ta chỉ đơn gi n thêm vào t khóa static C code: static int ketqua = 0; phía tr c: Static Variable trong một function Chú ý: có đôi chút khó hiểu đây. Trong m t function, n u b n thêm t khóa static tr c dòng khai báo bi n số, bi n số đó s không bị xóa đi khi function k t thúc, giá trị c a bi n số đó v n đ c giữ l i. Và lần gọi function sau, biến số sẽ giữ lại giá trị đó. Có sự khác biệt v i những bi n số global. Ví d : C code: int triple (int soHang) { static int ketqua = 0; // Bien so ketqua duoc tao ra lan dau khi function duoc goi ketqua = 3 * soHang; return ketqua; } // Bien so ketqua khong bi xoa di khi function ket thuc V y điều này có ý nghĩa nh th nào ? Ng i ta có thể gọi l i bi n số trong những lần sau và bi n số ketqua v n giữ nguyên giá trị. Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 18 - Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 Xem thêm ví d sau để hiểu rõ hơn: C code: int increase( ); int main (int argc, char *argv[ ]) { printf ("%d\n", increase ( )); printf ("%d\n", increase ( )); printf ("%d\n", increase ( )); printf ("%d\n", increase ( )); return 0; } int increase ( ) { static int soHang = 0; soHang++; return soHang; } Console 1 2 3 4 Khi ta gọi function increase, bi n số soHang đ function k t thúc nó không bị xóa đi. c t o ra v i giá trị 0. Nó đ c tăng lên 1, và khi Khi function này đ c gọi l i lần nữa, dòng khai báo bi n số bị bỏ qua. Máy tính s sử d ng ti p t c v i bi n số soHang đ c t o ra tr c đó. Giá trị bi n số soHang tr Dịch giả: Mr. Hung daihung.pham@yahoo.fr c đó là 1, bây gi thành 2, rồi thành 3, thành 4... - 19 - Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 Các local functions dùng riêng cho một file Để k t thúc phần này, tôi s chỉ b n về ph m vi sử d ng c a các functions. Bình th ng, khi b n t o m t function, nó s đ c dùng chung cho toàn b ch ơng trình. Nghĩa là nó cǜng có thể đ c sử d ng cho b t kì file .c nào khác. Nh ng n u b n cần t o m t function chỉ dùng riêng cho mỗi file ch a nó. B n chỉ cần thêm vào t khóa static tr c function đó: C code: static int triple (int soHang) { //instructions } Hưy nghĩ đ n việc c p nh t prototype cho function này nhé. C code: static int triple (int soHang); Bây gi , function static triple chỉ có thể gọi b i m t function khác nằm chung trong file ch a nó. N u b n thử gọi function triple b i m t function khác ch a trong file khác, s không ho t đ ng. Tóm tắt l i những ph m vi sử d ng có thể có c a các bi n số:     M t bi n số khai báo trong m t function s bị xóa đi khi function k t thúc, nó chỉ được sử dụng riêng cho function này. M t bi n số khai báo trong m t function v i t khóa static phía tr c s không bị xóa khi function k t thúc, nó sẽ lưu lại giá trị và cập nhật dọc theo chương trình. M t bi n số khai báo bên ngoài các functions là m t bi n số global, có thể sử dụng cho tất cả các functions của tất cả các file source có trong project. M t bi n số global v i t khóa static riêng cho file chứa nó, không dùng đ phía tr c là bi n số global chỉ được sử dụng c b i các function vi t các file khác. T ơng tự, đây là các ph m vi sử d ng có thể có c a các function:   Một function mặc định có thể sử dụng chung cho tất cả các files trong project, nó có thể gọi ra t b t c vị trí nào trong các file khác. N u ta muốn m t function dùng riêng cho mỗi file chứa nó, bắt bu c ph i thêm vào t khóa static phía tr Dịch giả: Mr. Hung daihung.pham@yahoo.fr c. - 20 - Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 Bài 2: Pointer Con trỏ Đư đ n lúc chúng ta tìm hiểu về con trỏ. Hưy ra hít m t hơi th t sâu tr c khi bắt đầu vì tôi bi t bài học này chắc chắn s không khi n b n th y thú vị. Nh ng con trỏ là m t khái niệm đ c sử d ng r t th ng xuyên trong C. Nói về tầm quan trọng, chúng ta không thể nào l p trình trên ngôn ngữ C mà không dùng đ n con trỏ, và b n cǜng đư t ng dùng nó mà không bi t. Phần l n những ng i bắt đầu học C th ng xuyên v p ngư trong phần ki n th c về con trỏ. Và tôi hi vọng bài học này s giúp các b n không nằm trong số đó. Hưy t p trung g p đôi bình th ng và bỏ thêm th i gian để hiểu rõ t ng biểu đồ, ví d có trong bài học này. Một vấn đề nan giải Đây là m t trong những v n đề l n liên quan đ n con trỏ, các b n m i bắt đầu th l n, c m th y khó khăn trong việc nắm vững cách ho t đ ng và sử d ng. "Con trỏ r t cần thi t, và chúng ta s th ng bị nhầm ng xuyên dùng đ n nó, hưy tin tôi !" Tôi s cho b n xem m t ví d mà các b n không thể nào gi i quy t đ c n u không sử d ng đ n con trỏ. Đây cǜng là tiêu điểm c a bài học này, tôi s h ng d n cách gi i quy t cuối bài học. Đây là v n đề: Tôi muốn vi t m t function tr về hai giá trị. Việc này là không thể vì mỗi function chỉ có thể tr về duy nh t m t giá trị. C code: int function ( ) { return giatri; } N u ta khai báo function v i type int, thì ta s nh n đ c m t số d ng int (nh vào instruction return). Chúng ta cǜng đư học cách vi t m t function không tr về b t c giá trị nào v i t khóa void: C code: void function( ) { } Nh ng để nh n đ c hai giá trị tr về cùng lúc th t sự là việc không thể. Chúng ta không thể sử d ng hai return cùng lúc. Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 21 - Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 Gi sử tôi muốn vi t m t function, trong parameter tôi s cho nó m t giá trị tính bằng phút, tôi muốn nó chuyển thành gi và phút t ơng ng: 1. N u ta đ a vào giá trị 45, function s tr về 0 gi và 45 phút. 2. N u ta đ a vào giá trị 60, function s tr về 1 gi và 0 phút. 3. N u ta đ a vào giá trị 60, function s tr về 1 gi và 30 phút . Nhìn có v khá đơn gi n, nh ng hưy cùng test đo n code sau: C code: #include <stdio.h> #include <stdlib.h> /* Toi dat cac prototypes tren cung. Vi day la mot chuong trình kha nho nen toi khong dat chung trong mot file.h, nhung trong mot chuong trinh that su, toi se dat chung trong mot file.h nhu da huong dan o bai truoc*/ void chuyenDoi(int gio, int phut); int main (int argc, char *argv[ ]) { int gio = 0, phut = 90; /*Chung ta co bien so "phut" giá trị 90. Sau khi ket thuc function, toi muon bien so "gio" nhan gia tri 1 và bien so "phut" nhan gia tri 30 */ chuyenDoi(gio, phut); printf ("%d gio va %d phut", gio, phut); return 0; } void chuyenDoi(int gio, int phut) { gio= phut/ 60; // 90 / 60 = 1 phut= phut% 60; // 90 % 60 = 30 } Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 22 - Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 Và đây là k t qu : Console 0 gio va 90 phut c... ch ơng trình đư không ho t đ ng. Vì sao v y? Khi b n gửi giá trị c a m t bi n số vào vị trí parameter c a m t function, m t b n sao c a bi n số này đ c t o ra. Nói cách khác, bi n số "gio" trong function chuyenDoi không ph i là bi n số "gio" trong function main! Nó chỉ là b n sao! Function chuyenDoi đư thực hiện nhiệm v c a nó. Trong function chuyenDoi, những bi n số "gio" và "phut" nh n giá trị chính xác: 1 và 30. Nh ng sau đó, function k t thúc khi d u ngo c } đóng l i. Nh ta đư học bài học tr c, t t c những bi n số t o ra trong m t function s bị xóa đi khi function đó k t thúc. Và đây, bi n số gio và phut đư bị xóa đi. Sau đó ch ơng trình ti p t c phần ti p theo c a main, và đó bi n số gio và phut c a main giá trị v n là 0 và 90. Đó là lí do b n th t b i! Cần ghi thêm đây, function t o ra m t b n sao cho bi n số ta gửi vào nó, nên b n không cần ph i gọi tên bi n số đó chính xác giống nh cách b n gọi main. Để rõ ràng hơn, b n xem đo n code sau: void chuyenDoi (int g, int p) (g thay cho gio và p thay cho phút) Và ti p theo, hưy thử tìm nhiều cách khác sửa đổi ch ơng trình trên, nh tr về m t giá trị sau khi k t thúc function (sử d ng return và thay đổi type function thành int), b n chỉ nh n đ c m t trong hai giá trị b n cần. B n không thể nào nh n đ c cùng lúc hai giá trị. Và b n tuyệt đối không đ c sử d ng bi n số global, lí do tôi đư gi i thích bài tr c. Và đó là v n đề khó khăn đ t ra Dịch giả: Mr. Hung daihung.pham@yahoo.fr , v y con trỏ s gi i quy t v n đề trên nh th nào? - 23 - Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 Địa chỉ trong bộ nhớ Nhắc lại kiến thức B n còn nh bài học về những bi n số không? Dù có hay không, tôi v n khuy n khích b n xem l i phần đầu c a bài học, phần "Công việc c a b nh ". Trong đó có m t biểu đồ khá quan trọng mà tôi cần nhắc l i tr m i: c khi d y b n những ki n th c Cách sắp xếp trong bộ nhớ (RAM) Đó là cách trình bày m t RAM trong máy tính c a b n. Hưy đọc kĩ t ng dòng trong biểu đồ. Dòng đầu tiên t ơng ng v i "ô" đầu tiên c a b nh (RAM). Mỗi ô t ơng ng v i m t số, là địa chỉ c a nó (address)! B nh ch a m t số l ng l n địa chỉ, bắt đầu t địa chỉ 0 đ n m t số nào đó (m t số vô cùng l n, số l ng địa chỉ ph thu c vào dung l ng b nh đ c lắp đ t trong t ng máy tính). Mỗi địa chỉ có thể ch a m t số. M t và chỉ m t. Ta không thể nào ch a 2 số trong cùng m t địa chỉ. B nh c a b n t o ra chỉ để ch a những con số. Nó không thể ch a chữ cái cǜng nh đo n văn. Để gi i quy t v n đề này, ng i ta t o ra những b ng mư ch a trong đó số và chữ cái t ơng ng. Ví d "Số 89 t ơng ng v i chữ cái Y". V n đề này s đ c gi i thích rõ hơn gi , chúng ta chỉ t p trung vào cách ho t đ ng c a b nh . Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 24 - bài học sau. Bây Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 Địa chỉ và giá trị Khi b n t o ra m t bi n số tuoi type int, l y ví d : C code: int tuoi = 10; ... ch ơng trình c a b n s yêu cầu hệ điều hành (ví d là Windows) quyền sử d ng m t ít b nh . Hệ điều hành s tr l i bằng cách đ a ra địa chỉ b nh đ c phép ch a con số b n cần. Đây cǜng là m t trong những nhiệm v chính c a hệ điều hành: Khi chúng ta yêu cầu m n b nh cho ch ơng trình. Máy tính giống nh ông ch , nó điều hành t ng ch ơng trình và kiểm tra xem chúng có quyền sử d ng b nh t i vị trí đ c c p hay không. Và đây là m t trong những nguyên nhân khi n máy tính b n bị đơ: N u ch ơng trình đ t nhiên ho t đ ng trên m t vùng b nh không cho phép. Hệ điều hành (OS) s t chối và d ng ngay ch ơng trình, giống nh nói v i b n "Mày nghĩ ai là ch đây?" Ng i dùng, s nhìn th y m t cửa sổ hiện lên thông báo d ng "Ch ơng trình bị d ng l i do thực hiện m t công việc không đ c phép". Quay tr l i v i bi n số tuoi. Giá trị 10 đ c đ a vào m t vị trí nào đó trong b nh , l y ví d nó đ c đ a vào địa chỉ 4655. Và điều x y ra đây là (nhiệm v c a compiler), t tuoi trong ch ơng trình s thay th bằng địa chỉ 4655 khi đ c ch y. Việc đó giống nh , mỗi khi b n điền vào tuoi trong code source, chúng s đ c chuyển thành 4655, và máy tính s bi t đ c cần đ n địa chỉ nào trong b nh để l y giá trị . Và ngay sau đó, máy tính xem giá trị đ c ch a trong địa chỉ 4655 và tr l i chúng ta "bi n số tuoi co giá trị là 10"! Và để l y giá trị m t bi n số, đơn gi n chỉ cần đánh tên c a bi n số đó vào code source. N u ta muốn hiển thị tuổi, ta có thể sử d ng function printf: C code: printf ("Bien so tuoi co gia tri la : %d", tuoi); Không có điều gì m i v i dòng code trên đúng ko. Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 25 - Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 Khuyến mãi thêm! B n đư bi t cách hiển thị giá trị c a m t bi n số, nh ng b n có bi t chúng ta cǜng có thể hiển thị địa chỉ c a bi n số đó? ...Đ ơng nhiên là b n ch a bi t rồi Để hiển thị địa chỉ c a m t bi n số, chúng ta cần sử d ng kí hiệu %p (p đây vi t tắt c a t pointer) trong printf. M t khác, chúng ta ph i đ a vào printf địa chỉ c a bi n số đó và để làm việc này, b n cần ph i đ t kí hiệu & tr c bi n số đó (tuoi), giống nh cách tôi h ng d n b n sử d ng scanf, xem code sau: C code: printf ("Dia chi cua bien so tuoi la %p", &tuoi); K t qu Console Dia chi cua bien so tuoi la 0023FF74 Đó là địa chỉ c a bi n số tuoi trong th i điểm ch ơng trình ho t đ ng. Vâng, 0023FF74 là m t số, nó đơn gi n chỉ đ c vi t trên hệ hexadecimal (th p l c phân), thay vì hệ decimal (th p phân) mà chúng ta th ng sử d ng. N u b n thay kí hiệu %p thành %d, b n s nh n đ c m t số th p phân mà b n bi t. N u b n ch y ch ơng trình này trên máy tính c a b n, địa chỉ s khác hoàn toàn. T t c ph thu c vào phần trống có trong b nh , ch ơng trình b n đang dùng,... Hoàn toàn không có kh năng báo tr c địa chỉ nào c a bi n số s đ c c p. N u b n thử ch y ch ơng trình liên t c nhiều lần, địa chỉ có thể s không đổi trong th i điểm đó. Nh ng n u b n kh i đ ng l i máy tính, ch ơng trình chắc chắn s hiển thị m t giá trị khác. V y chúng ta s làm gì v i t t c những th đó? Tôi cần b n n m vững những điều sau:   tuoi: t ng tr ng cho giá trị c a bi n số. &tuoi: t ng tr ng cho địa chỉ c a bi n số. V i tuoi, máy tính s đọc và gửi l i giá trị c a bi n số. V i &tuoi, máy tính s nói v i chúng ta địa chỉ nào s tìm th y bi n số. Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 26 - Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 Cách sử dụng pointers (con trỏ) Đ n bây gi , b n chỉ có thể t o bi n số để ch a các số h ng. Và sau đây chúng ta s học cách t o ra những bi n số ch a địa chỉ c a chúng, những bi n số này gọi là con trỏ. Nh ng... Địa chỉ cǜng là m t số đúng không? Nh v y số này cần ph i ch a trong m t bi n số khác và c nh th nó s l p l i mãi sao? Chính xác. Nh ng các con số này s có m t kí hiệu đ c biệt để nh n bi t: địa chỉ c a m t bi n số khác trong b nh . Cách tạo một con trỏ Để t o m t bi n số d ng con trỏ, ta cần ph i thêm kí tự * tr C code: int *pointer; c tên c a bi n số. B n cần bi t rằng chúng ta cǜng có thể vi t: int* pointer; v n ho t đ ng t ơng tự nh trên. Nh ng cách vi t đầu đ c khuy n khích hơn. B i vì trong tr ng h p b n cần khai báo cùng lúc nhiều con trỏ trong cùng m t dòng, b n bắt bu c ph i đ t * tr c mỗi tên con trỏ: int *pointer1, *pointer2, *pointer3; Giống nh điều tôi d y b n khi khai báo bi n số, b n cần cho nó giá trị ngay khi kh i t o, r t quan trọng, bằng cách cho nó giá trị 0 (l y ví d v i bi n số). Và đối v i con trỏ, điều này còn quan trọng hơn nữa! Để kh i t o con trỏ, có nghĩa là cho nó m t giá trị m c định, ng i ta không dùng giá trị 0 mà dùng t khóa NULL (ph i đ c vi t hoa): C code: int *pointer = NULL; B n đư kh i t o m t con trỏ giá trị NULL. Nh v y, b n chắc rằng con trỏ c a b n không ch a địa chỉ nào. Việc này diễn ra nh th nào? Đo n mư trên s đ t tr c m t chỗ trong b nh , giống nh cách b n t o ra m t bi n số thông th ng. Nh ng điều thay đổi đây là giá trị c a con trỏ chỉ dùng để ch a địa chỉ c a m t bi n số khác. Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 27 - Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 V y thử xem vơi địa chỉ c a tuoi thì sao? Và đây là cách chỉ ra địa chỉ c a m t bi n số (tuoi) dựa trên giá trị c a nó (bằng cách sử d ng kí tự &), nào bắt đầu thôi! C code: int tuoi = 10; int *pointerTuoi = &tuoi; Dòng th nh t : "T o m t bi n số type int có giá trị là 10". Dòng th hai : "T o m t con trỏ có giá trị là địa chỉ c a bi n số tuoi". Dòng th hai thực hiện cùng lúc hai việc. N u b n th y ph c t p nên không muốn g p hai việc v i nhau, tôi s tách biệt chúng bằng cách chia thành hai giai đo n, xem đo n code sau : C code: int tuoi = 10; int *pointerTuoi; // 1) co nghia la "Toi tao mot con tro poiterTuoi" pointerTuoi = &tuoi; // 2) co nghia la "con tro pointerTuoi chua dia chi cua bien so tuoi" B n cần nh rằng không có type "pointer" nh type int hay double. Ng pointer pointerTuoi; i ta không ghi nh sau: Thay vì v y, chúng ta s sử d ng kí tự *, sau int. T i sao l i nh v y? Th t ra, chúng ta cần ph i chỉ rõ type c a bi n số mà con trỏ s ch a địa chỉ c a nó. trên, pointerTuoi s ch a địa chỉ c a bi n số tuoi (type là int), v y con trỏ ph i có type int* N u bi n số tuoi có type là double, ta ph i vi t double *pointerTuoi. Giá trị c a con trỏ pointerTuoi chỉ ra địa chỉ c a bi n số tuoi. Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 28 - Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 Hình sau tóm tắt l i những gì đư diễn ra trong b nh : Trong biểu đồ trên, bi n số tuoi đ c đ t vào ô địa chỉ 177450 (và b n th y t i đó giá trị t ơng ng là 10), và con trỏ pointerTuoi đ c đ t vào ô địa chỉ 3 (t t c địa chỉ đều đ c chọn ng u nhiên, và các địa chỉ trong biểu đồ cǜng do tôi tự vi t ra ). Khi con trỏ đ c t o ra, hệ điều hành s dành m t ô trong b nh giống nh cách nó t o ra v i bi n số tuoi. Khác nhau là giá trị c a pointerTuoi là địa chỉ c a bi n số tuoi. Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 29 - Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 Chúng ta bắt đầu ti n vào th gi i huyền diệu c a những con trỏ. Th gi i bí m t c a những ch ơng trình vi t trên ngôn ngữ C (C++) Ok, nh ng... nó dùng để làm gì ? Hiển nhiên, nó không giúp máy tính b n bi n đổi thành máy làm ra café. Chỉ là, ta có m t con trỏ pointerTuoi ch a địa chỉ c a bi n số tuoi. Hãy dùng printf xem thử nó ch a gì trong đó: C code: int tuoi = 10; int *pointerTuoi = &tuoi; printf ("%d", pointerTuoi); Console 177450 uhm, th t sự điều này không có gì ng c nhiên lắm. Ng i ta yêu cầu giá trị ch a trong pointerTuoi và đó là địa chỉ c a bi n số tuoi (177450). V y làm sao có đ c giá trị c a bi n số mà pointerTuoi chỉ vào? Chúng ta ph i đ t kí tự * tr c tên c a con trỏ: C code: int tuoi = 10; int *pointerTuoi = &tuoi; printf ("%d", *pointerTuoi); Console 10 Đó, b n làm đ bi n số tuoi. c rồi đ y! Bằng cách đ t kí tự * tr c tên con trỏ, ta nh n đ N u chúng ta sử d ng kí tự & tr c tên c a con trỏ, chúng ta s nh n đ con trỏ (trong tr ng h p này là 3). Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 30 - c giá trị c a c địa chỉ để tìm th y Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 V y tôi đ t đ c điều gì đây? Nãy gi tôi chỉ th y các v n đề càng lúc càng rắc rối thêm. Tôi th y không cần thi t hiển thị giá trị c a bi n số tuoi bằng con trỏ! Tr c đây chúng ta v n có thể hiển thị giá trị c a bi n số mà đâu cần đ n con trỏ. Đây là m t câu hỏi chính đáng (mà b n bắt bu c ph i đ t ra cho b n thân). Sau t t c những điều rắc rối b n v a đ c học, hiển nhiên là b n muốn bi t tác d ng c a nó, nh ng t i th i điểm này, tôi khó có thể gi i thích h t, qua các bài học sau, t ng chút m t, các b n s th y nó không đơn gi n đ c t o ra chỉ để làm mọi th ph c t p thêm. Xin b n hưy bỏ qua m t bên cái c m giác khó chịu tôi t o ra cho b n ("T t c mọi th để làm những việc này thôi sao?" ). N u b n hiểu đ các bài học sau. c nguyên lý ho t đ ng, thì chắc chắn, những thắc mắc s đ trên chỉ c sáng tỏ trong Những điều cần nắm vững Đây là những điều mà b n cần hiểu và nắm vững tr  Đối v i m t bi n số, l y ví d bi n số tuoi: o o  c khi ti p t c bài học: tuoi có nghĩa là: "Tôi muốn giá trị c a bi n số tuoi", &tuoi có nghĩa là: "Tôi muốn địa chỉ để tìm th y bi n số tuoi"; Đối v i m t con trỏ, l y ví d con trỏ pointerTuoi: o o pointerTuoi có nghĩa là: "Tôi muốn giá trị c a con trỏ pointerTuoi" (giá trị này là địa chỉ c a m t bi n), *pointerTuoi có nghĩa là: "Tôi muốn giá trị c a bi n số mà con trỏ pointerTuoi chỉ vào". Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 31 - Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 Để có thể hiểu đ c 4 điểm chính trên. B n cần test nhiều lần để hiểu cách nó ho t đ ng. Biểu đồ sau đây giúp b n có thể hiểu rõ hơn: Chú ý không nhầm l n các ý nghĩa c a kí tự * Khi b n khai báo m t con trỏ, * có tác d ng chỉ ra b n muốn t o ra m t con trỏ: int *pointerTuoi; Còn trong printf: printf ("%d",*pointerTuoi); điều này không ph i "tôi muốn t o m t con trỏ" mà là "tôi muốn giá trị c a bi n số mà con trỏ chỉ vào". Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 32 - Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 T t c những điều trên là cơ b n. B n ph i học thu c lòng và t t nhiên ph i hiểu rõ. Đ ng ng i đọc đi đọc l i nhiều lần. Đ ng x u hổ n u không hiểu ngay đ c bài học khi chỉ đọc qua lần đầu tiên, có nhiều v n đề chúng ta cần nhiều ngày để có thể hiểu rõ và đôi khi cần nhiều tháng để có thể sử d ng thành th o. N u b n có c m giác không theo kịp, thì hưy nghĩ đ n những b c thầy trong việc l p trình: không ai trong số họ có thể hiểu rõ hoàn toàn ho t đ ng c a con trỏ trong lần đầu tiên. N u có m t ng i nh v y tồn t i, b n hưy gi i thiệu v i tôi nhé. Cách sử dụng con trỏ trong một function Điều khá thú vị con trỏ là chúng ta có thể sử d ng chúng trong các function để có thể thay đổi trực ti p giá trị c a bi n số trong b nh ch không ph i m t b n sao nh b n đư th y đo n đầu bài học. V y nó ho t đ ng nh th nào? Có r t nhiều cách th c để sử d ng. Đây là ví d đầu tiên: C code: void triplePointer(int *pointerSoHang); int main (int argc, char *argv[ ]) { int soHang = 5; triplePointer(&soHang); // Ta gui dia chi cua soHang vao function printf ("%d", soHang); /* Ta hien thi bien so soHang. Va function da truc tiep thay doi gia tri cua bien so vi no biet dia chi cua bien so nay */ return 0; } void triplePointer(int *pointerSoHang) { *pointerSoHang *= 3; // Ta x3 gia tri cua so hang duoc dua vao } Console 15 Function triplePointer nh n vào parameter giá trị type int * (đó là m t con trỏ chỉ vào m t bi n số type int). Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 33 - Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 Và đây là những gì diễn ra theo th tự, bắt đầu b i function main: 1. M t bi n số soHang đ c t o ra trong main. Kh i t o v i giá trị 5. 2. Ta gọi function triplePointer. Ta gửi vào parameter địa chỉ c a bi n số. 3. Function triplePointer nh n địa chỉ là giá trị c a pointerSoHang. Và trong funtion triplePointer, ta có m t con trỏ pointerSoHang ch a địa chỉ c a bi n số soHang 4. lúc này, ta có m t con trỏ chỉ lên bi n số soHang, ta đư có thể thay đổi trực ti p giá trị c a bi n số soHang trong b nh ! Chỉ cần dùng *pointerSoHang để điều chỉnh giá trị c a bi n số soHang! ví d trên, ng i ta chỉ đơn gi n thực hiện: nhân 3 lần giá trị c a bi n số soHang. 5. k t thúc bằng return trong function main, lúc này soHang đư có giá trị 15 vì function triplePointer đư trực ti p thay đổi giá trị c a nó. T t nhiên, tôi có thể thực hiện return để tr về giá trị nh cách chúng ta đư học trong bài học về function. Nh ng điều thú vị đây là, bằng cách sử d ng con trỏ, chúng ta có thể thay đổi giá trị c a nhiều bi n số trong b nh (có nghĩa là "chúng ta có thể tr về nhiều giá trị"). Không còn gi i h n m t giá trị duy nh t đ c tr về nữa ! V y return còn giá trị sử d ng gì khi ng giá trị ? i ta đư có thể dùng con trỏ để thay đổi Điều này ph thu c vào b n và ch ơng trình b n vi t. Chúng ta cần hiểu là cách dùng return để tr về giá trị là m t cách vi t khá đẹp và đ c sử d ng th ng xuyên trong C. Và th ng xuyên nh t, ng i ta dùng return để thông báo lỗi c a ch ơng trình: ví d , function tr về 1 (true) n u t t c diễn ra bình th ng, và 0 (false) n u có lỗi trong ch ơng trình. Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 34 - Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 Một cách khác để sử dụng con trỏ trong function Trong những code source mà chúng ta v a th y, không có con trỏ trong function main. Duy nh t chỉ bi n số soHang. Con trỏ duy nh t đ c sử d ng nằm trong function triplePointer (có type int *) B n cần bi t rằng có cách vi t khác cho đo n code v a rồi bằng cách thêm vào con trỏ trong function main: C code: void triplePointer(int *pointerSoHang); int main (int argc, char *argv[ ]) { int soHang = 5; int *pointer = &soHang; // con tro nhan dia chi cua bien so soHang triplePointer (pointer); // Ta dua con tro (dia chi cua soHang) vao function printf ("%d", *pointer); // Ta hien thi gia tri cua soHang voi *pointer return 0; } void triplePointer(int *pointerSoHang) { *pointerSoHang *= 3; // Ta x3 gia tri cua soHang } Hưy so sánh đo n code source này v i đo n code source tr chúng s cho ta cùng m t k t qu : c đó. Có m t số thay đổi nh ng Console 15 Điều cần xét đ n là cách đ a địa chỉ c a bi n số soHang vào function, cách sử d ng địa chỉ c a bi n số soHang. Điều khác biệt x y ra đây là cách t o con trỏ trong function main. VD trong printf, tôi muốn hiển thị giá trị c a bi n số soHang bằng cách vi t *pointer. B n cần bi t rằng tôi v n thể vi t soHang: k t qu s giống nhau vì*pointer và soHang đều có chung m t giá trị trong b nh Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 35 - Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 Trong ch ơng trình "L n hơn hay nhỏ hơn", chúng ta đư sử d ng con trỏ b t ch p việc bi t nó là gì, trong việc sử d ng function scanf. Th t ra, function này có tác d ng đọc những thông tin mà ng l i k t qu . i dùng nh p vào bàn phím và gửi Để scanf có thể thay đổi trực ti p giá trị c a m t bi n số bằng cách nh p t bàn phím, ta cần địa chỉ c a bi n số đó: C code: int soHang = 0; scanf ("%d", &soHang); function làm việc v i con trỏ c a bi n số soHang và có thể thay đổi trực ti p giá trị c a soHang. Và nh chúng ta bi t, chúng ta có thể làm nh sau: C code: int soHang = 0; int *pointer = &soHang; scanf ("%d", pointer); Chú ý là ta không đ t kí tự & tr c pointer trong function scanf T i đây, pointer b n thân nó đư là địa chỉ c a bi n số soHang, không cần thi t ph i thêm & vào nữa ! N u b n làm điều đó, b n s đ a cho scanf địa chỉ c a pointer: nh ng th chúng ta cần là địa chỉ c a soHang. Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 36 - Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 Giải quyết vấn đề nan giải ở đầu bài? Đư đ n lúc chúng ta xem l i tâm điểm c a bài học. N u b n hiểu bài học này, b n đư có thể tự gi i quy t v n đề đ t ra. Hưy thử đi! tr c khi xem k t qu tôi đ a b n: C code: #include <stdio.h> #include <stdlib.h> void chuyenDoi(int *pointerGio, int *pointerPhut); int main (int argc, char *argv[ ]) { int gio = 0, phut = 90; // Ta dua vao dia chi cua gio va phut chuyenDoi(&gio, &phut); // Luc nay, gia tri cua chung da duoc thay doi ! printf ("%d gio va %d phut", gio, phut); return 0; } void chuyenDoi(int *pointerGio, int *pointerPhut) { /*Note: dung quen dat dau * o phia truoc ten cua con tro! Bang cach nay ban co the thay doi gia tri cua bien so chu khong phai dia chi c a no! Han la ban khong muon chia dia chi cua no dung khong? */ *pointerGio = *pointerPhut / 60; *pointerPhut = *pointerPhut % 60; } Console: 1 gio va 30 phut Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 37 - Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 Không có gì khi n b n ng c nhiên trong đo n code source này. Và nh mọi khi, để tránh những nhầm l n không đáng có, tôi s gi i thích những gì đư diễn ra để chắc chắn rằng các b n theo kịp tôi, vì đây là m t bài học quan trọng, b n cần cố gắng r t nhiều để hiểu, và tôi cǜng cố gắng h t s c để gi i thích rõ ràng giúp các b n hiểu: 1. Bi n số gio và phut đ c kh i t o trong function main. 2. Ta gửi vào function chuyenDoi địa chỉ c a gio và phut. 3. Function chuyenDoi nh n địa chỉ bằng cách đ a vào các con trỏ pointerGio và pointerPhut. B n cần bi t rằng, cách gọi tên con trỏ không quan trọng. Tôi có thể gọi là g và p, ho c cǜng có thể là gio và phut. 4. function chuyenDoi thay đổi trực ti p các giá trị c a gio và phut trong b nh vì nó đư có địa chỉ c a chúng trong các con trỏ. Và điều cần bi t đây, tuyệt đối ch p hành, là ph i đ t * tr c tên c a con trỏ n u nh ta muốn thay đổi giá trị c a gio và phut. N u ta không làm việc này, ta s thay đổi địa chỉ ch a trong con trỏ, và nó chẳng giúp ta đ c gì. Các b n cần l u ý là chúng ta v n có thể gi i quy t "v n đề" này không qua cách sử d ng con trỏ. Điều này là chắc chắn nh ng điều đó s phá vỡ lu t chúng ta đư đ t ra: không sử d ng những bi n số global. Ho c sử d ng printf trong function chuyenDoi (nh ng ta cần m t printf trong function main). M c đích chính c a ch ơng trình là giúp b n có đ c h ng thú v i việc sử d ng con trỏ. Và b n hãy cố gắng thúc đẩy những h ng thú này ngày m t nhiều hơn trong các bài học ti p theo. Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 38 - Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 Bài 3 Array Mảng Bài học này là phần ti p theo c a bài tr c, nó giúp b n hiểu thêm về cách sử d ng các pointer. B n c m th y có chút khó khăn trong việc sử d ng các pointers? B n s không tránh khỏi việc dùng chúng đâu! Các pointers đ tôi đư nói v i b n điều này! c sử d ng th ng xuyên trong C, Trong bài học này, chúng ta s học cách t o những bi n số type "array" hay còn gọi là m ng. Các m ng đ c sử d ng th ng xuyên trong C vì nó tiện l i trong việc sắp x p m t chuỗi các giá trị. Bài học đ c bắt đầu bằng vài l i gi i thích về cách ho t đ ng c a các arrays trong b nh . Tôi nh n th y việc m đầu v i các ki n th c trong phần b nh vô cùng quan trọng: Nó giúp b n hiểu đ c ph ơng th c ho t đ ng. M t l p trình viên hiểu đ c điều họ làm, điều này s đ m b o ch ơng trình vi t ra ch y ổn định hơn, b n nghĩ sao? Các arrays trong bộ nhớ “Arrays là một dãy các biến số cùng type, chứa trong một vùng bộ nhớ liên tục.” L i gi i thích trên có v giống trong t điển ph i không? Rõ ràng hơn, m ng có thể ch a m t số l Mỗi m ng có m t kích th theo tùy chọn c a b n. Dịch giả: Mr. Hung daihung.pham@yahoo.fr ng l n bi n số cùng type (long, int, char, double...). c xác định. Nó có thể t o b i 2, 3, 10, 150, 2500 cases (ô, slots), tùy - 39 - Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 Biểu đồ sau là m t m ng kích th c 4 ô trong b nh , nó bắt đầu t địa chỉ 1600 : Khi b n yêu cầu t o m t m ng kích th c 4 ô trong b nh , ch ơng trình s yêu cầu hệ điều hành quyền sử d ng 4 ô b nh . 4 ô này ph i nằm kề nhau, có nghĩa là ô sau s k ti p ô tr c. Giống nh trên, các địa chỉ nằm nối ti p nhau: 1600, 1601, 1602, 1603 và không có "kho ng trống" nào giữa. Cuối cùng, mỗi ô trong m ng ch a m t số cùng type. N u m ng có type int, thì mỗi ô trong m ng ch a m t số type int. Không thể t o m ng cùng lúc ch a giá trị type int và double. Tóm l i, sau đây là những điều bu c ph i ghi nh :   Khi m t m ng (array) đ c t o ra, nó sử d ng một vùng liên tục trong bộ nhớ: đó các ô b nh s nằm liên t c kề nhau. T t c các ô (case) trong m ng phải cùng type. M t array type int chỉ ch a các số d ng int, không thể ch a các số d ng khác. Cách tạo một mảng (array) Bắt đầu, chúng ta s xem làm th nào để t o m t m ng ch a 4 giá trị int: C code: int array[4] ; V y thôi, ta chỉ cần thêm vào trong d u ngo c vuông [ ] số l ng ô mà b n muốn ch a trong m ng. Không có gi i h n (tùy theo dung l ng b nh c a máy tính). Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 40 - Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 V y bây gi , làm cách nào đ a giá trị vào mỗi case trong m ng? R t đơn gi n, chỉ cần vi t array[số_th _tự_c a_case] Chú ý: m t m ng bắt đầu t số 0! M ng ch a 4 giá trị int có các ô v i số th tự 0, 1, 2, và 3. Không có ô số 4 trong array 4 cases! Các b n hay nhầm l n đây, nh kĩ. N u tôi muốn thêm vào m ng các giá trị giống nh trong biểu đồ trên, tôi s vi t: C code: int array[4] ; array[0] = 10; array[1] = 23; array[2] = 505; array[3] = 8; V y mối quan hệ giữa m ng và pointer là gì ? N u b n chỉ vi t là array thì đó chính là pointer. Đó là m t pointer chỉ vào ô đầu tiên c a m ng. Test : C code: int array[4] ; printf ("%d", array); K t qu , ta nh n đ c ô địa chỉ đầu tiên c a m ng: Console: 1600 N u b n ghi th tự c a ô trong m ng vào ngo c vuông [ ], b n s nh n đ c giá trị c a ô đó: C code: int array[4]; printf ("%d", array[0]); Console: 10 Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 41 - Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 T ơng tự v i các ô khác. Nhắc l i là n u b n vi t array, nó s là m t pointer, chúng ta có thể sử d ng kí tự * để có đ c giá trị c a ô đầu tiên: C code: int array[4]; printf ("%d", *array); Console: 10 Và có thể nh n giá trị c a ô ti p theo v i *(array+1) (địa chỉ c a array+1). C 2 dòng code sau ho t đ ng t ơng tự nhau: C code: array[1] // Cho gia tri cua o thu 2 (o dau tien viet la [0]) *(array+ 1) // Tuong tu: cho gia tri o thu 2 Nói rõ hơn, n u b n vi t array[0], cǜng giống nh b n yêu cầu giá trị tìm th y địa chỉ array + 0 ( ví d là 1600). N u b n vi t array[1], b n s nh n đ c giá trị địa chỉ array + 1 (1601 trong ví d ). Và t ơng tự v i những tr ng h p còn l i. Dynamic array(mảng động) Ngôn ngữ C tồn t i r t nhiều versions. Version tr c đây, gọi là C99, cho phép t o các dynamic array, có nghĩa là m ng v i kích th c đ c khai báo b i m t bi n số: C code: int kichThuoc = 5; int array[kichThuoc]; Cách vi t này không đ c thông d ng lắm đối v i các compiler, nhiều khi ch ơng trình ch y đ n dòng th 2 s d ng l i. T đầu đ n gi , tôi h ng d n b n ngôn ngữ C89 nên chúng ta s tuyệt đối không dùng dòng code th 2 V y là, các b n không đ c vi t m t bi n số đ t trong ngo c vuông để khai báo kích th c m t m ng, kể c khi bi n số này là hằng số (constant)! M t m ng ph i có kích th c xác định, có nghĩa là khi khai báo b n phái ghi rõ ràng kích th c c a m ng đó bằng m t con số: C code: int array[5] ; V y t i sao l i c m t o m t m ng v i kích th Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 42 - c ph thu c vào m t bi n số? Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 Tôi b o đ m v i b n: điều này là có thể thực hiện! ngay c trong C89. Nh ng để thực hiện điều này, chúng ta s dùng m t kĩ thu t khác (chắc chắn, đ m b o có thể ch y đ c trên mọi điều kiện) gọi là phân bổ đ ng (dynamic allocation). Chúng ta s đ c học về sau. Liệt kê các giá trị trong mảng (array) Bây gi tôi muốn hiển thị giá trị mỗi ô trong m ng. Tôi có thể sử d ng số l ng printf bằng v i số ô trong m ng. Nh ng tôi ph i vi t nhiều lần printf, điều này th t nhàm chán (hưy t ng t ng đ n tr ng h p m ng ch a 8000 giá trị) Vì v y, tốt hơn là sử d ng vòng l p. Và vòng l p for r t tiện l i trong việc này: C code: int main (int argc, char *argv[ ]) { int array[4], i = 0; array[0] = 10; array[1] = 23; array[2] = 505; array[3] = 8; for (i = 0 ; i < 4 ; i++) { printf ("%d\n", array[i]); } return 0; } Console: 10 23 505 8 Vòng l p s ch y dọc các ô trong m ng nh vào bi n số i (các nhà l p trình th m t bi n số khá thông d ng để ch y dọc m t m ng) ng dùng i, đây là Cách này đ c biệt thông d ng, ta để m t bi n số trong d u [ ]. Những bi n số tuyệt đối c m sử d ng trong việc t o các m ng (để khai báo kích th c), nh ng nó đ c phép sử d ng để "di chuyển" trong m ng, có nghĩa là để hiển thị các giá trị! Trong ví d trên, tôi đ t bi n số i, nó s Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 43 - Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 tăng dần t 0, 1, 2 rồi 3. Nh v y, nó s hiển thị các giá trị c a array[0], array[1], array[2] và array[3]! Cần chú ý là không nên hiển thị giá trị c a array[4]! M t array 4 ô chỉ có số th tự là 0, 1, 2, 3. N u b n thử hiển thị giá trị c a array[4], nó s hiển thị m t số không xác định, đây s là m t lỗi khá đẹp. Hệ điều hành s d ng ch ơng trình l i do nó cố ý xâm nh p vào m t địa chỉ không cho phép. Khởi tạo các giá trị trong một mảng B n đư bi t cách di chuyển trong m ng, điều này có nghĩa b n có thể kh i t o m ng v i giá trị 0 t t c các ô bằng việc sử d ng vòng l p! B n đư đ t đ c trình đ cần thi t có thể thực hiện điều này : C code: int main (int argc, char *argv[ ]) { int array[4], i = 0; // Khoi tao cac gia tri trong array for (i = 0 ; i < 4 ; i++) { array[i] = 0; } // Hien thi cac gia tri de kiem tra for (i = 0 ; i < 4 ; i++) { printf ("%d\n", array[i]); } return 0; } Console: 0 0 0 0 Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 44 - Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 Một cách khác để khởi tạo giá trị B n cần bi t là còn m t cách khác để kh i t o giá trị trong m ng khá th công trong C. Bằng cách vi t t ng giá trị trong ngo c nhọn { }, cách nhau b i dầu phẩy “,” : array[4] = { giaTri1, giaTri2, giaTri3, giaTri4}; C code: int main (int argc, char *argv[ ]) { int array[4] = {0, 0, 0, 0}, i = 0; for (i = 0 ; i < 4 ; i++) { printf ("%d\n", array[i]); } return 0; } Console: 0 0 0 0 M t khác, l i ích c a cách làm này là, b n chỉ cần khai báo giá trị những ô đầu tiên, những ô còn l i s tự đ ng nh n giá trị 0: C thể, n u tôi vi t: C code: int array[4] = {10, 23}; // Gia tri nhap vao: 10, 23, 0, 0 Ô đầu tiên nh n giá trị 10, ô th 2 nh n giá trị 23, các ô còn l i nh n giá trị 0. V y làm cách nào để khai báo t t c các ô v i giá trị 0? B n chỉ cần khai báo ô đầu tiên giá trị 0, sau đó các ô còn l i cǜng s nh n giá trị 0. C code: int array[4] = {0} // Khai bao array voi tat ca cac o gia tri 0. Kỹ thu t này có thể ho t đ ng v i b t kì kích th nữa) c nào (có thể thực hiện trên array 100 ô và hơn Chú ý, th ng g p lỗi int array[4] = {1}; Dòng code này s thêm vào các giá trị sau: 1, 0, 0, 0. Nhiều b n nghĩ rằng dòng code trên s kh i t o m ng v i t t các giá trị là 1, nh ng không ph i v y, để làm điều này các b n cần sử d ng vòng l p. Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 45 - Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 Tạo một function để liệt kê các giá trị trong mảng B n đư bi t cách hiển thị n i dung c a m t m ng. V y t i sao b n không thử vi t m t ch ơng trình để thực hiện điều này? Nh th b n có thể tìm hiểu cách đ a m t array vào function. Chúng ta s cần gửi hai parameter vào function: đầu tiên là array (địa chỉ c a array) và th hai s là kích th c c a nó! Và function c a b n có kh năng ho t đ ng v i b t kì array nào khác đ a vào. Chúng ta s cần m t bi n số kichThuocArray. B n bi t rằng array có thể xem nh là m t pointer. Vì v y, chúng ta s đ a array vào function giống nh thực hiện v i pointer: C code: // Prototype cua function hien thi void hienThi (int *array, int kichThuocArray); int main (int argc, char *argv[ ]) { int array[4] = {10, 15, 3}; // Chung ta hien thi noi dung cua array hienThi (array, 4); return 0; } void hienThi (int *array, int kichThuocArray) { int i; for (i = 0 ; i < kichThuocArray ; i++) { printf ("%d\n", array[i]); } } Console 10 15 3 0 Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 46 - Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 Function này không khác nhiều so v i function chúng ta đ c học bài tr c. Chúng ta s đ a vào m t parameter pointer type int (array), sau đó là kích th c c a array (r t quan trọng để bi t khi nào vòng l p d ng l i!). N i dung c a array s đ c hiển thị qua function nh vào m t vòng l p. Cần ghi thêm là còn m t cách khác để đ a m ng vào function: C code: void hienThi (int array[ ], int kichThuocArray) ho t đ ng t ơng tự nh trên, việc sử d ng những d u ngo c vuông cho phép ng i đọc code hiểu rõ: function cần m t m ng. Có thể h n ch đ c nhầm l n là function cần m t con trỏ ho c bi n cơ b n nào đó. Tôi th ng sử d ng cách vi t th hai để đ a m ng vào function, các b n nên thực hiện giống tôi. Và trong cách vi t này chúng ta không cần khai báo kích th c trong ngo c vuông. Một vài bài tập thực hành! Tôi lúc nào cǜng có r t nhiều bài t p cho b n luyện t p ! Các b n nên tự vi t các function làm việc v i array. Tôi chỉ đ a đề bài cho các b n thực hành, về code, các b n s tự vi t l y. Hưy đ t câu hỏi, n u có thắc mắc. Bài tập 1 T o m t function tongArray để tính tổng các giá trị ch a trong nó (sử d ng return để tr về giá trị). Và để giúp b n hiểu rõ hơn, đây là prototype c a function cần vi t: C code: int tongArray (int array[ ], int kichthuocArray); Bài tập 2 T o m t function trungBinhArray để tính trung bình các giá trị ch a trong nó. Prototype: C code: double trungBinhArray (int array[ ], int kichThuocArray); Bài tập 3 T o m t function copyArray để chép n i dung array này sang m t array khác. Prototype: C code: void copyArray (int array1[ ], int array2[ ], int kichThuoc); Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 47 - Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 Bài tập 4 Vi t m t function maximumArray có nhiệm v so sánh t t c các giá trị ch a bên trong array v i giaTriMax. N u có giá trị l n hơn bi n số giaTriMax đ a vào, nó s chuyển thành 0. Prototype: C code: void maximumArray (int array[ ], int kichThuoc, int giaTriMax); VD: array {1,5,7,8,5,2,3} và max=5, s chuyển thành {1,5,0,0,5,2,3}. Bài tập 5 Bài t p này khó hơn hẳn các bài t p kia nh ng b n hoàn toàn có kh năng thực hiện. Hưy vi t m t function sapXepArray sắp x p l i các giá trị bên trong theo th tự tăng dần. C code: void sapXepArray (int array[ ], int kichThuoc); VD: array {1,5,7,8,5,2,3} s chuyển thành {1,2,3,5,5,7,8}. Hãy vi t trong file array.c ch a t t c các functions cần thi t và file array.h ch a các prototypes c a các functions đó. Nào bắt đầu làm việc thôi ! N u b n qua đ c bài học về pointer thì các bài khác các b n đều có thể v nghĩ là bài học này s gây khó khăn cho b n. t qua. Tôi không B n nên nh hai điều quan trọng sau:   Đừng bao giờ quên m t array bắt đầu bằng số th tự 0 (không ph i 1) Khi b n đ a m t array vào function, luôn gửi kèm theo kích th c c a array. Và tôi s cho b n m t tin vui, bạn đã có đủ trình độ để có thể làm việc với các chuỗi kí tự. B n có thể đ a m t chuỗi kí tự vào b nh , ví d nh việc yêu cầu họ tên ng i dùng. Và trong bài học sau chúng ta s học cách làm việc v i các chuỗi kí tự ! Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 48 - Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 Bài 4: String Chuỗi (mảng ký tự) String - Chuỗi (mảng ký tự) là m t thu t ngữ tin học chính xác dùng để chỉ một dãy các ký tự, đơn gi n là nh v y! M t chuỗi ký tự đ c l u trong b nh máy tính d i d ng bi n số. Nh v y ta có thể l u trữ tên c a ng i dùng. Và nh b n đư bi t, máy tính chỉ có thể nh đ gì. V y làm th nào máy tính có thể nh đ c những con số. Máy tính không hiểu chữ cái là c những dãy ký tự? Biến kiểu char: Trong phần này, chúng ta s đ c biệt quan tâm đ n bi n kiểu char. B n có nh rằng bi n kiểu char cho phép ch a các con số trong kho ng -128 và 127. Liệu bi n kiểu char có cho phép ch a những con số? B n cần bi t rằng trong C ng i ta rất hiếm khi sử d ng chúng để làm điều đó. Bình th ng, ngay c đối v i những con số th t sự nhỏ, ng i ta v n dùng int để l u l i. Hẳn rằng tôi đư sử d ng nhiều b nh hơn so v i char nh ng trong th i đ i ngày nay, v n đề b nh không còn đáng lo nữa. Th t ra bi n kiểu char đ c t o ra để ch a … m t ký tự! Chú ý là tôi nói rõ ràng « m t ký tự ». B nh máy tính chỉ có thể ch a những con số nên ng i ta đư t o ra m t b ng chuyển đổi giữa số và ký tự. L y ví d con số 65 s đ c chuyển đổi thành chữ cái A. Ngôn ngữ C cho phép chúng ta chuyển đổi dễ dàng giữa số và chữ cái t ơng ng. Để nh n đ c m t số ng v i chữ cái, ng i ta chỉ cần vi t chúng giữa những d u móc đơn, nh sau: ‘A’. Qua quá trình compilation, ‘A’ s đ c thay th bằng con số t ơng ng. Test thử nào: C code: int main (int argc, char *argv[ ]) { char letter = 'A'; printf ("%ld\n", letter); return 0; } Console: 65 Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 49 - Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 Chúng ta th y ngay rằng chữ A vi t hoa đư đ c thay bằng số 65. T ơng tự nh v y B thay bằng 66, C bằng 67… Test thử v i những chữ cái vi t th ng, giá trị c a những chữ cái s thay đổi. Và chữ ‘a’ không giống nh ‘A’, máy tính phân biệt chữ cái vi t hoa và vi t th ng. Hầu h t các chữ cái thông th ng đ c code giữa 0 và 127. B ng chuyển đổi giữa số và chữ cái có tên là ASCII (cách đọc “át-xơ-ki”). Trang web AsciiTable.com là địa chỉ khá nổi ti ng để tìm th y b ng chuyển đổi này nh ng nó không ph i là duy nh t, chúng ta có thể tìm th y nó trên Wikipédia và m t số trang web khác. Cách hiển thị một ký tự: Function printf, s ti p t c làm chúng ta ng c nhiên, nó có kh năng hiển thị m t chữ cái. Để làm điều đó, chúng ta cần ph i sử d ng kí hiệu %c (c vi t tắt c a character): C code: int main (int argc, char *argv[ ]) { char letter = 'A'; printf ("%c\n", letter); return 0; } Console: A Chúc m ng b n đư bi t cách hiển thị m t chữ cái! Chúng ta cǜng có thể yêu cầu ng scanf: i dùng nh p vào m t chữ cái bằng cách sử d ng %c trong C code: int main (int argc, char *argv[ ]) { char letter = 0; scanf ("%c", &letter); printf ("%c\n", letter); return 0; } Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 50 - Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 N u tôi nh p vào đây chữ cái B, máy tính s hiển thị: Console: B B (chữ B th nh t là do tôi nh p t bàn phím và chữ B th 2 do printf hiển thị) Và đó là những gì b n cần bi t về bi n kiểu char.     Cần nh : Type char cho phép nh p vào những gia trị t -128 đ n 127, unsigned char t 0 đ n 255. Có những b ng chuyển đổi cho phép máy tính chuyển t chữ cái thành số và ng c l i. Chúng ta có thể sử d ng type char chể ch a M T chữ cái. ‘A’ đ c ch ơng trình dịch chuyển thành m t số t ơng ng (thông th ng là 65). Chúng ta dùng những d u ngo c đơn ‘…’ để có đ c giá trị c a m t chữ cái. String - Chuỗi ký tự là một mảng các giá trị char! Tiêu đề trên nói ra t t c những gì tôi s nói v i b n đây. M t chuỗi ký tự (string) chỉ là m t m ng các giá trị bi n kiểu char. Đơn gi n là m t m ng, không hơn. N u tôi t o m t m ng: C code: char string[5]; … n u tôi đ t vào string[0] chữ cái ‘H’, string[1] chữ cái ‘e’… tôi s t o đ ho c là m t văn b n (text) Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 51 - c m t dãy ký tự , Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 Đây là biểu đồ về cách b nh máy tính l u trữ (B n cần chú ý kĩ phần này vì phần sau bài học s ph c t p hơn r t nhiều): Giống nh b n th y, đó là m t b ng ch a 5 ô b nh để trình bày chữ ấ Hello ». Để có đ c giá trị số, tôi đ t chúng vào giữa những d u ngo c đơn, để nói v i máy tính rằng đó là m t số ch không ph i ký tự. Và trong thực t , trong b nh máy tính s l u l i giá trị số t ơng ng v i những chữ cái đ c l u. Vâng, nh ng b n cần l u ý, m t dãy ký tự không chỉ ch a các chữ cái! Biểu đồ trên ch a hoàn chỉnh. M t dãy ký tự bắt bu c ph i ch a m t ký tự đ c biệt Ký tự đó đ c vi t là ‘\0’. cuối cùng, gọi là « ký tự k t thúc chuỗi ». T i sao ta cần ph i k t thúc chuỗi bằng m t ‘\0’ ? Đơn gi n là vì máy tính cần bi t khi nào k t thúc m t chuỗi ký tự. Ký tự ‘\0’ cho phép gi i thích: « D ng l i, không còn gì để đọc ti p đây nữa! ». Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 52 - Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 Qua đó, để l u t « Hello » (gồm 5 chữ cái) vào b nh , ta cần dùng m t string 6 char ch không ph i là m t string 5 char! Mỗi khi b n t o m t chuỗi ký tự, b n cần ph i nghĩ đ n tr đây là điều bắt bu c! c đó vị trí dự phòng để đ t ký tự ‘\0’, Lỗi th ng g p trong l p trình ngôn ngữ C là việc b n quên đi ký tự này, chính tôi cǜng đư mắc lỗi này khá là nhiều lần Để hiểu rõ hơn, đây chính là biểu đồ chính xác về t « Hello » ch a trong b nh : B n th y đ y, chuỗi ký tự chi m 6 ô trong b nh ch không ph i 5. Chuỗi ký tự s k t thúc bằng ‘\0’, ký tự k t thúc chuỗi cho phép máy tính bi t rằng chuỗi ký tự s k t thúc đây. Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 53 - Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 B n s th y rằng ký tự ‘\0’ s là m t l i th cho chúng ta. Nh nó mà b n có thể bi t đ c đ dài c a chuỗi ký tự, vì nó nằm vị trí k t thúc c a chuỗi. B n có thể đ a chuỗi ký tự vào m t function mà không cần ph i thêm vào m t bi n số chỉ đ dài c a chuỗi. Việc này chỉ có tác d ng đối v i những chuỗi ký tự ( có nghĩa là v i type char*, ho c char[ ]). Đối v i những d ng m ng khác, các b n bắt bu c ph i l u l i đ l n c a chuỗi đâu đó. Khai báo và kh i t o chuỗi ký tự N u ta muốn khai báo m t t « Hello », ta có thể sử d ng ph ơng pháp buồn chán sau: C code: char string[6]; // mang string gom 6 char de luu tru H-e-l-l-o va \0 string[0] = 'H'; string[1] = 'e'; string[2] = 'l'; string[3] = 'l'; string[4] = 'o'; string[5] = '\0'; Ph ơng pháp này ho t đ ng, b n có thể kiểm tra l i bằng cách sử d ng printf. Ah, tôi quên m t string). printf: b n cần ph i bi t thêm môt ký tự đ c biệt khác đó là %s (s nh là m t Và đây là đo n code hoàn chỉnh t o và hiển thị t « Hello »: C code: #include <stdio.h> #include <stdlib.h> int main (int argc, char *argv[ ]) { char string[6]; // mang string gom 6 char de luu tru H-e-l-l-o và \0 // Khoi tao chuoi ky tu (Ta viet tung ky tu vao bo nho) string[0] = 'H'; string[1] = 'e'; string[2] = 'l'; string[3] = 'l'; string[4] = 'o'; string[5] = '\0'; // Hien thi chuoi ky tu bang printf nho %s printf ("%s", string); return 0; } Console: Hello Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 54 - Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 T t c những dòng code trên chỉ để t o và hiển thị mỗi t « Hello ». Khá là mệt mỏii khi ph i l p đi l p l i việc khai báo riêng biệt t ng chữ cái trong m ng string. Để kh i t o m t chuỗi ký tự, may mắn thay ta còn m t ph ơng pháp khác đơn gi n hơn: C code: int main (int argc, char *argv[ ]) { char string[ ] = "Hello"; // Do dai cua chuoi ky tu duoc may tinh tu dong tinh toan. printf ("%s", string); return 0; } Console: Hello dòng đầu tiên trong main, tôi t o m t bi n số d ng char[ ]. Tôi cǜng có thể vi t là char*, k t qu s v n nh nhau. Sau đó b n s điền trong ngo c kép t b n muốn l u l i, b dịch c a C s tính toán tự đ ng đ dài cần thi t. Có nghĩa là nó s đ m số chữ cái và thêm vào ký tự ‘\0’. Sau đó nó s điền t ng chữ cái c a t « Hello » vào b nh giống nh cách đầu tiên chúng ta làm trên. Tóm l i, đơn gi n và dễ sử d ng. Khuy t điểm: Cách này chỉ ho t đ ng khi kh i t o chuỗi, b n s không thể sử d ng ti p phần sau c a ch ơng trình, b n không thể vi t: những C code: string = "Hello"; Sau phần kh i t o, b n chỉ có thể l u m t t bằng cách vi t riêng biệt t ng chữ cái vào b nh . Cách lưu trữ một chữ cái bằng scanf: Các b n có thể nh ng i sử d ng nh p vào m t t bằng scanf, bằng cách sử d ng ti p ký tự %s. V n đề duy nh t x y ra, b n s không bi t có bao nhiêu chữ cái ng i dùng s nh p vào. N u b n yêu cầu nh p tên, có thể ng i dùng chỉ nh p vào Nhu (3 chữ cái), và đôi lúc ng i đó s nh p vào Superman (nhiều chữ cái hơn). Để làm điều này, 36 k c a tôn tử là không đ cho b n. Đôi khi, chúng ta cần khai báo m t m ng char l n hơn, đ l n để có thể ch a tên c a ng i dùng. Chúng ta s t o m t char[100] để ch a tên ng i dùng. Việc này khi n b n có c m giác chúng ta đang lưng phí b nh máy tính, nh ng v n đề về b nh này không th t sự đáng để l u tâm (s có những ch ơng trình làm lưng phí b nh máy tính hơn r t nhiều lần nh th này, t t b n s th y). Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 55 - Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 C code: int main (int argc, char *argv[ ]) { char ten[100]; printf ("E ku, may ten gi vay? "); scanf ("%s", ten); printf ("Hello %s, tao rat vui vi duoc gap may!", ten); return 0; } Console: E ku, may ten gi vay? M0N1M Hello M0N1M, tao rat vui vi duoc gap may!" Và đ y là phần l n những gì ph i làm để yêu cầu ng i dùng nh p vào m t t . Các thao tác sử dụng trên chuỗi ký tự: Những chuỗi ký tự s đ c sử d ng th ng xuyên. T t c những câu, những t hiển thị trên màn hình máy tính b n th y đều đ c t o b i các array type char trong b nh máy tính, chúng ho t đ ng theo cách tôi v a h ng d n trên. Tôi s gi i thiệu cho b n m t số thao tác để tùy chỉnh những chuỗi ký tự, ng th viện string.h những function cần thi t. Tôi s không h cần thi t. Tôi s h i ta đư t o trong ng d n b n t t c trong bài này, s r t dài và có m t số function th t sự không ng d n b n những cái cần thi t đ dùng trong th i điểm hiện t i. Hãy nh khai báo th viện string.h M c dù điều này là hiển nhiên nh ng tôi muốn b n xác định rõ: khi b n s ph i sử d ng m t th viện m i (string.h), b n cần ph i khai báo đầu file .c mà b n cần dùng đ n: #include <string.h> N u b n không làm việc này, máy tính s không bi t đ c những function mà tôi sắp h ng d n cho b n, vì ch ơng trình không có đ c những prototypes cần thi t, và việc dịch ch ơng trình s không hoàn thành đ c. Tóm l i, b n đ ng quên ph i khai báo th viện này mỗi khi cần dùng đ n những function thao tác trên chuỗi. Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 56 - Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 Strlen: tính độ dài một chuỗi Strlen là m t function dùng để tính toán đ dài m t chuỗi ký tự (không tính ký tự ‘\0’). B n chỉ cần đ a vào nó parameter: chuỗi ký tự c a b n! Function này s tr về đ dài c a nó. Bây gi b n đư bi t prototype là gì vì v y tôi s đ a b n prototype c a function tôi sắp h ng d n. Những ng i l p trình xem prototype giống nh “h ng d n sử d ng tr c khi dùng” c a function (và những dòng chú thích bên c nh s không bao gi th a). Prototype đây: size_t strlen(const char* string); size_t là m t type đ c biệt, nó cho bi t function s tr về m t số t ơng ng v i m t kích th c nào đó. Đây không ph i là m t type cơ b n nh int, long hay char, đây là m t type đ c “ch ” thêm. Chúng ta s học cách t o ra những type m i những bài học sau. Trong th i điểm hiện t i, chúng ta t m th i hài lòng v i giá trị tr về c a strlen đ c l u l i trong m t bi n số type long (máy tính s tự đ ng chuyển size_t thành long). Để rõ ràng, chính xác, b n cần l u l i k t qu trong m t bi n số type size_t, nh ng thực t thì m t bi n số type long là đ . Function này có parameter là m t type const char. Const (có nghĩa là constant, b n nh ch ?) có tác d ng ngăn không cho strlen thay đổi chuỗi ký tự c a b n. Khi b n th y m t const, b n bi t rằng giá trị c a bi n số đó s không thể thay đổi, chỉ có thể đọc giá trị c a nó. Test thử function strlen: C code: int main (int argc, char *argv[ ]) { char string[ ] = "Xinchao"; long doDaiChuoi = 0; // gia tri do dai cua chuoi se duoc luu lai trong bien so doDaiChuoi doDaiChuoi = strlen (string); // hien thi do dai chuoi printf ("Chuoi %s co do dai %ld ki tu", string, doDaiChuoi); return 0; } Console: Chuoi Xinchao co do dai 7 ki tu Function strlen này đ c vi t dễ dàng bằng cách sử d ng vòng l p trên m ng kiểu char, và nó s d ng l i khi g p ký tự k t thúc chuỗi ‘\0’. Nó s lần l t truy c p vào các ô có ch a t ng ký tự c a chuỗi, số l t truy c p s tăng dần sau mỗi vòng l p, và k t qu s tr về giá trị cuối cùng c a những l t truy c p này. Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 57 - Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 Tôi s vi t function strlen c a riêng mình. Việc này s giúp b n hiểu rõ hơn nó ho t nh th nào: C code: long doDaiChuoi(const char* string); int main (int argc, char *argv[ ]) { char string[ ] = "Hello"; long doDai = 0; doDai = doDaiChuoi(string); printf ("chuoi %s co do dai %ld ki tu", string, doDai); return 0; } long doDaiChuoi (const char* string) { long soLuongKiTu = 0; char kiTuHienTai = 0; do { kiTuHienTai = string[soLuongKiTu]; soLuongKiTu++; } while (kiTuHienTai != '\0'); // Vong lap tiep tuc neu ki tu hien tai khong phai la \0 soLuongKiTu--; // Do dai chuoi giam di 1 vi ta khong tinh \0 return soLuongKiTu; } Giải thích 1. Function doDaiChuoi t o vòng l p trên m ng string. Nó s l u l i t ng ký tự trong bi n kiTuHienTai. Khi kiTuHienTai ti n đ n \0, vòng l p s d ng l i. 2. T i mỗi vòng l p, đ l n s tăng lên 1 sau mỗi lần truy c p ô ch a ký tự. 3. Khi k t thúc vòng l p, số l ng ký tự s b t đi 1. Điều này có nghĩa là ta không tính ký tự k t thúc chuỗi ‘\0’. 4. Cuối cùng, k t qu s đ c tr về soLuongKiTu. 5. Trò chơi k t thúc. Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 58 - Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 Strcpy: sao chép chuỗi này vào chuỗi khác Function strcpy(có thể hiểu là « string copy ») cho phép sao chép m t chuỗi ký tự này đ t vào trong m t chuỗi ký tự khác. Prototype c a nó là: char* strcpy(char* copyString, const char* stringCopy); function này nh n 2 parameter: copyString: là m t pointer char* (m ng char). Chuỗi ký tự s đ c chép vào trong m ng này. stringCopy: là m t pointer c a m t m ng char khác. Chuỗi ký tự này s đ copyString. c dùng để chép vào Function tr về pointer c a copyString cǜng không hữu d ng lắm. Thực t , ta không cần sử d ng k t qu function này tr về. Cùng test thử nhé. C code: int main (int argc, char *argv[ ]) { /* Chung ta khai bao bien "string" kieu char trong do co chua 1 chuoi ky tu va mot bien "copy" voi kich thuoc 100 ky tu de bao dam co du cho trong */ char string[ ] = "Text", copy[100] = {0}; strcpy(copy, string); // Chung ta se sao chep nhung ky tu tu "string" sang "copy" // Neu khong co gi sai sot thi "copy" bay gio se giong nhu "string" printf ("string is : %s\n", string); printf ("copy is : %s\n", copy); return 0; } Console: string is : Text copy is : Text K t qu c a string là « Text ». Điều đó là bình th ng. Nh ng, bi n số copy cǜng có k t qu t ơng tự, ban đầu bi n số này không có giá trị nào, sau đó nó nh n n i dung c a string. V y, string đ c sao chép l i vào trong copy. B n cần chắc rằng đ l n c a chuỗi copy có đ để nh n n i dung c a string. N u trong ví d trên, tôi khai báo copy[4] (s không đ để ch a ký tự \0), function strcpy s v t gi i h n b nh , điều này s làm ch ơng trình b n d ng l i. B n cần tránh điều này. Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 59 - Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 Biểu đồ s nh sau: Mỗi ký tự trong string s đ c điền vào copy. Phần sau c a copy còn nhiều ô nh không dùng đ n, vì trên, tôi khai báo copy có đ dài là 100, nh ng trong ví d này, 5 là đ để sử d ng. L i ích c a việc t o ra m t m ng l n hơn là để có thể sử d ng cho nhiều tr ng h p khác nhau, có thể có m t số chuỗi có đ dài l n hơn trong phần sau c a ch ơng trình. strcat: ghép nối 2 chuỗi function này có tác d ng thêm n i dung m t chuỗi phía sau m t chuỗi khác. Gọi là concatenation. (sự xâu chuỗi ) N u ta có: string1 = "Hello " string2 = "M0N1M" N u tôi nối string2 vào string1, string1 s thành « Hello M0N1M ». Còn string2 s không thay đổi, string2 v n luôn là « M0N1M ». Chỉ mỗi string1 thay đổi. Đó là cách strcat họat đ ng, và đây là prototype c a nó: char* strcat(char* string1, const char* string2); Nh b n th y, string2 không thể thay đổi vì nó đ c a function. c định nghĩa là m t constant trong prototype Function tr về pointer c a string1, giống nh strcpy, không có giá trị sử d ng nhiều nên ta có thể không cần quan tâm đ n k t qu nó tr về. Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 60 - Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 Function thêm vào string1 n i dung c a string2. B n có thể hiểu rõ hơn qua đo n code sau: C code: int main (int argc, char *argv[ ]) { /* Chung ta se tao ra 2 mang ky tu, nho rang string1 phai du lon de chua duoc nhung ky tu cua string2. Neu khong chuong trinh se bao loi. */ char string1[100] = "Hello ", string2[ ] = "M0N1M"; strcat (string1, string2); // Nhung ky tu cua string2 se duoc noi tiep vao string1 // Neu moi thu dien ra tot dep thi ket qua string1 se la "Hello M0N1M" printf ("string1 is : %s\n", string1); // string2 van khong bi thay doi : printf ("string2 is always : %s\n", string2); return 0; } Console: string1 is : Hello M0N1M string2 is always : M0N1M Cần chắc chắn là string1 ph i đ l n để ch a thêm n i dung c a string2, n u không b n s v qua gi i h n b nh cho phép, điều đó s khi n ch ơng trình d ng l i. t Vì v y tôi khai báo string1 v i đ l n là 100. Trong string2, tôi để máy tính tự tính đ l n c a chuỗi (tôi không cần ph i suy nghĩ nhiều về điều này) vì chuỗi này không bị thay đổi. Ta không cần thi t ph i khai báo. Biểu đồ s nh sau: M ng c a string2 s đ c thêm vào phía sau c a string1 (nó s chi m thêm m t vài ô b nh ) Ký tự ‘\0’ c a chuỗi string1 s bị xóa đi (hay đ c thay th bằng M c a M0N1M). Thực t , không thể điền ‘\0’ vào giữa chuỗi n u không nó s cắt chuỗi ra làm 2 phần! ta chỉ đ t ‘\0’ vào vị trí cuối c a chuỗi, khi chuỗi k t thúc. Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 61 - Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 Strcmp: so sánh độ dài 2 chuỗi Function Strcmp dùng để so sánh đ dài 2 chuỗi v i nhau. Và đây là prototype: C code: int strcmp(const char* string1, const char* string2); Những bi n số c a string1 và string2 đ c so sánh v i nhau. Nh b n th y, không có chuỗi nào bị thay đổi vì chúng đ c khai báo nh những constants. K t qu tr về c a function này cần đ c giữ l i. Thực t , strcmp tr về:   Giá trị 0 n u hai chuỗi giống nhau. M t giá trị khác 0 (l n hơn hay nhỏ hơn 0) n u hai chuỗi khác nhau. Về logic, function tr về 1 n u 2 chuỗi bằng nhau để nói là « TRUE » thì h p lí hơn (những boolean). Nh ng function này không do tôi vi t ra… Nói rõ hơn, function s so sánh giá trị c a t ng ký tự c a mỗi chuỗi v i nhau. N u t t c những ký tự giống nhau, nó s tr về 0. N u các ký tự c a string1 l n hơn string2, nó s tr về m t số d ơng. Ng về m t số âm. Trong thực tiễn, ng i ta th c l i, function s tr ng dùng strcmp để so sánh 2 chuỗi n u chúng giống nhau. Đây là đo n mư để test. C code: int main (int argc, char *argv[ ]) { char string1[ ] = "Text for test", string2[ ] = "Text for test"; if (strcmp(string1, string2) == 0) // Neu 2 chuoi giong nhau { printf ("Hai chuoi giong nhau!\n"); } else { printf ("Hai chuoi khac nhau!\n"); } return 0; } Console: Hai chuoi giong nhau! Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 62 - Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 N u hai chuỗi giống nhau, k t qu s tr về 0. L u ý thêm là tôi có thể l u k t qu c a strcmp l i bằng m t bi n số kiểu int. Tuy nhiên chúng ta không bắt bu c ph i làm điều này, b n có thể dùng trực ti p if nh tôi đư làm. Chúng ta s không nói nhiều hơn về function này. Nó khá đơn gi n để sử d ng, và điều b n cần nh đó là giá trị 0 có nghĩa là ấ giống nhau » và m t giá trị khác 0 có nghĩa là ấ khác nhau ». Đây chính là nguyên nhân lỗi th ng xu t hiện khi sử d ng function này. Strchr: tìm kiếm một ký tự Ch c năng c a function strchr là tìm ki m m t ký tự trong chuỗi. Prototype c a function strchr đây: char* strchr(const char* string, int characterSearch); function nh n 2 parameters:   string: chúng ta s tìm ki m ký tự trong bi n này. characterSearch: ký tự b n muốn tìm ki m trong bi n string. B n th y rằng characterSearch là bi n kiểu int ch không ph i char. Điều này cǜng bình th thôi vì về cơ b n, ký tự chính là số. Tuy nhiên, ng i ta th ng ng dùng char hơn là int để ch a m t ký tự trong b nh . Function s tr về pointer c a chữ cái đầu tiên tìm th y, có nghĩa là tr về địa chỉ c a ký tự đó trong b nh . K t qu s tr về NULL n u không tìm th y ký tự b n muốn tìm. Trong ví d sau, tôi s l u pointer này trong subString: C code: int main (int argc, char *argv[ ]) { char string[ ] = "Text for test", *subString = NULL; subString = strchr(string, 'f'); if (subString != NULL) // NULL la ko tim thay, vay dong nay nghia la “neu ta tim dc gi do” { printf ("Bat dau tu ky tu dau tien duoc tim thay, string duoc in ra la : %s \n", subString); } return 0; } Console: Bat dau tu ky tu dau tien duoc tim thay, string duoc in ra la : for test B n hiểu điều gì đư x y ra Dịch giả: Mr. Hung daihung.pham@yahoo.fr đây không? Có m t sự khác biệt nhỏ - 63 - đây . Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 Thực t , subString cǜng là m t pointer nh string. Khác biệt chỗ string chỉ vào chữ cái đầu tiên (T vi t hoa), còn subString chỉ vào chữ ‘f’ đầu tiên đ c tìm th y trong string. Biểu đồ sau chỉ ra cho b n th y nó ho t đ ng nh th nào : String bắt đầu ở ký tự đầu (‘T’ viết hoa) còn subString thì chỉ vào ký tự ‘f’ Khi tôi vi t m t printf lên subString, thông th ng nó s hiển thị k t qu là «for test». Function printf s hiển thị t t c những chữ cái nó g p ('f', 'o', 'r ', ' ' 't', 'e', 's', 't') đ n khi g p ký tự \0 để thông báo là string k t thúc đây. Biến thể: Ngoài ra còn có m t function strrchr v i cách ho t đ ng hoàn toàn giống nh strchr, khác chỗ nó s chỉ vào chữ cái cuối cùng đ c tìm th y trong string thay vì chữ cái đầu tiên. L u ý:   Function strchr tìm ki m và chỉ vào ký tự đầu tiên nó tìm th y trong chuỗi. Fucntion strrchr tìm ki m và chỉ vào ký tự cuối cùng nó tìm th y trong chuỗi. VD: N u b n muốn tìm ký tự ‘r’ trong chuỗi Program:   Khi dùng strchr: hàm s chỉ vào ký tự ‘r’ đầu tiên giữa ký tự ‘P’ và ‘o’. Khi dùng strrchr: hàm s chỉ vào ký tự ‘r’ cuối cùng giữa ký tự ‘g’ và ‘a’. Strpbrk: chữ cái đầu tiên trong danh sách. Function này có cách ho t đ ng khá giống v i function tr c. Nó s tìm m t trong những chữ cái trong danh sách b n cho d i d ng m t dãy ký tự, ng c l i v i strchr chỉ có thể tìm ki m mỗi lần duy nh t m t ký tự. Ví d , n u danh sách tìm ki m đ a vào có d ng « xfs » cho dãy ký tự « Text for test », Function s cho k t qu là m t pointer chỉ vào chữ cái đầu tiên tìm đ c c a m t trong những chữ cái trong danh sách. Nói rõ hơn, chữ cái đầu tiên trong « xfs » nó tìm th y trong « Text for test » là x, nên strpbrk s tr về pointer lên ‘x’. Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 64 - Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 Prototype c a function này là: char* strpbrk (const char* string, const char* charactersSearch); Test thử xem nào: C code: int main (int argc, char *argv[ ]) { char *subString; // Chung ta tim kiem su xuat hien dau tien cua 1 trong 3 ky tu x, f hoac s trong "Text for test" subString = strpbrk("Text for test", "xfs"); if (subString != NULL) { printf ("Sau khi tim mot trong ba ky tu: x, f, s trong "Text for test"\n"); printf ("Bat dau tu ky tu dau tien duoc tim thay.\n"); printf ("Bien string duoc in ra la: %s", subString); } return 0; } Console: Sau khi tim mot trong ba ky tu: x, f, s trong "Text for test" Bat dau tu mot trong nhung ky tu dau tien duoc tim thay. Bien string duoc in ra la: xt for test Trong ví d này, tôi đ a trực ti p các giá trị vào thẳng function (trong ngo c kép). Chúng ta không bị bắt bu c ph i t o m t bi n số, việc vi t trực ti p nh th này khá tiện l i. Các b n ph i l u ý những cách vi t sau: N u các b n sử d ng ngo c kép “ ’’ có nghĩa là chuỗi (m ng ký tự). N u các b n sử d ng móc đơn ‘ ’ có nghĩa là ký tự Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 65 - Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 Strstr: tìm kiếm chuỗi ký tự trong một chuỗi ký tự khác. Function này s giúp b n tìm kiếm chuỗi ký tự đầu tiên tìm th y trong m t chuỗi ký tự khác. Prototype c a nó là: char* strstr(const char* string, const char* stringSearch); Prototype c a function này khá giống v i strpbrk, nh ng chú ý đ ng nhầm l n, strpbrk tìm ki m m t chữ cái trong danh sách, còn strstr tìm ki m nguyên c dãy ký tự Ví d : C code: int main (int argc, char *argv[ ]) { char *subString; // Chung ta se tim kiem chuoi "test" trong "Text for test" : subString = strstr("Text for test", "test"); if (subString != NULL) { printf ("Chuoi ky tu dau tien ma ban muon tim trong chuoi Text for test la: %s\n", subString); } return 0; } Chuỗi ký tự đầu tiên mà b n muốn tìm trong chuỗi Text for test là : test Function strstr tìm ki m dãy ký tự ấ test » trong “Test de test”. Và tr l i k t qu , giống nh những function khác, m t pointer t i vị trí nó tìm th y. K t qu là NULL n u nó không tìm th y gì. Hãy nh kiểm tra tr ng h p function tìm ki m không tr về k t qu NULL. N u b n không làm điều này tr c, khi b n muốn hiển thị m t dãy ký tự có pointer là NULL, ch ơng trình s bị lỗi. Hệ điều hành s d ng ch ơng trình c a b n ngay t c khắc vì nó cố gắng xâm nh p vào m t địa chỉ NULL mà nó không đ c phép. Trong vd trên, tôi hài lòng v i k t qu mà function này đư tr về. Trong thực tiễn, việc này th t sự không cần thiết. B n chỉ cần vi t m t đo n code if (result != NULL) để bi t rằng việc tìm ki m tìm đ c m t th gì đó. Tr ng h p không tìm th y gì, hiển thị thông báo “Khong tim duoc ket qua can tim”. T t c đều tùy theo ch ơng trình b n vi t, nh ng trong mọi tr ng h p, những function này là cơ b n để b n thực hiện các thao tác tìm ki m liên quan t i văn b n. Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 66 - Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 sprintf: viết trong một chuỗi Khác v i các function tr c đó, function này đ c tìm th y trong stdio.h. Tên c a function này khi n b n nh đ n th gì quen thu c. Function này khá giống v i function printf mà b n đư đ c bi t, nh ng thay vì in ra màn hình, sprintf s vi t vào m t dãy ký tự! B n để ý có thể th y tên c a function này bắt đầu bằng “s” c a t “string” Đây là m t function r t tiện l i để t o ra m t chuỗi, sau đây là m t ví d nhỏ: C code: #include <stdio.h> #include <stdlib.h> int main (int argc, char *argv[ ]) { char string[100]; long age = 18; // Chuong trinh se viet "You are 18 years old !" sprintf (string, "You are %ld years old !", age); // Chung ta se in chuoi ky tu nay ra man hinh de dam bao chuong trinh hien thi dung printf ("%s", string); return 0; } Console: You are 18 years old ! Nó đ c sử d ng t ơng tự nh printf, chỉ khác điểm b n ph i thêm vào m t parameter ngay vị tríc đầu tiên, đó là m t pointer chỉ đ n chuỗi b n cần vi t vào. Trong ví d c a tôi, tôi vi t trong chuỗi “You are %ld years old !”, t i đó %ld s đ c thay th bằng giá trị c a bi n số age. T t c các nguyên tắc c a printf đều có đây, b n có thể sử d ng %s để điền vào m t chuỗi ký tự khác vào đ y. Nh th ng lệ, hãy kiểm tra tr s gửi vào. c việc chuỗi c a b n có đ l n để nh n những ký tự mà sprintf N u không, chíuuuuuu…. Bùm Ph i nói là các thao tác trên chuỗi ký tự trong C cần đ c thi hành m t cách r t tỉ mỉ. B n cần bi t rằng tôi cǜng không thể bi t đ c t t c những function có trong string.h. Tôi cǜng không yêu cầu b n ph i học thu c lòng. Nh ng b n cần ph i bi t cách dãy ký tự ho t đ ng v i \0 và những điều trên. Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 67 - Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 Tổng kết !!!  Máy tính không thể làm việc v i các ký tự, nó chỉ bi t đ n những con số. Chúng ta có những con số để gi i quy t v n đề liên quan đ n những ký tự trong b ng chữ cái c a mình bằng b ng mã tên là ASCII.  Mỗi bi n kiểu char chỉ có thể ch a m t và chỉ m t ký tự duy nh t. Chúng th ng đ c l u l i t i m t ô địa chỉ b nh ng u nhiên trên máy tính, máy tính s tự đ ng sắp x p và biên dịch những bi n này.  Để có thể t o ra m t t ho c m t c m t chúng ta ph i t o ra m t chuỗi. Trong tr ng h p này, chúng ta s sử d ng m ng ký tự.  T t c các chuỗi đều đ c k t thúc b i m t ký tự đ c biệt, đó là ký tự k t thúc chuỗi ‘\0’  Có r t nhiều functions dùng để xử lý chuỗi đư đ c vi t s n trong th viện string.h. Vì v y đ ng quên khai báo th viện này đầu ch ơng trình tr c khi b n muốn thao tác v i chuỗi ký tự nhé. B n cần nh rằng ngôn ngữ C có b c “t ơng đối th p”, có nghĩa là các thao tác c a b n r t gần v i cách ho t đ ng c a máy tính. L i ích khác là hiện gi b n đư bi t ph ơng th c ho t đ ng c a máy tính trên chuỗi ký tự. Những ki n th c tôi d y b n đây s phát huy tác d ng trong t ơng lai, tôi có thể chắc đ m b o v i b n. Ng c l i, việc l p trình trên Java hay trên Basic không cần thi t ph i hiểu cách th c ho t đ ng c a máy tính, b n hiện gi đư bắt đầu hiểu làm th nào máy tính ho t đ ng, điều này theo tôi là r t quan trọng. Nghe có v hay đ y nh ng có m t điều đáng ng i là ch ơng này hơi ph c t p. B n ph i dự đoán tr c đ l n c a array, nghĩ đ n l u l i \0… Các thao tác trên dưy ký tự không dễ dàng nắm vững b i ng i m i bắt đầu, ph i cần thêm m t ít thực hành để có thể đ t đ c. Nói về ph ơng diện thực hành m t cách chính xác thì tôi có công việc dành cho b n. Tôi khuy n khích b n cố gắng thực hành th t nhiều. Còn điều gì tốt hơn bằng cách làm việc trên những chuỗi ký tự? N u ch a đ phê, hãy cùng lúc làm việc trên chuỗi ký tự, m ng và pointer… Và đây là việc tôi muốn b n làm: B n v a học m t số functions trong th viện string.h, nh ng b n đư có đ kh năng để tự vi t l i các function cho riêng mình. (?)Nh ng có cần thi t ko? N u các function đư đ c vi t rồi, t i sao ph i phí công vi t l i? Th t sự thì không cần thi t tí nào, và trong t ơng lai chắc chắn b n s sử d ng các function trong string.h ch không ph i những function c a riêng b n. Nh ng việc này s giúp b n luyện t p, tôi th y rằng đây là m t bài t p khá hay. Tôi đư chỉ cho b n cách ho t đ ng c a function strlen, điều đó có thể giúp b n thực hiện các function khác. Nh ng, đ ng cố gắng vi t l i những function nh sprintf, đây là m t function v i đ ph c t p t ơng đối cao. Hãy t m ch p nh n v i các function có trong string.h. N u b n bị kẹt đâu đó, đ ng ng i đ t câu hỏi trên các diễn đàn. Nào, làm việc thôi! Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 68 - Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 Bài 5: Preprocessor Tiền xử lý Sau những bài học về con trỏ (pointer), m ng (array) và chuỗi kí tự (string), chúng ta s t m nghỉ ngơi m t chút. Tôi nghĩ đ n th i điểm này các b n đư có đ c không ít những ki n th c l p trình t các bài học tr c và có l chúng ta nên ng ng l i m t chút cho dễ th . Nh ng điều này không có nghĩa là s không có điều gì m i m để học trong th i gian này. Nhìn chung trong bài này, b n s đ c học m t số th đơn gi n, cǜng nh đ c nhắc l i m t vài ki n th c. Bài này s vi t về Tiền xử lý (Preprocessor), đây là những ch ơng trình đ dịch (Compilation). c ch y tr c khi biên Tôi có m t sơ đồ thể hiện quá trình biên dịch (compile) c a máy tính để t o ra m t ch ơng trình. Chúng ta cùng nhau tham kh o để hiểu rõ hơn nhé: Sơ đồ quá trình biên dịch Có m t điều cần bi t là: n i dung c a bài này s giúp ích cho b n r t nhiều. Và những ki n th c này khá đơn gi n để hiểu… Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 69 - Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 Chỉ thị #include Giống nh tôi t ng gi i thích trong ch ơng đầu tiên, chúng ta tìm th y trong mã nguồn những dòng hơi đ c biệt gọi là chỉ thị tiền xử lý (preprocessor directives). Những preprocessor directives có đ c tính sau: chúng luôn bắt đầu bằng kí tự #. Khá dễ dàng để nh n bi t. Và dòng directive đầu tiên (cǜng là duy nh t) mà chúng ta t ng th y cho đ n th i điểm này đó là #include. Dòng directive này cho phép thêm nội dung một file khác vào file đang viết. Chúng ta đ c biệt dùng #include để thêm vào file.c các n i dung t những file.h c a các th viện (stdio.h, stdlib.h, string.h, math.h) và cǜng có thể là t những file.h c a riêng b n. Để thêm n i dung những file.h có trong th m c cài đ t IDE c a b n, b n cần sử d ng những ngo c nhọn < > : Code C: #include <stdlib.h> Để thêm n i dung những file.h có trong th m c ch a project c a b n, b n cần sử d ng những d u ngo c kép: Code C: #include "myfile.h" C thể hơn, những ch ơng trình tiền xử lý s bắt đầu tr c khi compile. Nó s quét các t p tin c a b n để tìm ra những chỉ thị tiền xử lý tr c, là t t c những dòng bắt đầu bằng #. Khi nó g p directive #include, nó s đ t n i dung c a file đ c chỉ định vào vị trí #include. Gi sử tôi có m t « file.c » ch a code c a các function và « file.h » ch a các prototypes c a các function trong file.c Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 70 - Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 Để đơn gi n hơn b n có thể xem biểu đồ sau : T t c n i dung c a file.h s đ D c đ t vào trong file.c, ngay t i vị trí đ t directive #include file.h i đây là những gì ta có trong file.c: Code C: #include "file.h" long myFunction(int cai_nay, double cai_kia) { /* Source code of function */ } void otherFunction(long value) { /* Source code of function */ } Và những gì có trong file.h : C code: long myFunction(int cai_nay, double cai_kia); void otherFunction(long value); Khi ch ơng trình tiền xử lý ch y đ n đây, tr file.c. Dịch giả: Mr. Hung daihung.pham@yahoo.fr c khi biên dịch file.c, nó s đ t file.h vào trong - 71 - Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C Cuối cùng, mã nguồn c a file.c tr www.siteduzero.com Tác giả: M@teo21 c khi biên dịch s giống nh sau: C Code: long myFunction(int cai_nay, double cai_kia); void otherFunction(long value); long myFunction(int cai_nay, double cai_kia) { /* Source code of function */ } void otherFunction(long value) { /* Source code of function */ } Nội dung của file.h đư đ c đ t t i vị trí c a dòng #include. Th t sự không có gì quá khó hiểu đúng không? Tôi nghĩ rằng đư có khá nhiều b n bi t cách th c ho t đ ng c a nó nh th nào. Ch c năng c a các #include không có gì khác ngoài hành đ ng chèn file này vào file khác, và các b n ph i bi t rằng việc hiểu v n đề này r t quan trọng. Thay vì đ t t t c các prototype vào trong các file.c, chúng ta nên chọn cách đ t những prototypes trong các file.h, việc này gần nh đ c xem là m t nguyên tắc cơ b n khi l p trình. Ta có thể u tiên để những prototypes này lên đầu file.c (th ng g p trong những file ch ơng trình nhỏ), nh ng để tiện cho việc sắp x p, tôi khuyên b n hưy đ t những prototype này trong những file.h. Khi ch ơng trình b n vi t ngày càng ph c t p và có quá nhiều file.c, chỉ cần sử d ng #include để giúp chúng sử d ng chung 1 file.h, b n s c m th y mình th t may mắn khi không ph i tốn quá nhiều th i gian để copy & paste l i những prototype có cùng 1 ch c năng. Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 72 - Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 Chỉ thị #define Chúng ta s tìm hiểu m t preprocessor directives m i: #define. Lệnh directive này cho phép khai báo m t hằng số (constant) c a tiền xử lý (preprocessor). Nó cho phép đ a giá trị số vào m t t . L y ví d : Code C: #define MANG_SONG_NHAN_VAT_BAN_DAU 3 B ns    vi t theo th tự: #define T khóa s đ c nh n giá trị Giá trị Cần chú ý m t chút về cách vi t: tên c a constant này th ng đ để phân biệt v i những constant mà ta đ c học tr c đó: c vi t hoa toàn b , ch y u là Code C: const long MANG_SONG_NHAN_VAT_BAN_DAU = 3; Những constant s chi m m t chỗ trong b nh , ngay c khi giá trị không đổi, số « 3 » s đ c l u trữ đâu đó trong b nh . Không giống nh tr ng h p c a các constant c a preprocessor V y nó ho t đ ng nh th nào? Đó là, #define s thay th t t c những t khóa trong mã nguồn b i giá trị t ơng ng. T ơng tự nh ch c năng ấ find/replace » trong word b n th ng sử d ng. Và dòng: Code C: #define MANG_SONG_NHAN_VAT_BAN_DAU 3 S thay th t t c t MANG_SONG_NHAN_VAT_BAN_DAU có trong file bằng giá trị 3 Và đây là m t ví d : M t file.c tr c khi qua preprocessor : Code C: #define MANG_SONG_NHAN_VAT_BAN_DAU 3 int main (int argc, char *argv[ ]) { long mang_song = MANG_SONG_NHAN_VAT_BAN_DAU; /* Code ...*/ Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 73 - Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 Sau khi qua preprocessor: Code C: int main (int argc, char *argv[ ]) { long mang_song = 3; /* Code ...*/ Tr c khi biên dịch, t t c những #define s đ c thay th bằng các giá trị t ơng ng. Compiler s th y n i dung trong file sau khi qua tiền xử lý, trong đó t t các những chỗ cần thay th đều đư đ c thực hiện V y đ n th i điểm này, đâu là l i ích c a việc sử d ng những constant ? Ok, nh tôi đư gi i thích v i b n là nó không chi m thêm b nh máy tính. Điều này có nghĩa là, đ n giai đo n compilation thì trong đó chỉ còn những con số trong mã nguồn c a b n. M t l i ích khác khi sử d ng #define là việc thay th s đ c thực hiện trong t t c các file ch a dòng #define. Khác v i khi b n khai báo m t constant trong m t function, nó chỉ có hiệu lực trong function đó, sau đó nó s bị xóa đi. Nh ng đối v i #define, nó s tác đ ng lên t t c các function có trong file đó, và điều này th t sự r t tiện l i cho những ng i l p trình. B n muốn m t ví d c thể về việc sử d ng #define? Đây là m t ví d mà b n có thể ng d ng ngay. Khi b n m m t cửa sổ ch ơng trình bằng C, hẳn là b n muốn xác định những constant c a tiền xử lý đề chỉ ra kích th c c a cửa sổ: C Code: #define CHIEUDAI_CUASO 800 #define CHIEURONG_CUASO 600 u điểm c a việc này là n u về sau b n có ý định thay đổi kích th c c a các cửa sổ (chẳng h n nh vì lý do chúng quá nhỏ), b n chỉ cần thay đổi l i những dòng #define và sau đó biên dịch l i. Vi t thêm rằng: những dòng #define th ng đ c đ t trong những file.h, bên c nh các prototype (B n có thể xem những file.h c a các th viện nh stdio.h, b n s th y đó luôn có những dòng #define!). Những #define giống nh ấ cách th c đơn gi n », giúp b n thay đổi kích th c cửa sổ bằng việc thay đổi những #define, thay vì ph i rà soát l i toàn b n i dung file tìm nơi mà b n m cửa sổ để thay đổi l i kích th c. Việc này s ti t kiệm r t nhiều th i gian cho b n trong việc l p trình. Tóm tắt l i, những constants c a preprocessor cho phép « chỉnh sửa » ch ơng trình c a b n tr việc compilation. Giống nh m t d ng mini-configuration. Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 74 - c Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C Define cho kích th Ta th www.siteduzero.com Tác giả: M@teo21 c array ng dùng các define để xác định kích th c các table. Đây là ví d : Code C: #define KICH_THUOC_TOI_DA 1000 int main (int argc, char *argv[ ]) { char line1[KICH_THUOC_TOI_DA], line2[KICH_THUOC_TOI_DA]; // ... Nh ng… tôi nghĩ rằng chúng ta không thể đ t bi n số (KICH_THUOC_TOI_DA) giữa những d u ngo c [ ] khi kh i t o table (line1[ ], line2[ ]) ? Đúng v y, nh ng < KICH_THUOC_TOI_DA > KHÔNG ph i là m t bi n số. Giống nh tôi đư nói, preprocessor s thay đổi n i dung c a file tr c khi compilation, nh sau: C Code: int main (int argc, char *argv[ ]) { char line1[1000], line2[1000]; // ... ...và đo n code này là chính xác. Bằng cách xác định giá trị c a KICH_THUOC_TOI_DA, các b n có thể sử d ng nó để t o những m ng có kích th c khác nhau. N u tr ng h p sau này m ng đang sử d ng không đ l n, b n chỉ cần thay đổi giá trị dòng #define, sau đó compile l i, và m ng d ng char c a b n đư s nh n kích th c m i mà b n v a thay đổi. Tính toán trong define Chúng ta có thể thực hiện m t vài phép tính trong define. Ví d , đo n code sau s t o constant CHIEUDAI_CUASO và CHIEURONG_CUASO, sau đó là SOLUONG_PIXELS ch a số l ng pixel hiển thị trong cửa sổ. (việc tính toán khá đơn gi n: chiều r ng*chiều dài): C Code: #define CHIEUDAI_CUASO 800 #define CHIEUCAO_CUASO 600 #define SOLUONG_PIXELS (CHIEUDAI_CUASO * CHIEUCAO_CUASO) Giá trị c a SOLUONG_PIXELS đ c thay đổi tr c khi compile bằng giá trị c a (CHIEUDAI_CUASO * CHIEUCAO_CUASO), có nghĩa là (800*600), 480000. Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 75 - Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 Hưy luôn đ t phép tính c a b n trong những d u ngo c đơn nh cách tôi đư làm. B n có thể thực hiện t t c các phép toán cơ b n mà b n bi t: phép c ng (+), phép tr (-), phép nhân (*), phép chia (/) và modulo (%). Những constant đ c xác định tr c Ngoài việc b n có thể tự t o ra m t số constant c a riêng b n, có tồn t i m t số constant khác đư đ c xác định tr c trong preprocessor Ngay lúc này đây, khi tôi đang vi t những dòng h ng d n này cho b n, b n cần bi t rằng có m t số constant tôi ch a t ng sử d ng đ n, tôi s gi i thiệu cho b n m t số constant có ích mà tôi bi t. Mỗi constant s bắt đầu và k t thúc b i hai d u g ch d     i_ __LINE__ : vị trí c a dòng code hiện t i __FILE__ : tên c a file hiện t i __DATE__ : ngày tháng hiện t i __TIME__ : th i gian hiện t i khi compile Tôi nghĩ rằng những constant này có ích trong việc khắc ph c l i các lỗi ch ơng trình, l y ví d : C Code: printf ("Co loi o dong %ld trong tap tin %s\n", __LINE__, __FILE__); printf ("Tap tin nay duoc bien dich vao ngay: %s luc: %s\n", __DATE__, __TIME__); Console: Co loi o dong 9 trong tap tin main.c Tap tin nay duoc bien dich vao ngay: 25 April 2015 luc: 15:36:10 Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 76 - Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 Cách xác định đơn gi n Ta cǜng có thể đơn gi n vi t: Code C: #define CONSTANT …mà không cần bi t giá trị c a nó Điều này có tác d ng báo cho preprocessor bi t rằng t CONSTANT đư đ không có giá trị, nh ng nó ấ tồn t i ». c xác định, nó Th t sự tôi không h ng thú lắm về nó, tác d ng c a nó là gì v y? Có l b n v n ch a th y rõ tác d ng c a nó, tôi s chỉ ra cho b n phần sau đây. Macros Chúng ta đư th y rằng v i các #define chúng ta có thể yêu cầu preprocessor thay th m t t khóa bằng m t giá trị. L y ví d : Code C: #define SOLUONG 9 ... có nghĩa rằng t t c những t « SOLUONG » trong code c a b n s đ c thay th bằng giá trị 9. Chúng ta th y rằng nó cǜng t ơng tự nh ch c năng find / replace trong Microsoft Word, trong tr ng h p này thì nó đ c thực hiện b i preprocessor tr c khi compilation. Tôi có m t thông tin m i cho b n! Thực t #define có tác d ng m nh hơn r t nhiều, nó cho phép thay th c … m t đo n code! Khi ta sử d ng #define để find / replace m t t bằng m t đo n code, chúng ta gọi đó là macro. Macro không dùng tham số (parameters) Đây là m t ví d c a macro đơn gi n: Code C: #define CUCKOO( ) printf ("Cuckoo"); Điều thay đổi đây, là những dầu ngo c ( ) đ Chúng ta s xem ngay đây tác d ng c a nó. Dịch giả: Mr. Hung daihung.pham@yahoo.fr c thêm vào sau t khóa ( đây là t CUCKOO). - 77 - Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 Test macro trong code source: C Code: #define CUCKOO( ) printf ("Cuckoo") ; int main (int argc, char *argv[ ]) { CUCKOO( ) return 0 ; } Console: Cuckoo Tôi đồng ý v i b n, rằng đây không ph i cách truyền thống mà chúng ta đư đ c học tr c. Điều b n cần ph i bi t đó là, macro s đ c trực ti p thay th l i trong code source tr compile. các bài c khi Đo n code trên s t ơng tự nh đo n code sau đây trong th i điểm compilation. C Code: int main (int argc, char *argv[ ]) { printf ("Cuckoo"); return 0; } N u b n đư hiểu 2 đo n code trên, b n hầu nh đư nắm bắt đ c nền t ng c a các macro. Nh ng… chúng ta chỉ có thể đ t duy nh t m t đo n code trong mỗi macro thôi sao? Không, th t may mắn là chúng ta có thể đ t nhiều dòng code trong mỗi macro. Chỉ cần đ t m t kí tự \ tr c mỗi dòng m i, t ơng tự nh sau: C Code: #define GIOI_THIEU_BAN_THAN( )printf ("Xin chao, toi ten la Minh\n"); \ printf ("Toi song tai thanh pho HCM\n"); \ printf ("Toi thich nghe nhac\n"); int main (int argc, char *argv[ ]) { GIOI_THIEU_BAN_THAN( ) return 0; } Console: Xin chao, toi ten la Minh Toi song tai thanh pho HCM Toi thich nghe nhac Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 78 - Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 Cần l u ý là trong main, khi gọi m t macro chúng ta không đ t d u ch m phẩy « ; » cuối dòng. Thực t , đây là m t dòng cho preprocessor, chúng ta không cần thi t ph i k t thúc bằng d u ch m phẩy. Macro v i nhiều tham số (parameters) V a rồi chúng ta đư bi t làm cách nào để t o ra m t macro không dùng parameter, có nghĩa là không ghi gì vào trong parameter c a nó. Tác d ng chính c a d ng macro này, là có thể « rút ngắn » m t đo n code dài có kh năng l p l i nhiều lần trong code source c a b n. Trong tình huống nh v y, những macro này càng hiệu qu hơn khi chúng ta đ t vào chúng những parameter. Nó ho t đ ng t ơng tự nh các function. C Code: #define NGUOI_TRUONG_THANH(tuoi) if (tuoi >= 18) \ printf ("Ban la nguoi truong thanh\n"); int main (int argc, char *argv[ ]) { NGUOI_TRUONG_THANH(22) return 0; } Console: Ban la nguoi truong thanh Cần vi t thêm rằng tôi có thể thêm vào đó m t else để hiển thị « Ban van chua truong thanh ». Hãy làm thử để t p luyện, không có chút khó khăn nào c và đ ng quên đ t thêm d u antislash \ tr c mỗi dòng m i Tôi nghĩ b n có thể hiểu ngay cách ho t đ ng c a đo n macro này: C Code: #define NGUOI_TRUONG_THANH(tuoi) if (tuoi >= 18) \ printf ("Ban la nguoi truong thanh\n"); Tôi đ t vào trong d u ngo c ( ) tên c a bi n số ta gọi là "tuoi". Trong t t c đo n code đ c thay th , "tuoi" s đ c thay th bằng m t số khi ta gọi l i macro (trong tr ng h p này là 22). Và đo n code trên s t ơng tự nh sau ngay th i điểm biên dịch ch ơng trình: Code C: int main (int argc, char *argv[ ]) { if (22 >= 18) printf ("Ban la nguoi truong thanh\n"); return 0; } Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 79 - Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 Đo n code source trên đư đ c thay đổi l i, giá trị c a bi n số "tuoi" đư đ Và chúng ta cǜng có thể t o m t macro ch a nhiều parameters: c đ a thẳng vào. C Code: #define NGUOI_TRUONG_THANH(tuoi, ten) if (tuoi >= 18) \ printf ("Ban la nguoi truong thanh %s\n", ten); int main (int argc, char *argv[ ]) { NGUOI_TRUONG_THANH(22, "Minh") return 0; } Đó là phần l n những gì ta có thể bi t về macro. Bình th ng chúng ta không cần thi t ph i sử d ng th ng xuyên các macro. Nh ng m t số tr ng h p, trong các th viện t ơng đối ph c t p nh wxWidgets hay QT (th viện dùng để t o các cửa sổ b n s đ c học sau này) sử d ng r t nhiều các macro. Tôi th y việc h ng d n b n kể t lúc này s giúp b n thích ng dễ dàng hơn trong t ơng lai. Điều kiện - Conditions Hãy nắm rõ: b n có thể t o ra những điều kiện trong preprocessor Đây là ví d về cách chúng ho t đ ng: C Code: #if condition /* Nhung ma nguon (source code) se duoc bien dich neu dieu kien dua ra la dung */ #elif condition2 /* Neu dieu kien tren khong dung, nhung ma nguon (source code) se duoc bien dich neu dieu kien 2 dung */ #endif T khóa #if cho phép đ a vào m t điều kiện cho preprocessor. #elif có nghĩa t ơng tự nh là else if. Điều kiện s d ng l i khi b n đ t vào nó dòng #endif. B n cần ghi nh rằng không có các ngo c {} trong các preprocessor. L i ích c a chúng là cho phép t o các điều kiện để biên dịch ch ơng trình. Trong tr ng h p điều kiện thỏa mưn, đo n code phía sau s đ c biên dịch. N u không nó s bị xóa đi trong th i điểm biên dịch ch ơng trình. Và nó s không xu t hiện khi ch ơng trình hoàn t t. Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 80 - Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 #ifdef và #ifndef Bây gi chúng ta s th y l i ích c a việc #define m t constant không xác định tr tôi có h ng d n b n tr c đó: c giá trị, nh C Code: #define CONSTANT Ta có thể sử d ng #ifdef để t o ra điều kiện "N u hằng số đư đ c xác định." #ifndef, để t o ra điều kiện: "N u hằng số ch a đ c xác định." B n hưy xem đo n code sau: C code: Code C: #define WINDOWS #ifdef WINDOWS /* Ma nguon cho Windows */ #endif #ifdef LINUX /* Ma nguon cho Linux */ #endif #ifdef MAC /* Ma nguon cho Mac */ #endif Đây là cách để vi t các ch ơng trình có thể ho t đ ng trên nhiều hệ điều hành khác nhau, ví d trong Windows, b n chỉ việc đ t vào #define WINDOWS, sau đó biên dịch. N u b n vi t ch ơng trình ho t đ ng trên Linux (t t nhiên là trong mã nguồn c a b n ph i có phần mã nguồn dành riêng cho Linux), b n chỉ cần thêm vào: #define LINUX, biên dịch l i, và lần này máy tính s biên dịch phần mã nguồn cho Linux, các phần khác s đ c bỏ qua. #ifndef để tránh các “inclusions vô h n” #ifndef th ng đ c dùng trong các file h để tránh các "inclusions vô h n". Th nào là m t inclusion vô h n ? ? Hưy t ng t ng, khá đơn gi n. Tôi có m t file A.h và m t file B.h . File A.h ch a m t dòng include file B.h. N i dung c a file B s đ Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 81 - c đ a vào trong file A. Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 Nh ng, hưy nghĩ đ n tr ng h p c chuối sau, gi sử file B.h l i include ng A.h? Tr ng h p này trong l p trình th ng hay x y ra. c l i n i dung c a File th nhât cần file th hai để ch y, file th 2 l i cần file th nh t để ch y. Hãy bỏ 10 giây để suy nghĩ, b n s nhanh chóng nh n th y chuyện gì s x y ra: 1. 2. 3. 4. 5. Máy tính đọc A.h và th y cần n i dung c a B.h Nó nh y vào B.h để đọc n i dung và nh n ra, đây cǜng cần n i dung c a A.h Vì v y, nó đ a n i dung c a A.h vào B.h, và trong A.h l i b o cần B.h! L i m t lần nữa, máy tính đi tìm đồng chí B.h và g p đồng chí A.h quen thu c. Vv và..vv, Không cần ph i là m t cao th để hiểu rằng nó không bao gi k t thúc giống nh câu hỏi "Tr ng hay gà có tr c" Trong thực t , vì bu c ph i thực thiện quá nhiều các inclusion, preprocessor d ng l i và b o "Đm, tao chán m y cái inclusion c chuối này c a mày lắm rồi !!" và đ t nhiên việc biên dịch bị crash. V y làm th quái nào để tránh cơn ác m ng kh ng khi p này? Đây là m t trick. Và kể t bây gi , tôi yêu cầu b n thực hiện chúng trong t t c các file.h c a b n, và t t nhiên là không có tr ng h p ngo i lệ: Code C: #ifndef DEF_FILENAME // Neu constant chua duoc xac dinh file nay chua duoc dua vao #define DEF_FILENAME // Ta xac dinh constant de lan sau file nay se khong dua vao lai nua /* Noi dung cua file.h (cac includes khac, cac prototypes cho cac functions, cac dong defines...)*/ #endif Và sau đó b n s đ t vào giữa #ifndef và #endif, n i dung c a file.h (các includes khác, các prototypes cho các functions, các dòng defines...) B n đư hiểu rõ nó ho t đ ng th nào rồi ch ? Tôi đư không hiểu trong lần đầu tiên khi tôi đ h ng d n về phần này. c Hưy t ng t ng, khi file.h đ c include lần đầu tiên. Máy tính đọc điều kiện "N u constant DEF_FILENAME ch a đ c xác định ". Vì đây chính là lần đầu tiên file đ c đọc, constant đó v n ch a xác định, do đó, preprocessor s đọc n i dung bên trong if Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 82 - Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 Instruction đầu tiên máy tính th y là nh sau: Code C: #define DEF_ FILENAME T i th i điểm hiện t i, constant đư đ c thi t l p. N u lần k ti p, file l i đ c yêu cầu include ti p t c, điều kiện là không còn đúng nữa, và yêu cầu này không đ c thi hành. T t nhiên, b n có thể gọi tên constant nh cách b n muốn. Tôi gọi nó là DEF_FILENAME vì thói quen, Nh ng mỗi ng i có m t s thích riêng mà đúng không. Điều quan trọng đây là đổi tên constant cho mỗi t p tin .h khác nhau. Không đ c dùng cùng m t constant cho t t c những file.h, n u không máy tính chỉ đọc file.h đầu tiên ch không ph i các file khác. Vì v y, b n hãy thay th FILENAME v i tên c a file.h c a b n. N u b n muốn chắc chắn những điều b n v a nghe không ph i vô nghĩa, tôi m i b n tham kh o ý ki n các th viện .h chuẩn có trong ổ c ng c a b n. B n s tìm th y ng i ta đư xây dựng chúng trên cùng m t nguyên tắc (m t ifndef đầu và m t endif cuối). Họ muốn chắc chắn rằng không có b t kì inclusion infinie nào x y ra. Th t buồn c i, tôi c m th y tôi đang d y b n m t ngôn ngữ l p trình m i. Nghĩ thì cǜng đúng m t m t nào đó, tuy nhiên, đối v i các preprocessor, nó s đọc mã nguồn c a b n ngay tr c khi gửi cho trình biên dịch bằng ngôn ngữ c a riêng nó. Nó cǜng có thể làm đ c 2-3 việc nhỏ nh t khác mà tôi không h ng d n đây, nh ng nhìn chung, ng i ta th ng sử d ng các preprocessor directives trong các file.h t ơng tự nh cách b n v a đ c học. Oh, đây là m t l u ý nhỏ tr c khi k t thúc: Tôi chân thành khuyên b n để thêm vài dòng trống ngay sau #endif cuối file.h c a b n. Để tránh #endif là dòng cuối cùng c a file, tôi đư g p lỗi khi biên dịch và và tôi đư vô cùng v t v để tìm ra nguyên nhân xu t phát t đâu. Nó báo lỗi "No new line at the end of file" Vì v y, hưy đ t 2-3 dòng trống sau #endif nh th này: C Code: #endif Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 83 - Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 Điều này cǜng đ c áp d ng t ơng tự trong các file.c. Hưy đ t m t số dòng trống điều này s giúp b n tránh đ c các lỗi nh c đầu không đáng có. cuối cùng, Tôi ch a bao gi nói v i b n rằng l p trình là m t môn khoa học chính xác đúng không. Đôi khi, b n rơi vào các lỗi quá kỳ l mà đ n nỗi b n ph i tự hỏi rằng éo bi t do ch ơng trình mình vi t hay do máy tính bị cái éo gì mà biên dịch éo đ c. Đ ng lo lắng n u nó x y ra v i b n, vì đây chỉ là m t trong các tai n n khi b n chọn nghề nghiệp l p trình này. Nh ng tr c sau gì thì các b n cǜng s đúc k t đ c những kinh nghiệm đáng quý sau mỗi lần g p lỗi thôi. N u sau t t c mọi nỗ lực tự mày mò tìm ki m, đ ng đ p bỏ chi c máy tính thân yêu và cǜng đ ng ng i ngùng đi tham kh o ý ki n ho c nh sự tr giúp t các b c tiền bối ho c b n bè, những ng i đư có kinh nghiệm l p trình, họ s chỉ cho b n nguyên nhân và cách gi i quy t các lỗi kỳ qu c này. Chú thích: thuật ngữ, từ vựng: 1. Preprocessor - Tiền xử lý: Là 1 quá trình bao gồm những ch ơng trình ch y tr c khi biên dịch. 2. Preprocessor directives ậ Chỉ thị tiền xử lý : là những chỉ thị cung c p h ng d n trình biên dịch xử lí tr c các thông tin tr c khi b c vào giai đo n biên dịch thực t . 3. Compile (v) & Compilation (n) ậ Biên dịch & Trình biên dịch: Là ho t đ ng dịch ngôn ngữ l p trình do b n vi t sang ngôn ngữ máy tính để t o ra m t ch ơng trình máy tính. 4. Compiler ậ Công c thực hiện ch c năng biên dịch (xem l i bài 1 ậ ch ơng 1). 5. Prototype ậ Khai báo hàm nguyên m u: M t prototype th t ra là l i chỉ d n cho máy tính. Nó s thông báo tr c v i máy tính có sự tồn t i c a function (xem l i bài 1 ậ ch ơng 2). 6. Macro - T p lệnh thay th : Đây đ c hiểu là m t t p h p các câu lệnh. Khi trong ch ơng trình có những khối câu lệnh giống nhau thì ng i ta có thể định nghĩa chúng bằng 1 macro cho khối câu lệnh đó và sau đó dùng tên c a macro này thay th cho toàn b khối lệnh kia trong suốt quá trình vi t ch ơng trình. 7. Source code ậ Mã nguồn: Là t p h p toàn b những gì b n vi t ra cho máy tính để nó biên dịch. 8. Constant ậ Giá trị hằng: Là những giá trị không đổi trong suốt quá trình làm việc c a máy tính. Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 84 - Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 Bài 6: Tạo ra những biến kiểu riêng của bạn Ngôn ngữ C cho phép chúng ta làm m t số điều th t sự đ c biệt: t o ra những bi n kiểu riêng c a b n, ta có thể gọi là “những bi n tự t o”. Hôm nay chúng ta s học về struct (cấu trúc) và kiểu liệt kê (enum). Việc t o kiểu bi n c a riêng b n s tr nên hữu ích khi các ch ơng trình bắt đầu ph c t p hơn. Hãy t p trung vì chúng ta s ti p t c sử d ng l i những ki n th c này trong những bài học ti p theo. L u ý rằng các th viện th ng định nghĩa theo kiểu riêng c a chúng. B n s không ph i m t quá nhiều th i gian tr c khi xử lý m t kiểu bi n, ho c có thể sau này là những kiểu ch ơng trình Window, Audio, Keyboard … Định nghĩa một cấu trúc (struct): C u trúc (struct) là m t t p h p gồm những phần tử có nhiều kiểu khác nhau. Không giống nh khi làm việc v i m ng (array), chúng ta đ c yêu cầu sử d ng cùng m t kiểu định d ng đối v i các phần tử trong toàn b m ng. V i c u trúc (struct), b n có thể t o ra m t t p h p gồm các bi n kiểu int, long, char ho c là double… Những c u trúc th ng đ c định nghĩa trong file.h, cǜng giống nh khi khai báo những nguyên m u (prototypes) hay những định nghĩa (defines). Chúng ta cùng xem m t VD: C code: struct TenCauTruc { int bien1; int bien2; int bienKhac; double soThapphan; }; Để khai báo m t c u trúc, chúng ta s bắt đầu bằng t khóa “struct”, ti p sau đó là tên đ i diện cho t p h p các phần tử (VD: sinhvien, taptin). Riêng cá nhân tôi th ng hay áp d ng nguyên tắc đ t tên c a bi n để đ t tên cho c u trúc, tôi th ng vi t hoa chữ cái đầu cho dễ nh n bi t. Gi sử, n u tôi th y c m “tenSinhvien” thì có nghĩa đó là tên c a m t bi n bình th ng vì chữ cái đầu c a nó không đ c vi t hoa. T ơng tự khi th y c m “TenSinhVien”, tôi bi t đây là tên c a m t “bi n tự t o” trong c u trúc. Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 85 - Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 Sau tên c u trúc các b n nh đóng m ngo c nhọn { } giống khi thao tác v i hàm. L u ý m t điều đ c biệt sau: B n ph i đ t m t d u ch m phẩy (;) sau d u đóng ngo c nhọn. Điều này là bắt bu c vì n u thi u nó thì ch ơng trình c a b n s không thể biên dịch đ c. Và những th nằm giữa 2 ngo c nhọn đó có gì l ? Đơn gi n thôi, đó là những “bi n thành phần” đ c t o ra để xây dựng nên c u trúc c a b n. M t c u trúc th ng có t 2 bi n tr lên, nh ng có l b n cǜng không cần quan tâm quá nhiều về v n đề này. Nh b n đư bi t, việc kh i t o m t bi n cơ b n không quá ph c t p. T t c những c u trúc mà b n th y th t ra chỉ là “t p h p” c a những bi n kiểu cơ b n nh int, long,… Ch có điều gì m i m đây c . Ví dụ về cấu trúc (struct): Gi sử b n muốn t o ra m t bi n để l u tọa đ m t điểm trên màn hình. Chắc chắn b n s cần đ n m t c u trúc nh th này khi t o ra các game 2D trong những phần ti p theo, đây là cơ h i để nâng cao trình đ m t chút. Đối v i những b n ch a có nhiều ki n th c về những khái niệm “hình học không gian 2 chiều” thì sau đây là m t số gi i thích cơ b n cho hình học 2 chiều (2D). Hệ trục tọa độ Oxy chứa điểm A Khi làm việc v i không gian 2 chiều (2D), chúng ta có 1 hệ tr c tọa đ gồm:    Gốc tọa đ O. T i đây giá trị gốc bằng 0 và tăng dần theo chiều mǜi tên c a 2 tr c tọa đ . Tr c hoành (tr c x) ch y t trái sang ph i, ch a các giá trị gọi là hoành đ . Tr c tung (tr c y) ch y t d i lên trên, ch a các giá trị gọi là tung đ . M t điểm b t kỳ trên m t phẳng tọa đ 2 chiều th ng có 2 thông số để xác định vị trí c a nó trên m t phẳng. Hai thông số đó gồm hoành đ (giá trị trên tr c x) và tung đ (giá trị trên tr c y). Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 86 - Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 Chúng ta th ng dùng m t bi n tên “x” để biểu diễn giá trị c a hoành đ , t ơng tự nh v y ta s dùng bi n “y” để biểu diễn giá trị c a tung đ . N u vi t điểm A (x;y) có nghĩa là điểm này có tên là A, hoành đ c a nó là x và tung đ là y. VD: ta có m t điểm diembatky (20;10), có nghĩa là điểm này có tên là diembatky, hoành đ c a nó là x=20 và tung đ là y=10. B n có thể vi t m t c u trúc Toadodiem để l u trữ các giá trị hoành đ trên tr c x và tung đ trên tr c y c a m t điểm. Nào nào, nó th t sự không khó đâu: C code: struct Toadodiem { int x; // hoanh do cua diem int y; // tung do cua diem }; C u trúc (srtuct) c a chúng ta có tên là “Toadodiem” gồm có 2 bi n “x” và “y” để lần l diễn hoành đ trên tr c x và tung đ trên tr c y. t biểu N u muốn b n hoàn toàn có thể t o ra m t c u trúc (struct) m i cho không gian 3 chiều (3D), chỉ việc thêm vào m t bi n “z” để biểu diễn tọa đ trên tr c đó (th ng gọi là cao đ ). V i những ki n th c này, chúng ta có thể t o ra m t c u trúc để qu n lý các điểm trong không gian 3D. Mảng cấu trúc (mảng struct): Những c u trúc có ch a m ng. Th t may mắn, chúng ta hoàn toàn có thể t o các m ng cơ b n và m ng ký tự (string) trong c u trúc. Nào bây gi gi sử chúng ta có m t c u trúc Taikhoan để l u trữ thông tin c a m t ng i dùng: C code: struct Taikhoan { char ten[100]; char ho[100]; char diachi[1000]; int tuoi; int gioitinh; // Boolean : 1 = nam, 0 = nu }; Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 87 - Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 C u trúc Taikhoan ch a 5 bi n thành phần, trong đó:   3 bi n đầu tiên kiểu char l u trữ các thông tin lần l t là: tên, họ, địa chỉ. 2 bi n còn l i kiểu int l u trữ các thông tin: tuổi và gi i tính. Riêng gi i tính là m t bi n d ng boolean (b n đư học các bài tr c về boolean), bi n này s tr về 1 = đúng = gi i tính là nam, tr về 0 = sai = gi i tính là nữ, chúng ta t m chia ra 2 gi i tính thôi nhé ^^! B n có thể ng d ng c u trúc này để t o m t ch ơng trình l u trữ danh sách ng i dùng. Dĩ nhiên là b n có thêm m t số bi n khác để bổ sung những thông tin mà b n muốn. Không có gi i h n số l ng bi n trong m t c u trúc nên b n đ ng lo. Sử dụng cấu trúc: Bây gi những c u trúc c a chúng ta đư đ d ng các function c a chúng trong file.c c định nghĩa trong các file.h và chúng ta có thể sử V y hãy cùng xem làm th nào để t o m t bi n mang kiểu Toadodiem (c u trúc đ ct o trên): C code: #include "main.h" // File.h chua cac prototypes va structs int main (int argc, char *argv[ ]) { struct Toadodiem diembatky; // Khoi tao bien diembatky co kieu Toadodiem return 0; } V a rồi chúng ta đư t o ra m t bi n “diembatky” mang kiểu bi n “Toadodiem”. Bi n này s tự đ ng bao gồm luôn 2 bi n thành phần x và y (hoành đ và tung đ ) mà ta đư khai báo tr c đó. V y chúng ta có bắt bu c ph i thêm t khóa “struct” mỗi lần khai báo bi n không ? Câu tr l i là CÓ: Điều này s giúp máy tính phân biệt các bi n tự t o (VD nh bi n kiểu Toadodiem) v i những bi n cơ b n (VD nh bi n kiểu int). Tuy nhiên các l p trình viên th ng c m th y l i khi ph i luôn thêm t khóa “struct” mỗi khi khai báo các bi n tự t o. Để gi i quy t v n đề này, họ đư phát minh ra m t lệnh đ c biệt, họ gọi nó là typedef. Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 88 - Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 Typedef: Tr l i v i những file.h có ch a những định nghĩa c u trúc Toadodiem c a chúng ta. Chúng ta s thêm vào m t câu lệnh gọi là typedef để t o ra m t tên c u trúc thay th cho toàn b c u trúc đó. Bây gi chúng ta s thêm vào m t dòng tr c khi khai báo c u trúc đầu đo n code lúc nãy: C code: typedef struct Toadodiem Toadodiem; struct Toadodiem { int x; // hoanh do cua diem int y; // tung do cua diem }; Tôi s gi i thích cho b n về dòng m i đ c thêm vào này, nó s đ c chia làm 3 phần chính (nói thêm v i b n là tôi không hề mắc lỗi khi l p l i c m Toadodiem 2 lần). 1. typedef: s chỉ ra cho máy tính bi t rằng chúng ta đang đ t m t tên thay th cho c u trúc. 2. struct Toadodiem: đây là tên c a c u trúc mà b n s đ t tên thay th v i typedef. 3. Toadodiem: đây chính là tên mà b n đ t để thay th cho c u trúc struct Toadodiem. B n có thể đ t m t tên b t kỳ mà b n thích, tôi đ t là Toadodiem để cho các b n th y rằng khi dùng typedef, b n s t o ra m t c m t thay th cho c u trúc v i ch c năng t ơng đ ơng. Rõ ràng điều này có nghĩa là khi b n vi t c m t Toadodiem thì nó s thay th cho toàn b c u trúc struct Toadodiem. Bằng cách này, b n s không ph i đ t c m struct Toadodiem mỗi khi khai báo bi n tự t o c a mình nữa. V y bây gi chúng ta s vi t l i đo n code trong main.c sau khi đư dùng lệnh typedef nhé: C code: int main (int argc, char *argv[ ]) { Toadodiem diembatky; /* May tinh se hieu bien nay mang kieu cau truc Toadodiem sau khi da duoc dat ten thay the boi typedef */ return 0; } Tôi khuy n khích các b n nên t p thói quen dùng typedef v i các c u trúc giống nh cách tôi đư làm v i c u trúc Toadodiem trong bài học này. Hầu h t các l p trình viên đều làm nh v y. Việc này s giúp các b n ti t kiệm th i gian khi không ph i vi t l i nhiều lần t “struct” trong c đo n code. Có m t điều l là hình nh m t l p trình viên giỏi thì th ng khá l i. Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 89 - Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 Chỉnh sửa các thành phần của cấu trúc: Bây gi thì bi n diembatky đư đ c kh i t o, và n u chúng ta muốn thay đổi những thành phần trong nó thì sao. Làm th nào để tác đ ng vào bi n x và y, nào cùng xem thử nhé: C code: int main (int argc, char *argv[ ]) { Toadodiem diembatky; diembatky.x = 10; diembatky.y = 20; return 0; } Bây gi thì giá trị c a bi n diembatky đư đ c thay đổi, chúng ta đư cho nó m t giá trị hoành đ x=20 và tung đ y=10. Bây gi diembatky c a chúng ta đang tọa đ (20;10). Tôi s minh họa m t chút bằng m t phẳng tọa đ Oxy trong hình học 2 chiều để các b n có cái nhìn trực quan hơn: Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 90 - Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 V y tóm l i, để truy c p vào các bi n thành phần c a c u trúc chúng ta s vi t theo cách sau: C code: tenbientutao.tenBienThanhPhanTrongCauTruc Bi n tự t o diembatky tách biệt v i bi n thành phần x và y trong c u trúc Toadodiem. Sử d ng c u trúc Taikhoan mà chúng ta đư t o dùng nh p tên, họ sau đó in ra màn hình. đầu bài và vi t ch ơng trình yêu cầu ng i Đo n code s nh sau: C code: int main (int argc, char *argv[ ]) { Taikhoan nguoidung; printf ("Ten ban la gi ? "); scanf ("%s", nguoidung.ten); printf ("Ho cua ban la gi ? "); scanf ("%s", nguoidung.ho); printf ("Ho ten day du cua ban la %s %s", nguoidung.ho, nguoidung.ten); return 0; } Console: Ten ban la gi ? Nhan Ho cua ban la gi ? Sieu Ho ten day du cua ban la Sieu Nhan Chúng ta dùng hàm scanf để nh p giá trị cho bi n nguoidung.ten, có nghĩa là lúc này giá trị đó s đ c truyền thẳng vào bi n ten c a nguoidung. B n chỉ việc thực hiện t ơng tự nh v y đối v i họ, tuổi, gi i tính. Dĩ nhiên là b n cǜng không cần ph i học về c u trúc (struct) thì m i có thể vi t đ c ch ơng trình trên. Mọi việc b n cần làm chỉ đơn gi n là t o ra các bi n l u trữ tên, họ … giống nh các bài học mà ta đư học tr c đây. Nh ng điều thú vị struct là b n có thể t o ra những kiểu bi n riêng cho t ng tr Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 91 - ng h p. Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 Gi sử b n có m t game dành cho 2 ng i: C code: Taikhoan nguoichoi1, nguoichoi2; … B n th y không, nh nó mà ng t ng ng i. i chơi s có thể l u trữ tên, họ … những thông tin riêng c a Tuy nhiên, chúng ta còn có thể làm tốt hơn, th m chí chúng ta còn có thể t o m t m ng cho c u trúc đó. R t đơn gi n thôi: C code: Taikhoan nguoichoi[2]; Để khai báo bi n tên ng i chơi vị trí th 0 trong m ng trên thì b n s vi t nh sau: C code: nguoichoi[0].ten L i ích c a việc sử d ng m ng đây là b n có thể t o ra m t vòng l p để yêu cầu nh p thông tin ng i chơi th 1 và th 2 nh ng không cần ph i vi t đo n code đó 2 lần. Chỉ cần tham chi u vào t ng thành phần c a m ng để yêu cầu nh p t ng tên, họ, địa chỉ … Bài T p: Hãy t o m t m ng và sử d ng vòng l p để yêu cầu ng i chơi nh p vào những thông tin khác. Hãy bắt đầu v i 2 ng i chơi, nh ng sau khi đư nắm vững b n có thể m r ng hơn theo ý thích c a b n. Sau cùng hiển thị ra màn hình t t c những thông tin mà b n đư thu th p đ c t những ng i chơi. Khởi tạo một cấu trúc: Đối v i những c u trúc cǜng giống nh các bi n, m ng hay con trỏ, tôi khuy n khích các b n nên kh i t o chúng “không ch a giá trị nào” ngay t đầu. Tôi nói th t đ y, hưy để tôi nhắc l i m t chút, khi m t bi n bình th ng đ c t o ra, nó s mang giá trị b t kỳ c a ô địa chỉ đ c máy tính c p phát cho nó. Đôi khi bi n mang giá trị 0, nh ng đôi khi nó l i mang m t giá trị rác c a m t ch ơng trình khác đư sử d ng tr c đó, những giá trị rác này th ng không có ý nghĩa (chẳng h n nh : -69,69). S n tiện tôi s nhắc l i m t chút về các cách kh i t o:    Bi n: chúng ta th ng cho nó mang giá trị 0 lúc đầu (trong những tr ng h p đơn gi n). Con trỏ: chúng ta th ng đ t giá trị là NULL. NULL là m t định nghĩa có s n trong th viện stdib.h nói chung nó cǜng có nghĩa là giá trị 0, nh ng chúng ta v n sử d ng NULL để bi t đ c rằng đó là con trỏ ch không ph i bi n bình th ng. M ng: chúng ta th ng đ t giá trị 0 cho mỗi phần tử c a m ng. Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 92 - Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 Đối v i c u trúc (struct), việc kh i t o s có m t chút giống v i m ng. Th t v y, chúng ta cùng xem khi kh i t o m t bi n c u trúc thì nh th nào nhé: C code: Toadodiem diembatky = {0, 0}; Sau khi kh i t o nh trên máy tính s tự đ ng gửi giá trị lần l diembatky.y = 0. t vào diembatky.x = 0 và Quay tr l i v i c u trúc Taikhoan (có ch a chuỗi ký tự). B n có thể bắt đầu t o ra m t chuỗi trong c u trúc bằng cách vi t c p d u ngo c kép " " (không có thành phần nào ch a bên trong c p d u này). Tôi ch a nói cho b n bi t về nó những bài học về chuỗi tr c đây, nh ng bây gi v n ch a mu n để bi t về nó. Nh đó, chúng có thể khai báo trong c u trúc Taikhoan những thông tin nh ten, ho, diachi, tuoi, gioitinh nh sau: C code: Taikhoan nguoidung = {"", "", "", 0, 0}; Cá nhân tôi không th ng sử d ng cách này. Tôi thích sử d ng m t hàm taoToadodiem v i ch c năng kh i t o các bi n cho bi n diembatky c a tôi. Để làm đ c việc đó b n ph i t o ra bi n con trỏ. N u tôi chỉ sử d ng bi n bình th ng, m t b n sao s đ c t o ra trong hàm (nó không ph i là bi n b n đư t o) và hàm s thay đổi giá trị c a b n sao đó ch không ph i giá trị c a bi n mà b n đư kh i t o. N u c m th y khó hiểu đo n này, b n có thể xem l i bài học cǜ về con trỏ. Vì v y, bây gi chúng ta s ph i học cách sử d ng con trỏ (pointer) trong c u trúc (struct). Mọi th bắt đầu có chút thú vị rồi đây. Con trỏ cấu trúc (pointer of struct): M t con trỏ c u trúc đ kiểu cơ b n nào khác: c t o ra theo cách t ơng tự nh m t con trỏ kiểu int, double hay b t kỳ C code: Toadodiem* diembatky = NULL; Chúng ta v a kh i t o m t con trỏ cho c u trúc Toadodiem, con trỏ có tên là diembatky. Để tránh làm các b n bỡ ngỡ tôi xin phép nói thêm rằng b n v n có thể đ t d u * tr trỏ nh chúng ta th ng làm tr c đây, nó s nh th này: c tên con C code: Toadodiem *diembatky = NULL; Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 93 - Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 Tôi v n th ng làm nh cách trên, vì trong tr ng h p để định nghĩa nhiều con trỏ trên cùng m t dòng thì b n ph i đ t d u * tr c mỗi con trỏ đó. VD: C code: Toadodiem *diembatky1 = NULL, *diembatky2 = NULL; Gửi một hàm vào cấu trúc: Những gì chúng ta quan tâm đây là làm sao để sử d ng m t con trỏ c u trúc trong hàm, t đó ta có thể thay đổi trực ti p giá trị c a bi n. Chúng ta s thử v i vd này: Chúng ta chỉ cần đơn gi n t o ra m t bi n Toadodiem và sau đó gửi địa chỉ c a nó vào hàm taoToadodiem. Hàm này s qui định các thành phần có giá trị 0. Hàm taoToadodiem s cần m t tham số (parameter): tham số đó là m t con trỏ đ n c u trúc Toadodiem (a *Toadodiem): C code: int main (int argc, char *argv[ ]) { Toadodiem diembatkyCuatoi; taoToadodiem(&diembatkyCuatoi); return 0; } void taoToadodiem(Toadodiem* diembatky) { // Tao cac bien thanh phan cua cau truc o day } Bi n diembatkyCuatoi đư đ c t o ra và địa chỉ c a nó s đ c gửi vào hàm taoToadodiem, chúng ta gọi bi n này là con trỏ (b n có thể đ t tên nó nh th nào tùy ý, điều này không nh h ng đ n hàm). Nào bây gi đối v i hàm taoToadodiem, chúng ta s lần l t kh i t o giá trị cho các thành phần. Đ ng quên đ t d u * tr c tên c a con trỏ để truy c p vào các bi n c a nó. N u thi u d u * b n s chỉ thay đổi địa chỉ con trỏ và đó không ph i là điều chúng ta muốn máy tính thực hiện. Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 94 - Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 Ok, nh ng mà có m t v n đề ... chúng ta th t sự không thể làm đ c: C code: void taoToadodiem(Toadodiem* diembatky) { *diembatky.x = 0; *diembatky.y = 0; } Trông th t đơn gi n ... nh ng t i sao chúng ta l i không thể làm điều đó? Nguyên nhân là vì d u ch m phân cách chỉ làm việc v i các ký tự, nó không hiểu d u * là gì. Nh ng chúng ta cần sử d ng d u * để truy c p vào và thay đổi giá trị c a bi n. Gi i pháp cho v n đề này là chúng ta s đ t m t c p ngo c đơn để bao phần d u sao và tr ch m ngăn cách l i. Lúc này chúng ta có thể truy c p vào và thay đổi giá trị c a bi n: cd u C code: void taoToadodiem(Toadodiem* diembatky) { (*diembatky).x = 0; (*diembatky).y = 0; } Đo n code trên đư ho t đ ng, b n có thể kiểm tra thử. Những bi n kiểu c u trúc Toadodiem đư đ c đ a vào hàm và kh i t o cho chúng giá trị x=0, y=0. Trong ngôn ngữ C, chúng ta th trên. ng kh i t o các c u trúc theo cách đơn gi n mà ta đư th y Tuy nhiên, đối v i C++, việc kh i t o th ng đ c thi t l p trong các hàm. C++ th t sự không có gì khác ngoài m t sự “c i ti n” c a C. T t nhiên là có r t nhiều điều để nói về ngôn ngữ này, ít thì cǜng tốn c 1 cuốn sách để vi t về nó, và chúng ta s không thể học t t c cùng m t lúc bây gi . Một phím tắt thường được sử dụng phổ biến: B n s th y rằng con trỏ đ c sử d ng r t th ng xuyên. Thẳng thắn mà nói, ngôn ngữ C hầu nh chỉ sử d ng những c u trúc con trỏ. Tôi đang nói v i b n về điều này m t cách r t nghiêm túc (không hề c i nhé, hehe)! Nh đư nói trên, khi sử d ng c u trúc con trỏ thì ta ph i vi t th này: C code: (*diembatky).x = 0; Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 95 - Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 Nh ng những nhà l p trình viên thiên tài v n th y cách này ch a đ nhanh, họ c m th y khó chịu v i những c p d u ngo c đơn. Ngay sau đó, những vị l i bi ng thông minh này đư sáng t o ra phím tắt sau đây để thay th : C code: *diembatky -> x = 0; Phím tắt này mô phỏng hình nh c a m t mǜi tên, nó là sự k t h p c a m t d u tr (-) và m t d u l n ( > ). Khi chúng ta vi t diembatky -> x cǜng t ơng đ ơng v i (*diembatky).x Nh rằng b n có thể sử d ng mǜi tên ( -> ) khi thao tác v i con trỏ và n u làm việc trực ti p v i các bi n, b n ph i sử d ng d u ch m ( . ) nh chúng ta đư học đầu bài. Nào hãy thử áp d ng những phím tắt v a rồi vào hàm taoToadodiem xem nh th nào: C code: void taoToadodiem(Toadodiem* diembatky) { *diembatky->.x = 0; *diembatky->.y = 0; } Hãy nh rõ cách dùng phím tắt mǜi tên này, chúng ta s còn dùng l i nó nhiều lần nữa. Cẩn th n đ ng nhầm l n giữa việc dùng mǜi tên ( -> ) v i d u ch m ( . ). Mǜi tên là dành cho con trỏ, và d u ch m là dành riêng cho bi n. Cùng xem m t ví d nhỏ để phân biệt rõ hơn giữa chúng nhé: C code: int main (int argc, char *argv[ ]) { Toadodiem diembatkyCuatoi; Toadodiem *contro = &diembatkyCuatoi; diembatkyCuatoi.x = 10; // Lam viec voi mot bien ta su dung dau cham contro -> x = 10; // Lam viec voi mot con tro ta su dung mui ten } Giá trị c a x đ c gán bằng 10 theo 2 cách: đầu tiên ta làm việc trực ti p trên bi n, lần th hai ta làm việc thông qua con trỏ. Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 96 - Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 Kiểu liệt kê (enum): Kiểu liệt kê (enum) là m t cách hơi khác để t o ra các bi n riêng c a b n. Kiểu liệt kê (enum) không bắt bu c ph i ch a các “bi n thành phần” nh c u trúc (struct). Đây là m t danh sách “các giá trị phù h p” cho m t bi n. Do đó kiểu enum s chi m 1 địa chỉ b nh để dùng cho các giá giá trị mà b n xác định (và chỉ duy nh t mỗi lần 1 giá trị). Vd: C code: typedef enum Volume Volume; enum Volume { LOW, MEDIUM, HIGH } B n th y rằng chúng ta l i sử d ng typedef nh đư dùng tr c đó. Để t o m t danh sách liệt kê, chúng ta dùng c m enum. Danh sách c a chúng ta tên là Volume (cái này trên tivi ng i ta hay gọi là âm l ng đó mà). Đây là m t bi n tự t o giúp chúng ta chọn ra 1 trong 3 giá trị đư đ c chỉ ra LOW ho c MEDIUM ho c HIGH. Bây gi chúng ta đư có thể t o ra m t bi n Volume (âm l thanh khi nghe nh c trên máy tính. Đây là m t ví d , kh i t o bi n âm l ng) để điều chỉnh đ l n c a âm ng v a cho nh c: C code: Volume music = MEDIUM; Sau này chúng ta v n có thể yêu cầu máy tính thay đổi giá trị c a âm l LOW. ng thành HIGH ho c Sự kết hợp của những giá trị: B n có th y rằng tôi đư vi t IN HOA những giá trị trong danh sách liệt kê. Việc này g i cho các b n nh về những hằng số (constants) và định nghĩa (defines) đúng không ? Th t v y, nó cǜng gần nh t ơng tự nh ng không hoàn toàn giống hẳn. Trình biên dịch s tự đ ng gán m t số cho t ng giá trị trong danh sách liệt kê. Trong tr ng h p danh sách liệt kê Volume c a chúng ta, LOW mang giá trị 0, MEDIUM và HIGH lần l t mang giá trị 1 và 2. Sự liên k t đ c máy tính tự đ ng sắp x p, và nó luôn bắt đầu bằng giá trị 0. Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 97 - Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 Không giống nh #define là m t trình biên dịch t o ra MEDIUM 1, kiểu liệt kê không ph i là m t tiền xử lý. Nó chỉ gần giống thôi. Thực t là khi b n đư kh i t o m t bi n music = MEDIUM, ch ơng trình s đ t giá trị 1 vào ô b nh đó. V y, có l i ích gì khi bi t tr c giá trị c a MEDIUM là 1 và HIGH là 2 … ? Theo tôi thì không, chúng ta không cần quan tâm t i việc này. Trình biên dịch s tự đ ng liên k t các giá trị và đ t chúng vào t ng bi n. Chúng ta chỉ cần vi t nh sau: C code: if (music == MEDIUM) { // Am thanh cua chuong trinh choi nhac se theo gia tri cua Volume } Đ ng để ý đ n giá trị c a MEDIUM là bao nhiêu, b n c để cho trình biên dịch tự đ ng qu n lý các số giá trị trong danh sách. L i ích c a việc này là gì? Việc này giúp cho code c a b n dễ đọc hơn. Th t v y, mọi ng i có thể dễ dàng đọc hiểu tr c If c a b n (điều kiện đ c hiểu là n u bi n music là MEDIUM thì chơi nh c m c v a). Gán một giá trị cụ thể: T bây gi , trình biên dịch s tự đ ng đ t số 0 cho giá trị th nh t và lần l ... theo th tự. t ti p theo là 1, 2 ,3 Nó có thể yêu cầu gán t ng con số c thể cho mỗi giá trị thành phần c a kiểu liệt kê. Đó là những gì thú vị t nó sao? Nào để tôi cho b n th y, gi sử trên máy tính c a b n âm l ng (volume) đ c qui định các m c t 0 đ n 100 (m c 0 có nghĩa là câm nín và 100 t c là hát đi c c tai). Đây chính là cơ h i để chúng ta thử gán giá trị cho các thành phần trong kiểu liệt kê. C code: typedef enum Volume Volume; enum Volume { LOW = 10, MEDIUM = 50, HIGH = 100 } Nh các b n th y, m c volume LOW bằng 10% m c volume c a máy tính, MEDIUM thì bằng 50% và t ơng tự HIGH là 100%. Ng i ta cǜng có thể t o ra m t giá trị m i tên MUTE (câm nín). Chúng ta s gán số 0 cho giá trị này. B n đư hiểu ra v n đề đúng không. Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 98 - Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C      www.siteduzero.com Tác giả: M@teo21 Tổng kết: C u trúc (struct) là m t kiểu bi n tự t o, b n có thể tự mình t o ra nó và sử d ng trong ch ơng trình c a b n. Bi n này do b n định nghĩa, không nh các kiểu bi n cơ b n nh int, double … M t c u trúc luôn ch a những “bi n thành phần”, những bi n thành phần này là các bi n cơ b n kiểu int, double …, t ơng tự nh v i m ng nh ng c u trúc có thể ch a nhiều bi n khác kiểu. Khi b n muốn truy c p vào m t bi n thành phần trong c u trúc, b n có thể sử d ng d u ch m để ngăn cách tên c a bi n bình th ng và tên c a bi n thành phần c a c u trúc, VD: bienbinhthuong.bienthanhphan1 N u b n muốn sử d ng con trỏ để truy c p vào bi n thành phần, b n chỉ cần thay d u ch m thành mǜi tên ( -> ), VD: contro -> bienthanhphan1 M t kiểu liệt kê cǜng là bi n tự t o, b n có thể đ a vào đây danh sách các giá trị nh trong VD trên: LOW, MEDIUM, HIGH. Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 99 - Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 Bài 7: Những thao tác làm việc với tập tin Những lỗi c a các bi n chỉ tồn t i trong RAM. M t khi b n đư hoàn thành ch ơng trình c a mình, t t c các bi n c a b n s bị xóa khỏi b nh và giá trị c a chúng s không thể khôi ph c l i. V y thì làm cách nào để chúng ta có thể l u l i điểm số cao nh t trong game? Làm sao để m t ch ơng trình so n th o văn b n ho t đ ng n u t t c những gì b n vi t ra s bị xóa khi tắt ch ơng trình? May mắn thay, b n có thể đọc và ghi dữ liệu c a các t p tin trong ngôn ngữ C. Những t p tin này đ c l u vào ổ c ng (hard drive) c a máy tính: l i ích là những t p tin s đ c l u l i t i đó, ngay c khi b n d ng ch ơng trình hay tắt máy tính. Để có thể đọc và ghi dữ liệu vào các t p tin chúng ta s đây: con trỏ, c u trúc, chuỗi ký tự … ng d ng t t c những gì đư học tr c Mở và đóng một tập tin: Để đọc và ghi dữ liệu trong m t t p tin chúng ta s sử d ng các hàm (functions) có s n trong th viện “stdio” mà chúng ta đ c học tr c gi . Vâng và cǜng chính nó là nơi ch a 2 function quen thu c “printf” và “scanf”. Nh ng nó không chỉ ch a duy nh t 2 function này mà còn có c những function đ c t o ra để làm việc v i các t p tin. T t c những th viện (libraries) mà chúng ta đư t ng sử d ng cho t i nay (stdlib.h, stdio.h, math.h, string.h …) đ c gọi là các th viện chuẩn. Chúng s đ c IDE tự đ ng nh n diện dù cho b n có ch y ch ơng trình trên b t kỳ hệ điều hành nào Windows, Linux, Mac hay m t hệ điều hành nào đó. Những th viện chuẩn có số l ng gi i h n và hỗ tr cho phép b n thực hiện m t số điều cơ b n nh chúng ta đư t ng th y trong những bài tr c. Đối v i những ch c năng cao c p hơn, nh là m m t cửa sổ ch ơng trình, ta ph i t i về và cài đ t m t th viện m i. Chúng ta s học về chúng sau !!! Để chắc cú thì b n nên luôn luôn bắt đầu những dòng code c a mình v i việc khai báo các th viện chuẩn nh stdio.h và stdlib.h trên đầu t p tin “file.c” c a b n: C code: #include <stdlib.h> #include <stdio.h> Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 100 - Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 Những th viện này r t cơ b n, thực sự r t cơ b n, nh ng tôi v n khuyên b n hãy luôn thêm nó vào t t c những ch ơng trình c a b n sau này, dù cho chúng có tác d ng gì đi nữa. Okie! Bây gi chúng ta s xem những th viện tuyệt v i đó có thể làm gì, chúng ta có thể gi i quy t câu hỏi làm sao để m ho c đọc ho c ghi dữ liệu vào m t t p tin. D i đây là những gì luôn diễn ra khi b n yêu cầu máy tính thực hiện các thao tác đó: 1. Chúng ta s gọi m t hàm m t p tin , th s tr về cho chúng ta m t con trỏ đ n t p tin. 2. Chúng ta s kiểm tra xem thao tác m t p tin có thành công không (t t nhiên là tr đó ph i tồn t i) bằng cách kiểm tra các giá trị con trỏ mà ta nh n đ NULL, có nghĩa là thao tác m t p tin đư th t b i, trong tr c tiên t p tin c. N u con trỏ mang giá trị ng h p này chúng ta không thể ti p túc nữa (màn hình s hiển thị thông báo lỗi). 3. Trong tr ng h p thao tác m t p tin thành công (con trỏ không mang giá trị NULL), chúng ta có thể tho i mái đọc, ghi dữ liệu lên t p tin thông qua ch c năng c a các hàm mà các b n sắp đ c học. 4. M t khi b n đư làm xong việc v i các t p tin, hẳn là b n ph i nghĩ t i việc đóng nó l i đúng không? Và chúng ta có fclose. Đầu tiên chúng ta s tìm hiểu về 2 hàm fopen và fclose. Sau khi đư nắm rõ v n đề, chúng ta s bắt đầu học cách đọc n i dung c a m t t p tin và ghi dữ liệu vào nó. fopen: hàm mở một tập tin: Trong bài học về chuỗi ký tự (string), chúng ta đư bi t cách sử d ng “nguyên m u hàm” (prototype) nh m t “h ng d n sử d ng” c a hàm (function). Đó cǜng là thói quen phổ bi n c a các l p trình viên, họ đọc các prototypes để hiểu về những functions có trong đo n code. Tuy nhiên tôi th y rằng chúng ta v n cần gi i thích thêm m t số th : C code: FILE* fopen(const char* tenTaptin, const char* chedoMotaptin); Hàm này có 2 tham số:   Tên t p tin đ c m . Ch đ m t p tin, nhìn vào tham số này b n có thể th y đ c nó diễn t những gì b n muốn làm v i t p tin: chẳng h n nh đọc ho c ghi dữ liệu ho c c 2 ch c năng cùng lúc. Hàm này s tr về m t con trỏ t i FILE. Đây là con trỏ đ n c u trúc FILE. C u trúc này đư đ c định nghĩa s n trong th viện stdio.h, b n có thể m file th viện ra để tự mình kiểm tra xem Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 101 - Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 FILE có cái quái gì trong đó nh ng tôi nghĩ dù có ch a gì đi nữa cǜng không nh h đ n công việc c a chúng ta. đ ng nhiều T i sao tên c a c u trúc (struct) FILE l i đ c vi t hoa h t v y? Tôi nh những cái tên c vi t hoa là dành riêng cho các định nghĩa (define) và hằng (constant) cơ mà ??? Đây là quy tắc do tôi tự đ t ra (và r t nhiều l p trình viên khác cǜng làm theo quy tắc t ơng tự). B n không bắt bu c ph i làm theo những quy tắc c a tôi và ng i t o ra th viện stdio chắc cǜng có những quy tắc c a riêng mình. Và lý do duy nh t tôi có thể gi i thích cho b n t i sao FILE l i đ c vi t hoa trong th viện stdio, là vì ng i t o ra nó muốn nh v y ^^! Đ ng để m t cái tên làm b n m t t p trung. Rồi b n s th y những th viện chúng ta sắp học sau này cǜng sử d ng quy tắc đ t tên r t giống v i tôi, c thể là khi đ t tên c u trúc th ng chỉ cần vi t hoa chữ cái đầu tiên. Quay l i v i hàm fopen c a chúng ta. Nó tr về FILE*. Con trỏ này th t sự r t quan trọng, nó s giúp ta có thể đọc ho c ghi dữ liệu vào m t t p tin. Bây gi chúng ta s t o m t con trỏ c a FILE để bắt đầu hàm c a b n (VD function main): C code: int main (int argc, char *argv[ ]) { FILE* taptin = NULL; return 0; } Con trỏ đ c kh i t o giá trị NULL ngay t đầu. Tôi nhắc l i cho b n nh đây là m t nguyên tắc cơ b n, hãy luôn kh i t o giá trị NULL cho con trỏ ngay t đầu n u b n không có giá trị nào khác để gán cho nó. N u không thực hiện điều này, rồi b n s tha hồ đọc thông báo lỗi trong t ơng lai nhé. B n s th y rằng việc vi t struct FILE *taptin = NULL là không cần thi t. Ng i t o ra stdio cǜng đư t o ra m t typedef nh tôi đư d y b n để ta có thể vi t là FILE *taptin = NULL. L u ý rằng c u t o c a c u trúc có thể thay đổi tùy theo t ng hệ điều hành khác nhau (không nh t thi t lúc nào cǜng ph i có các bi n thành phần giống nhau). Vì v y, chúng ta s không bao gi trực ti p thay đổi n i dung c a FILE (vd nh ta s không sử d ng taptin.bienthanhphan1 để tác đ ng vào bi n thành phần c a FILE). Nh ng bằng cách sử d ng các hàm chúng ta có thể làm việc đ c v i FILE. Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 102 - Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 Nào bây gi chúng ta s gọi hàm fopen và l y giá trị nó đư tr về trong con trỏ taptin. Nh ng tr c khi làm điều đó, tôi ph i gi i thích cho các b n về tham số th hai, tham số chedoMotaptin. Th t ra có m t đo n code đư đ c gửi cho máy tính để nói cho nó bi t rằng t p tin này s đ c m trong ch đ “chỉ đ c đọc dữ liệu” (read-only) ho c “chỉ đ c ghi dữ liệu” (write-only) ho c c 2 ch đ cùng m t lúc. Sau đây là những ch đ có thể áp d ng cho t p tin c a b n trong máy tính:       “r” ậ read only ậ ch đ chỉ đọc: B n có thể đọc đ c n i dung c a t p tin nh ng không thể vi t thêm gì vào (Tất nhiên là tập tin đã được tạo ra trước đó). “w” ậ write only ậ ch đ chỉ vi t: B n có thể vi t thêm ho c chỉnh sửa n i dung t p tin nh ng l i không đọc đ c nó (Nếu chưa tồn tại thì tập tin sẽ được tạo ra ở chế độ này). “a” ậ user addition ậ ch đ bổ sung: B n có thể vi t thêm vào n i dung c a m t t p tin bắt đầu t vị trí k t thúc n i dung t p tin đó (Nếu chưa tồn tại thì tập tin sẽ được tạo ra ở chế độ này). “r+” ậ read and write ậ ch đ đọc và vi t: B n có thể đọc và vi t trong n i dung c a t p tin (Tất nhiên là tập tin đã được tạo ra trước đó). “w+” ậ read and write, with removal of content beforehand ậ ch đ đọc và vi t đồng th i xóa s ch n i dung t p tin tr c đó: Tr c h t t p tin s không ch a b t kỳ n i dung nào tr c đó. B n có thể vi t trong n i dung c a t p tin và đọc nó sau (Nếu chưa tồn tại thì tập tin sẽ được tạo ra ở chế độ này). “a+” ậ add read/write at the end ậ ch đ đọc và vi t vị trí cuối cùng: B n có thể vi t và đọc n i dung c a t p tin bắt đầu t vị trí k t thúc c a n i dung trong t p tin (Nếu chưa tồn tại thì tập tin sẽ được tạo ra ở chế độ này). Để tôi nói thêm cho b n m t thông tin, tôi chỉ m i cho b n th y m t phần các ch đ c a thao tác m t p tin. Th t ra còn m t điều nữa, v i mỗi ch đ mà b n đang th y trên, n u b n thêm ký tự “b” sau ký tự đầu tiên c a mỗi ch đ (“rb”, “wb”, “ab”, “rb+”, “wb+”, “ab+”), lúc này các t p tin c a b n s đ c m trong ch đ nhị phân (binary). Đây là m t cách khá đ c biệt mà chúng ta s không đề c p đây. Th t ra ch đ hiển thị văn b n (text mode) là để l u trữ dữ liệu d ng văn b n vd nh “tên hiển thị” (chỉ hiển thị các ký tự), trong khi ch đ hiển thị số nhị phân (binary mode) là để l u trữ dữ liệu d ng số vd nh “dung l ng b nh ” (hầu nh chỉ toàn là số). Đây là sự khác nhau duy nh t giữa 2 ch đ hiển thị. Dù là ch đ nào thì chúng v n ho t đ ng theo m t cách t ơng tự nhau nh chúng ta th y trong bài học này. Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 103 - Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 Cá nhân tôi th ng sử d ng ch đ “r” (chỉ đọc), “w” (chỉ vi t), “r+” (đọc và vi t). Ch đ “w+” có m t chút nguy hiểm vì nó s xóa s ch n i dung t p tin c a b n mà chẳng cho b n m t thông báo nào. B n chỉ nên sử d ng ch đ này khi muốn làm m i (reset) l i t p tin c a mình. Về ch đ “a” có l nó s hữu d ng trong m t số tr số thông tin vào cuối t p tin. ng h p khi mà b n chỉ muốn bổ sung m t N u b n chỉ có ý định đọc m t t p tin, tôi khuy n khích các b n nên chọn ch đ “r”, dĩ nhiên là ch đ “r+” cǜng s giúp b n đọc đ c t p tin nh ng trong ch đ “r”, t p tin c a b n s đ m b o tính b o m t, b n s không s ng i nào đó thay đổi n i dung c a t p tin, đây cǜng là m t cách b o vệ t p tin c a mình. N u b n muốn vi t m t hàm loadLevel (để t i các c p đ trò chơi) thì nên chọn ch đ “r”, còn n u b n vi t m t hàm saveLevel (để l u l i các c p đ trò chơi) thì chỉ cần chọn ch đ “w” là đ . Đo n code sau đây s m m t t p tin test.txt ch đ “r+” (đọc / ghi): C code: int main (int argc, char *argv[ ]) { FILE* taptin = NULL; taptin = fopen("test.txt", "r+"); return 0; } Con trỏ taptin sau đó s thành con trỏ h Nh ng t p tin test.txt đ Nó nên đ cđ t ng đ n t p tin test.txt đâu? c đ t trong cùng m t th m c v i t p tin thực thi (executable) .exe c a b n. Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 104 - Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 Để tiện theo dõi bài học này, hãy t o m t t p tin test.txt trong th m c nh tôi đư làm trong hình d i: Nh b n th y, tôi đang sử d ng IDE Code::Blocks, đây là câu tr l i cho câu hỏi t i sao l i tồn t i t p tin dự án (project) có đuôi .cbp (ch không ph i là t p tin dự án có đuôi ch m .sln nh những ng i dùng Visual C++). T m th i, điều quan trọng cần để ý đây là t p tin test.txt đ c đ t trong cùng m t th m c v i ch ơng trình test.exe. V y t p tin đ c t o ph i có đuôi .txt m i đ c sao? Không. Đó là do b n chọn phần định d ng c a t p tin khi b n m t p tin. B n có thể sáng t o ra kiểu định d ng c a b n VD nh t o m t t p tin đuôi ch m .level để l u l i c p đ trò chơi c a mình. Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 105 - Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 V y t p tin này lúc nào cǜng ph i nằm cùng th m c ch a ch ơng trình thực thi .exe sao? Cǜng không luôn. T p tin này có thể đ c đ t trong th m c con: C code: taptin = fopen("folder/test.txt", "r+"); Bây gi t p tin test.txt đ c đ t trong th m c con tên là folder.Ph ơng pháp này đ c gọi là “đ ng d n t ơng đối”, và th ng đ c sử d ng r ng rãi. V i cách này, ch ơng trình c a b n s ít bị g p lỗi hơn. Chúng ta cǜng có thể m m t t p tin b t kỳ đâu đó trong ổ c ng máy tính. Trong tr ng h p này b n ph i vi t đ ng d n m t cách chính xác và đầy đ (ta có thể gọi là đ ng d n tuyệt đối): C code: taptin = fopen("C:\\Program Files\\Notepad++\\readme.txt", "r+"); Đo n code này s m các t p tin readme.txt nằm trong C:\Program Files\Notepad++ Tôi đư dùng 2 d u \\ mỗi lần r nhánh th m c nh b n th y. N u tôi chỉ dùng m t d u \ máy tính tính s hiểu nhầm rằng b n đang thêm vào m t ký tự đ c biệt nh \n ho c \t. Để vi t m t d u \ trong chuỗi ký tự, b n ph i vi t nó 2 lần (vi t là \\), lúc này máy tính s hiểu rằng b n muốn sử d ng m t d u \ này. Có m t nh c điểm là những đ ng d n tuyệt đối có thể chỉ ho t đ ng trên m t hệ điều hành c thể. Đây không ph i là m t gi i pháp linh đ ng. Chẳng h n nh cǜng là m t đ ng d n nh ng trên Linux b n ph i vi t nh sau: C code: taptin = fopen("/home/Minh/folder/readme.txt", "r+"); Tôi khuyên b n nên sử d ng đ ng d n t ơng đối thay vì đ ng d n tuyệt đối. Đ ng sử d ng đ ng d n tuyệt đối n u ch ơng trình c a b n đ c vi t riêng cho m t hệ điều hành nào đó và các t p tin c a b n cǜng nên đ c l u m t th m c c thể trong ổ c ng c a máy tính. Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 106 - Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 Kiểm tra thao tác mở tập tin: T p tin ch a địa chỉ con trỏ taptin c a c u trúc FILE. Nó đư đ Bây gi có 2 tr   c c p phát b nh b i fopen ( ). ng h p có thể x y ra: M t p tin thành công và b n hoàn toàn có thể ti p t c thao tác v i t p tin (có thể là đọc, ho c vi t thêm vào t p tin). M t p tin th t b i do t p tin đó ch a tồn t i ho c t p tin đang đ c sử d ng b i ch ơng trình khác. Trong tr ng h p này b n không thể làm gì v i t p tin đó h t. Sau khi thực hiện thao tác m t p tin chúng ta có thể kiểm tra xem có thành công không. Cách kiểm tra r t đơn gi n thôi: n u con trỏ mang giá trị NULL, m t p tin th t b i. N u con trỏ mang m t giá trị b t kì nào khác NULL, m t p tin thành công. Chúng ta s làm nh sau để kiểm tra: C code: int main (int argc, char *argv[ ]) { FILE* taptin = NULL; taptin = fopen("test.txt", "r+"); if (taptin != NULL) { // khac gia tri NULL thi ban co the doc va ghi du lieu vao tap tin roi } else { // Ban co the cho no hien thi thong bao loi neu thich printf ("Khong the mo tap tin test.txt"); } return 0; } Hưy làm nh trên mỗi khi muốn m t p tin. N u b n không làm ho c t p tin không tồn t i, ch ơng trình s g p lỗi. Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 107 - Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 fclose: đóng một tập tin N u t p tin đ c m thành công thì b n có thể đọc và ghi thêm dữ liệu vào n i dung c a nó (chút nữa thôi chúng ta s th y cách làm). M t khi b n đư xong việc v i t p tin thì b n ph i đóng nó l i đúng không? Chúng ta s thực hiện thao tác này bằng fclose, việc này có vai trò giúp gi i phóng b nh cho máy tính, điều đó cǜng có nghĩa là những t p tin đ c n p vào RAM s đ c xóa s ch. Prototype c a fclose là: C code: int fclose(FILE* taptin); Hàm này chỉ có m t tham số: đó là con trỏ c a t p tin. Nó tr về m t giá trị kiểu int cho bi t đư đóng đ   c t p tin ch a, giá trị đó là: Giá trị bằng 0: N u t p tin đ c đóng thành công. Giá trị là EOF (End Of File): N u việc đóng t p tin th t b i. EOF đ c định nghĩa s n trong stdio.h t ơng ng v i m t số đ c biệt, giá trị này có nhiệm v thông báo cho máy tính về m t lỗi đư x y ra. Để đóng m t t p tin chúng ta s làm nh sau: C code: fclose(taptin); Tóm l i chúng ta s làm theo cách sau để m và đóng m t t p tin trong project nh sau: C code: int main (int argc, char *argv[ ]) { FILE* taptin = NULL; taptin = fopen("test.txt", "r+"); if (taptin != NULL) { // Ban co the doc va ghi du lieu vao noi dung tap tin // ... fclose(taptin); // Dong tap tin da duoc mo } return 0; } Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 108 - Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 Tôi đư không sử d ng else để hiển thị thông báo lỗi n u thao tác m t p tin th t b i. Nh ng n u muốn, tôi tin là b n bi t cách làm mà đúng không. Hãy luôn nh đóng t p tin l i mỗi khi hoàn thành công việc, điều này giúp gi i phóng b nh c a máy tính. N u b n không gi i phóng b nh cho máy tính, sau khi hoàn thành, ch ơng trình c a b n s chi m r t nhiều b nh và nó có thể không sử d ng đ c. V i những ví d nhỏ nh trên có l b n s không th y đ c sự quan trọng c a việc này nh ng trong m t ch ơng trình l n hơn, đây th t sự là m t v n đề quan trọng. Việc quên không gi i phóng b nh tr c sau gì cǜng s x y ra và b n s g p ph i m t sự cố mang tên “tràn b nh ”. Ch ơng trình c a b n s sử d ng nhiều b nh hơn cần thi t mà b n không hiểu đ c lý do t i sao. Th ng thì nguyên nhân đơn gi n chỉ vì 1 ho c 2 chi ti t nhỏ nh vì quên dùng fclose. Những phương pháp đọc/ghi dữ liệu trong tập tin: Sau khi đư bi t cách m và đóng m t t p tin, bây gi chúng ta chỉ việc thêm vào m t vài dòng code để đọc và ghi dữ liệu vào. Chúng ta s bắt đầu v i việc học cách ghi dữ liệu vào m t t p tin tr sau đó b n s học cách làm th nào để đọc t p tin. c (cǜng đơn gi n thôi) và Ghi vào một tập tin: Có m t vài hàm có ch c năng ghi dữ liệu vào t p tin, việc chọn ra cách nào thích h p nh t là ph thu c vào b n. Đây là 3 hàm mà chúng ta s học:    fputc: vi t m t ký tự vào t p tin (duy nh t mỗi lần 1 ký tự). fputs: vi t m t chuỗi vào t p tin. fprintf: vi t m t chuỗi có định d ng vào t p tin, gần giống nh hàm printf. Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 109 - Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 Hàm fputc: Hàm này s thêm vào t p tin mỗi lần 1 ký tự. Đây là prototype c a nó: C code: int fputc (int kytu, FILE* taptin); Hàm này có 2 tham số:   Tham số 1: Bi n đ i diện cho ký tự đ c vi t thêm vào (bi n đ c khai báo kiểu int, nh tôi đư t ng nói v i b n nó cǜng t ơng đ ơng khi khai báo kiểu char, khác chỗ số ký tự có thể sử d ng đây nhiều hơn). VD b n có thể vi t trực ti p ký tự ‘A’. Tham số 2: Con trỏ đ n t p tin để vi t. theo nh vd c a chúng ta con trỏ tên là taptin. L i th c a việc gọi con trỏ taptin mỗi lần cần sử d ng là có thể m nhiều t p tin cùng lúc và nh v y b n có thể đọc và vi t thêm vào mỗi t p tin. B n không bị gi i h n ph i m 1 t p tin t i 1 th i điểm. Hàm này tr về m t giá trị int, t ơng đ ơng v i giá trị c a ký tự đ c thêm vào. Giá trị int này s là EOF thể hiện cho 1 lỗi, n u hàm tr về 1 giá trị khác EOF thì mọi th v n bình th ng. Giống nh khi thao tác m t p tin thành công, tôi không th ng kiểm tra xem fputc có thực hiện tốt nhiệm v c a nó không, nh ng n u b n muốn thì b n c việc kiểm tra l i. Đo n code sau đây s thêm ký tự ‘A’ trong test.txt (n u t p tin đư tồn t i nó s đ c thay th , n u ch a thì nó s đ c t o ra). Có đầy đ mọi th trong đo n code bên d i, m t p tin, ghi thêm dữ liệu và đóng t p tin: C code: int main (int argc, char *argv[ ]) { FILE* taptin = NULL; taptin = fopen("test.txt", "w"); if (taptin != NULL) { fputc('A', taptin); // Ghi them vao tap tin ky tu A fclose(taptin); } return 0; } Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 110 - Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 Thử m t p tin test.txt c a b n ra để kiểm tra thử xem. Th t kỳ diệu đúng không, bây gi nó đư ch a ký tự A nh hình d i: Hàm fputs: Hàm này r t giống v i hàm fputc, chỉ có m t khác biệt đó là nó s ghi vào t p tin m t chuỗi (string), khác v i fputc chỉ có thể thêm vào m t ký tự. Điều này không có nghĩa là hàm fputc tr nên vô d ng, b n s ph i sử d ng fputc trong những tr ng h p ch ơng trình muốn yêu cầu ng i dùng điền m t ký tự duy nh t (tr l i câu hỏi trắc nghiệm chẳng h n). Sau đây là prototype c a hàm: C code: char* fputs(const char* chuoi, FILE* taptin); C hai tham số c a hàm này cǜng r t đơn gi n để hiểu: Tham số 1: chuoi, đây là chuỗi đ c thêm vào t p tin. Để ý rằng tham số này có kiểu const char*: bằng cách thêm t const vào, hàm này muốn nói rằng chuỗi này s đ c xem nh m t hằng số. Tóm l i, b n s không thể sửa n i dung c a chuỗi, điều này s giúp b n hiểu là: hàm fputs chỉ đọc và thêm vào chuỗi c a b n ch nó không hề thay đổi gì n i dung chuỗi, cǜng có nghĩa là thông tin b n muốn thêm vào s đ c b o vệ an toàn. Tham số 2: taptin, t ơng tự nh trong hàm fputc, nó là con trỏ FILE* c a b n để d n đề t p tin đ c đư đ c m . Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 111 - Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 Nào bây gi chúng ta s thử thêm m t chuỗi vào t p tin: C code: int main (int argc, char *argv[ ]) { FILE* taptin = NULL; taptin = fopen("test.txt", "w"); if (taptin != NULL) { fputs("Xin chao anh chang dep trai\nBan co khoe khong ?", taptin); fclose(taptin); } return 0; } T p tin sau khi ch y dòng code trên s nh sau: Hàm fprintf: Sau đây là m t vd khác cho hàm printf. Chúng ta có thể sử d ng cách này để ghi dữ liệu vào m t t p tin. Cách sử d ng cǜng gần giống nh hàm printf, ngo i tr việc b n ph i chỉ định m t con trỏ vị trí tham số đầu tiên. Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 112 - Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C Đo n code sau đây s hỏi tuổi c a m t ng www.siteduzero.com Tác giả: M@teo21 i và ghi k t qu nh n đ c vào t p tin: C code: int main (int argc, char *argv[ ]) { FILE* taptin = NULL; int tuoi = 0; taptin = fopen("test.txt", "w"); if (taptin != NULL) { // Sau đây chúng ta s hỏi tuổi printf (" Ban bao nhieu tuoi ? "); scanf ("%d", &tuoi); // Và bây gi là ghi dữ liệu vào t p tin fprintf (taptin, "Nguoi dang su dung chiec may tinh nay %d tuoi", tuoi); fclose(taptin); } return 0; } Và k t qu khi b n m t p tin test.txt s nh hình sau: B n có thể v n d ng l i những ki n th c đư học về hàm printf để áp d ng cho việc ghi dữ liệu vào t p tin đối v i hàm fprintf. Đây cǜng là lý do t i sao tôi r t hay sử d ng fprintf mỗi lần muốn thêm dữ liệu vào t p tin. Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 113 - Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 Đọc một tập tin: Chúng ta s sử d ng l i hầu h t các hàm dùng để ghi dữ liệu trong tên c a chúng: trên, chỉ có m t chút thay đổi 1. fgetc: Đọc m t ký tự 2. fgets: Đọc m t chuỗi 3. fscanf: Đọc m t chuỗi có định d ng Lần này tôi s đi nhanh hơn m t chút trong việc gi i thích về những hàm này. N u b n đư hiểu những gì tôi nói trên về việc ghi dữ liệu vào t p tin thì những ki n th c này chỉ là chuyện nhỏ. Hàm fgetc: Prototype là: C code: int fgetc(FILE* taptin); Hàm s tr về m t giá trị int, có nghĩa là ký tự đó đư đ đ c, hàm s tr về EOF. c đọc. Ng c l i n u không thể đọc Nh ng làm sao ta bi t đ c mình s đọc ký tự nào? Chẳng h n nh b n muốn đọc ký tự th 3 ho c th 10 thì làm sao để đọc? Th t ra trong thực t , khi b n đọc m t t p tin, s xu t hiện m t “d u nháy o”. D u nháy này s không hiển thị lên màn hình cho b n th y. B n có thể t ng t ng nó giống nh d u nháy khi b n chỉnh sửa n i dung văn b n trên Notepad ho c Microsoft Word. Nó có nhiệm v chỉ ra b n đang đâu trong t p tin. Chúng ta s th y ngay sau đây d u nháy o nằm vị trí nào trong t p tin và làm th nào để thay đổi vị trí d u nháy đó.(chuyển nó về đầu t p tin ho c m t vị trí c thể nào đó c a ký tự, vd nh vị trí c a ký tự th 10). Mỗi lần b n dùng fgetc để đọc m t ký tự thì hàm s gửi “d u nháy o” vào đó. N u b n gọi hàm này l i m t lần nữa, thì hàm s ti p t c t vị trí đó đọc ti p ký tự th 2, th 3 và c th đọc ti p. B n có thể t o ra m t vòng l p (loop) để lần l t đọc t ng ký tự trong t p tin. Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 114 - Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 Chúng ta s vi t m t đo n code để lần l t đọc t t c các ký tự trong t p tin, đồng th i in ra màn hình những ký tự đọc đ c. Vòng l p s ng ng l i khi hàm fgetc tr về giá trị EOF (End Of File, nhằm thông báo cho máy tính là “k t thúc m t t p tin”): C Code: int main (int argc, char *argv[ ]) { FILE* taptin = NULL; int kytuHientai = 0; taptin = fopen("test.txt", "r"); if (taptin != NULL) { // Vong lap lan luot doc tung ky tu do { kytuHientai = fgetc(taptin); // Doc ky tu printf ("%c", kytuHientai); // In ky tu do ra man hinh } while (kytuHientai != EOF); // fgetc tiep tuc duoc goi lai vi bien kytuHientai khac EOF fclose(taptin); } return 0; } Màn hình console s hiển thị toàn b n i dung t p tin, VD: Console: Xin chao, day la noi dung cua tap tin test.txt ! Hàm fgets: Hàm này s đọc m t chuỗi trong t p tin. Nó s giúp b n ti t kiệm th i gian thay vì ph i đọc t ng ký tự m t. Hàm s đọc m t chuỗi trên m t dòng (nó s d ng l i khi g p ký tự xuống dòng \n). N u b n muốn đọc nhiều dòng thì hãy t o ra m t vòng l p. Sau đây là prototype c a hàm fgets: C code: char* fgets(char* chuoi, int soKytuDuocdoc, FILE* taptin); Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 115 - Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 Hàm này có ch a m t tham số hơi kỳ qu c, nh ng nó thực sự s r t hữu ích: soKytuDuocdoc. Nó s thông báo cho hàm fgets ng ng đọc dòng n i dung n u v t quá số l ng ký tự t ơng ng. L i ích: Nó s b o đ m rằng chúng ta s không rơi vào tình tr ng “tràn b nh ”. Th t v y, n u m t dòng c a b n quá l n so v i chuỗi, hàm có thể s ph i đọc nhiều ký tự hơn. Điều này có thể gây ra lỗi cho ch ơng trình. Đầu tiên chúng ta s xem làm th nào để đọc n i dung trên m t dòng v i hàm fgets (chúng ta cǜng s bi t cách để đọc toàn b t p tin). Để thực hiện điều này, chúng ta s t o ra m t chuỗi đ l n để ch a n i dung c a dòng mà ta s đọc (ít nh t là v y, vì ta không thể chắc chắn đ c 100% n i dung dòng đó là bao nhiêu). B n s th y l i ích c a việc sử d ng định nghĩa (define) để xác định tr c kích th c c a m ng (array): C code: #define SO_KY_TU_TOI_DA 1000 // Kich thuoc cua mang la 1000 int main (int argc, char *argv[ ]) { FILE* taptin = NULL; char chuoi[SO_KY_TU_TOI_DA] = ""; // Chuoi co kich thuoc bang SO_KY_TU_TOI_DA taptin = fopen("test.txt", "r"); if (taptin != NULL) { fgets (chuoi, SO_KY_TU_TOI_DA, taptin); /* Co toi da SO_KY_TU_TOI_DA trong tap tin duoc doc, chung duoc luu tru vao "chuoi" */ printf ("%s", chuoi); // Hien thi chuoi len man hinh fclose (taptin); } return 0; } K t qu v n giống nh code c a hàm fgetc mà chúng ta đư th y tr hiển thị trên console: c đó, đây là những n i dung Console: Xin chao, day la noi dung cua tap tin test.txt ! Sự khác biệt đây là chúng ta không sử d ng vòng l p. Nó hiển thị toàn b chuỗi trong m t lần. Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 116 - Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 Chắc hẳn là b n đư th y những l i ích c a #define trong những dòng code trên nhằm xác định kích th c cho m ng, VD nh SO_KY_TU_TOI_DA đư đ c sử d ng 2 lần trong đo n code:   M t lần để xác định kích th c cho m ng khi đ c kh i t o. Và lần th hai trong hàm fgets nhằm gi i h n số ký tự tối đa s đ c đọc. L i ích c a việc này là n u b n th y rằng chuỗi ch a đ l n để đọc h t n i dung c a m t dòng trong t p tin, b n chỉ cần thay đổi giá trị trên dòng định nghĩa (dòng ch a #define) và sau đó biên dịch l i ch ơng trình. Điều này giúp b n ti t kiệm th i gian vì không cần ph i đọc l i toàn b code để tìm chỗ thay đổi kích th c c a m ng. Tiền xử lý (preprocessor) s thay th toàn b giá trị cǜ c a SO_KY_TU_TOI_DA bằng giá trị m i mà b n muốn. Nh tôi đư nói, hàm fgets đọc n i dung trên m t dòng. Nó s ng ng đọc dòng đó n u v ký tự tối đa đ c đọc do b n quy định. t quá số Nh ng câu hỏi là: Bây gi nó chỉ đọc đ c mỗi lần m t dòng v y thì làm th quái nào để ta có thể đọc đ c toàn b n i dung c a t p tin? Câu tr l i vô cùng đơn gi n: Vòng l p (loop) Hàm fgets s tr về giá trị NULL n u không đọc đ Các vòng l p s ng ng tr c n i dung mà b n yêu cầu. c khi fgets tr về giá trị NULL. Cần thêm vào m t th để fgets không tr về NULL: C code: #define SO_KY_TU_TOI_DA 1000 int main (int argc, char *argv[ ]) { FILE* taptin = NULL; char chuoi[SO_KY_TU_TOI_DA] = ""; taptin = fopen("test.txt", "r"); if (taptin != NULL) { while (fgets(chuoi, SO_KY_TU_TOI_DA, taptin) != NULL); /* Cu viec doc noi dung tap tin mien sao khong xuat hien loi (NULL)*/ printf ("%s", chuoi); // Hien thi noi dung doc duoc len man hinh fclose(taptin); } return 0; } Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 117 - Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 Đo n code trên s đọc toàn b n i dung c a t p tin, t ng dòng m t. Đây là những dòng thú vị nh t c a đo n code, dòng code có sử d ng vòng l p while: C code: while (fgets (chuoi, SO_KY_TU_TOI_DA, taptin) != NULL); Dòng code trên thực hiện 2 việc: Nó s đọc n i dung c a m t dòng trong t p tin và kiểm tra xem fgets có tr về giá trị NULL hay không. Có thể hiểu n i dung c a dòng code trên là “đọc n i dung c a m t dòng và ti p t c đọc dòng ti p theo cho t i khi k t thúc t p tin”. Hàm fscanf: V n là m t nguyên tắc ho t đ ng t ơng tự v i hàm scanf mà chúng ta đư t ng học. Hàm này đọc n i dung c a t p tin nh ng nó ph i đ c vi t m t cách chính xác. Gi sử t p tin c a b n ch a 3 số đ c phân cách bằng kho ng trắng, VD đó là những điểm số cao nh t c a m t trò chơi 15 20 30. Và b n muốn l y t ng số đó d i d ng bi n kiểu int. Hàm fscanf s giúp b n làm đ c điều đó m t cách dễ dàng: C code: int main (int argc, char *argv[ ]) { FILE* taptin = NULL; int diemso[3] = {0}; // Mang chua 3 gia tri diem so cao nhat taptin = fopen("test.txt", "r"); if (taptin != NULL) { fscanf (taptin, "%d %d %d", &diemso[0], &diemso[1], &diemso[2]); printf ("Cac diem so cao nhat la: %d, %d va %d", diemso[0], diemso[1], diemso[2]); fclose (taptin); } return 0; } Console: Cac diem so cao nhat la: 15, 20 va 30 Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 118 - Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 Nh b n s th y, hàm fscanf s nh n bi t 3 giá trị đ c phân cách nhau bằng những kho ng trắng ("%d %d %d"). Nó s đ a vào m ng c a chúng ta 3 thành phần. Sau đó b n có thể dùng printf để hiển thị mỗi giá trị nh n đ c. B n có để ý rằng tr c đây tôi chỉ đ t m t %d trong d u ngo c kép c a hàm scanf thì trong lần này v i hàm fscanf chúng ta có thể đ t m t lúc nhiều %d nh p giá trị. N u t p tin c a b n đ c vi t theo m t quy chuẩn rõ ràng thì việc thu th p các giá trị s đ c ti n hành dễ dàng hơn. Di chuyển một tập tin: Khi nưy, tôi đư nói v i b n về m t “d u nháy o” đúng không? Bây gi chúng ta s tìm hiểu chi ti t hơn để th y h t công d ng c a nó. Mỗi lần b n m m t t p tin, s có m t “d u nháy o” xu t hiện và chỉ ra vị trí hiện t i c a b n trong t p tin. B n có thể t ng t ng nó t ơng tự nh d u nháy trong các trình so n th o văn b n (Notepad ho c Microsoft Word), nó chỉ ra vị trí c a b n trong t p tin và b n s bắt đầu đọc ho c ghi thêm dữ liệu t vị trí đó. Tóm l i, “d u nháy o” cho phép b n đọc ho c ghi thêm dữ liệu vào m t t p tin t m t vị trí c thể. Có 3 hàm chúng ta cần ph i bi t: ftel: cho bi t vị trí hiện t i c a b n trong t p tin. fseek: chỉ định vị trí c a “d u nháy o” t i m t khu vực c thể. rewind: đ a “d u nháy o” về vị trí bắt đầu c a t p tin (t ơng tự, chúng ta cǜng có thể sử d ng fseek để chỉ định vị trí “d u nháy o” về vị trí bắt đầu c a t p tin). Hàm ftell: Vị trí hiện tại trong tập tin Cách sử d ng hàm này r t đơn gi n. Nó tr về giá trị c a “d u nháy o” nh m t bi n kiểu long. C code: long ftell (FILE* taptin); Giá trị đ c tr về cho bi t vị trí hiện t i c a “d u nháy o” trong t p tin. Hàm fseek: Prototype c a hàm fseek là: C code: int fseek(FILE* taptin, long vitri_chuyenden, int vitri_hientai); Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 119 - Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 Hàm fseek s di chuyển “d u nháy o” t vị trí gốc (đ c chỉ định b i bi n vitri_hientai) đ n vị trí c a m t ký tự trong t p tin (đ c chỉ định theo giá trị c a bi n vitri_chuyenden).   Giá trị c a vitri_chuyenden có thể là m t số d ơng (để d u nháy di chuyển ti n lên), đ ng im không di chuyên (giá trị bằng 0), và số âm (để di chuyển lùi l i). Giá trị kh i t o có thể là m t trong ba hằng số (constant) sau (khai báo #define nhé), xem nào: 1. SEEK_SET: chỉ ra vị trí bắt đầu c a t p tin 2. SEEK_CUR: chỉ ra vị trí hiện t i c a “d u nháy o”. 3. SEEK_END: chỉ ra vị trí k t thúc c a t p tin. Sau đây là m t vài ví d để chúng ta bi t cách làm việc v i những bi n vitri_hientai và vitri_hientai.  Đo n code sau đây s đ t “d u nháy o” vào vị trí c a ký tự th 2 sau vị trí bắt đầu t p tin: C code: fseek (taptin, 2, SEEK_SET);  Đo n code này s đ t “d u nháy o” vào vị trí c a ký tự th 4 trước vị trí hiện t i c a d u nháy: C code: fseek (taptin, -4, SEEK_CUR); L u ý là v i giá trị âm nh trên, d u nháy s di chuyển ng  c về tr c. Đo n code sau đây s đ t “d u nháy o” về vị trí cuối t p tin: C code: fseek (taptin, 0, SEEK_END); N u b n ghi thêm dữ liệu vào sau vị trí k t thúc c a t p tin, máy tính s bổ sung thêm dữ liệu đó cho t p tin c a b n (lần sau khi m l i t p tin này b n s th y những thông tin đ c bổ sung thêm vị trí cuối cùng). Nh ng n u b n đ t “d u nháy o” đầu t p tin và bắt đầu ghi thêm dữ liệu vào thì lúc này, những dữ liệu cǜ s bị ghi đè lên. Chúng ta không thể “chèn” thêm dữ liệu vào t p tin tr khi ta dùng m t hàm để l u l i những dữ liệu đ ng sau tr c khi chúng bị ghi đè lên. Nh ng làm th éo nào bi t đ Dịch giả: Mr. Hung daihung.pham@yahoo.fr c vị trí nào là - 120 - đâu để tôi tìm đ n mà đọc ho c ghi dữ liệu? Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 Điều này tùy thu c vào cách sắp x p n i dung t p tin c a b n. N u t p tin này là do b n vi t ra, thì chắc hẳn là b n ph i bi t rõ nó có c u trúc nh th nào. B i v y b n s bi t những thông tin mà b n cần nằm đâu. VD: b n sắp x p những điểm số ng i chơi vị trí 0, tên c a ng i chơi cuối cùng nằm vị trí th 50… Sau này chúng ta s làm việc v i những t p tin khổng lồ, và n u nh b n không có m t quy tắc sắp x p n i dung t p tin riêng c a mình, b n s không bi t ph i làm th nào để l y những thông tin mà mình cần đâu. Hưy nh rằng b n chính là ng i sắp x p t t c những n i dung này trong t p tin, t t c đều tùy thu c vào b n. Chẳng h n nh b n quy định:“tôi để điểm c a ng i chơi th nh t t i dòng 1, điểm c a ng i chơi th 2 t i dòng 2…” Hàm fseek có thể s không ho t đ ng tốt khi dùng nó để m những t p tin ch a n i dung d ng văn b n. Nói chung ng i ta th ng dùng nó để m các t p tin ch a n i dung d ng nhị phân. Khi m t ng i đọc ho c ghi dữ liệu trong t p tin d ng văn b n, th ng thì ký tự s đ c thay th bằng ký tự. Điểu duy nh t hữu d ng trong ch đ t p tin văn b n khi sử d ng hàm fseek là nó giúp đ a b n về vị trí đầu ho c cuối t p tin. Hàm rewind: quay về vị trí ban đầu. Cách này t ơng tự nh việc b n sử d ng fseek để đ a “d u nháy o” về vị trí 0 c a t p tin. C code: void rewind (FILE* taptin); Cách vi t hàm c a nó giống nh nguyên m u trên, không có gì để gi i thích thêm. Đổi tên và xóa tập tin: Chúng ta s k t thúc bài học kỳ này v i 2 hàm đơn gi n:   rename: đổi tên t p tin. remove: xóa t p tin. Điểm khác biệt những hàm này là chúng không yêu cầu b n sử d ng con trỏ taptin. Những hàm này chỉ cần xác định tên c a t p tin để xóa ho c đổi tên t p tin đó. Hàm rename: Đổi tên tập tin. Prototype c a hàm là: C code: int rename(const char* teCu, const char* tenMoi); Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 121 - Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 Hàm s tr về giá trị 0 n u đổi tên thành công. N u không thành công nó s tr về m t giá trị khác 0. Và sau đây là m t ví d : C code: int main (int argc, char *argv[ ]) { rename("test.txt", "test_rename.txt"); return 0; } Hàm remove: Xóa tập tin. Hàm này s xóa t p tin ngay và luôn mà không cần hỏi ý ki n hay thông báo cho b n. C code: int remove(const char* tentaptinMuonXoa); B n ph i cẩn th n khi sử d ng hàm này. Nó s xóa t p tin c a b n mà không cần xác nh n l i. T p tin s không bị đ a vào thùng rác (recycle bin) mà nó s bị xóa hoàn toàn khỏi máy tính c a b n. S không có cách nào khôi ph c l i t p tin đó (tr khi b n dùng th thu t đ c biệt để khôi ph c l i t p tin, nh ng th ng thì r t khó thành công). Và hàm remove này cǜng s k t thúc bài học đúng theo công năng c a nó. Chúng ta đư t o ra t p tin test.txt và bây gi tự tay mình s xóa nó: C code: int main (int argc, char *argv[ ]) { remove("test.txt"); return 0; } K t thúc bài học! Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 122 - Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 Bài 8: Cấp phát động T t c những bi n mà chúng ta t ng sử d ng cho đ n bây gi đều đ c tự đ ng t o ra b i trình biên dịch ngôn ngữ C. Ph ơng pháp hoàn toàn đơn gi n dễ hiểu. Nh ng v n có m t cách th công hơn để t o ra các bi n, gọi là “c p phát đ ng”. M t trong những l i ích chính c a việc c p phát đ ng là cho phép ch ơng trình dự trữ s n m t không gian bắt bu c để l u trữ m ng trong b nh . Cho đ n bây gi , những m ng mà chúng ta t o ra đều đư đ c cố định kích th c trong code. Sau bài học này chúng ta s bi t cách làm việc v i m ng m t cách linh ho t hơn. B n bắt bu c ph i nắm rõ cách làm việc v i con trỏ để có thể hiểu đ c bài học này. N u b n v n còn lăn tăn thì tôi khuyên b n nên dành chút th i gian xem l i những ki n th c c a các bài học về con trỏ tr c khi bắt đầu. Khi b n khai báo m t bi n, có nghĩa là chúng ta đang yêu cầu máy tính c p phát b nh : C code: int number = 0; Khi ch ơng trình nh n đ c dòng code nh trên thì s x y ra những điều sau: 1. Ch ơng trình s yêu cầu hệ điều hành (Windows, Linux, Mas Os …) cho phép sử d ng b nh . 2. Hệ điều hành s ti p nh n yêu cầu và cho ch ơng trình bi t nơi nào có thể l u trữ các bi n (nó s cho ch ơng trình địa chỉ b nh mà nó dự trữ s n t tr c). 3. Khi hàm k t thúc công việc c a nó thì bi n cǜng đồng th i bị xóa khỏi b nh . Ch ơng trình c a b n s nói v i hệ điều hành rằng “Tao không cần m mày n không gian b nh c a địa chỉ này nữa, c m ơn nhiều!”. Chuyện không đơn gi n là nói l i c m ơn v i hệ điều hành mà b n ph i th y rõ không ph i ai khác, chính hệ điều hành điều khiển b nh . Cho đ n th i điểm này mọi th đều diễn ra m t cách tự đ ng. Mỗi khi khai báo bi n, hệ điều hành tự đ ng đ c ch ơng trình liên hệ để yêu cầu c p phát b nh . V y chúng ta có thể làm điều này bằng tay m t cách th công không? Không ph i vì chúng ta thích tự làm khó mình v i những th ph c t p (kể c khi nó có h p d n đi nữa), mà vì có đôi khi ta bắt bu c ph i tự làm nh v y. Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 123 - Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 Trong ch ơng này chúng ta s : 1. Tìm hiểu về các ch c năng c a b nh (vâng, m t lần nữa) để bi t kích th c c a bi n s thay đổi th nào tùy thu c vào kiểu c a nó (int/float/char/double…) 2. Sau đó chúng ta đi thẳng vào v n đề chính, làm th nào để yêu cầu hệ điều hành c p phát b nh m t cách th công. Chúng ta s làm cái việc đầu bài học đư nêu ra: “c p phát đ ng”. 3. Cuối cùng, chúng ta s xem xem việc c p phát đ ng này s mang l i l i ích gì thông qua việc học cách t o ra m ng mà không bi t tr c kích th c c a nó trong ch ơng trình là bao nhiêu. Kích thước của các biến: Tùy thu c vào lo i bi n mà b n đang muốn t o ra (int/char/double/float…), b n s cần nhiều ho c ít b nh . Thực t là để l u trữ m t số ch y t -128 đ n 127 (m t bi n kiểu float), máy tính chỉ cần 1 byte trong b nh , nó th t sự r t nhỏ. Tuy nhiên v i m t bi n int, nó th s là 8 byte. ng chi m 4 byte trong b nh , còn v i m t bi n kiểu double V n đề là … không ph i lúc nào mọi th cǜng diễn ra đúng nh những gì chúng ta nói. Nó còn ph thu c vào máy tính nữa: chẳng h n nh bi n kiểu int c a b n có thể chi m 8 byte, ai mà bi t đ c? Và m c đích c a chúng ta là kiểm tra xem mỗi kiểu bi n s chi m bao nhiêu b nh trên máy tính c a b n. Có m t cách r t dễ để kiểm tra đó là dùng sizeof ( ). Không giống nh những gì chúng ta t ng học tr c đây, nó không ph i là m t hàm (function), mà là m t ch c năng cơ b n c a ngôn ngữ C, b n chỉ cần đ t đối t ng b n muốn kiểm tra kích th c vào trong c p d u ( ) để th y đ c điều b n muốn. Để bi t kích th c c a m t bi n kiểu int chúng ta s làm nh sau: C code: sizeof(int) T i th i điểm bắt đầu biên dịch, nó s đ c thay th b i những con số: đó là số b nh mà m t bi n kiểu int s chi m trong b nh máy tính c a b n, sizeof (int) là 4, nghĩa là nó chi m 4 byte trong b nh . Theo lý thuy t thì nó cǜng s có giá trị t ơng tự nh ng đây không ph i là m t quy lu t không đổi. Chúng ta cùng kiểm tra bằng cách dùng printf để hiển thị giá trị, Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 124 - Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 VD: C code: printf ("char : %d byte\n", sizeof(char)); printf ("int : %d byte\n", sizeof(int)); printf ("long : %d byte\n", sizeof(long)); printf ("double : %d byte\n", sizeof(double)); Màn hình console s hiển thị: char: 1 byte int: 4 byte dài: 4 byte Double: 8 byte Tôi đư không kiểm tra t t c các kiểu bi n mà chúng ta đư bi t. Tôi nh kiểm tra kích th c các kiểu bi n khác. B n s nh n th y rằng kiểu long và int chi m cùng m t dung l kiểu long cǜng s chi m 4 byte nh khi t o m t bi n int. ng phần đó cho b n để ng b nh . Việc t o m t bi n Th t ra thì kiểu long chính là long int, t ơng tự nh kiểu int. Đơn gi n là nó cǜng chỉ t o ra thêm m t cái tên m i ch không có gì nhiều, chỉ v y thôi! Tr c đây khi b nh máy tính v n ch a tốt nh bây gi thì những cái tên khác nhau c a kiểu bi n thực sự r t hữu d ng cho máy tính c a chúng ta. Các l p trình viên ngày tr c đư luôn ph i suy nghĩ chọn kiểu bi n phù h p nh t để ti t kiệm tối đa b nh . Ngày nay thì dung l ng b nh máy tính thực sự đư r t l n và v n đề này đư không còn quá quan trọng. Nh ng v n có những ng i thích t o ra những ch ơng trình chi m ít b nh nh t có thể. Tôi nghĩ rằng đó là các ch ơng trình cho điện tho i di đ ng, robot … V y chúng ta có thể bi t kích th c c a những kiểu bi n tùy chỉnh do mình t o ra (chẳng h n nh đối v i “c u trúc” ậ structure) Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 125 - Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 Câu tr l i là có! sizeof ( ) cǜng ho t đ ng v i c u trúc (structure)! C code: typedef struct Toado Toado; struct Toado { int x; int y; }; int main (int argc, char *argv[ ]) { printf ("Toado : %d byte\n", sizeof(Toado)); return 0; } Console: Toado : 8 byte V i những c u trúc ch a nhiều bi n thành phần thì s chi m nhiều b nh hơn. Quá là h p lý luôn đúng không ? Một cách mới để kiểm tra. Cho t i th i điểm này thì mô hình b nh c a tôi v n ch a đ c rõ ràng lắm. Và bây gi chúng ta s làm rõ mọi th , cuối cùng thì ta cǜng có thể bi t chính xác kích th c c a t ng lo i bi n. N u b n khai báo m t bi n kiểu int: C code: int number = 18; … và sizeof (int) đư chỉ ra rằng chúng ta s m byte này c a b nh . Dịch giả: Mr. Hung daihung.pham@yahoo.fr n 4 byte c a máy tính, sau đó bi n s chi m 4 - 126 - Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C Gi sử bi n đ hơn: www.siteduzero.com Tác giả: M@teo21 c c p phát cho địa chỉ 1600 trong b nh . Chúng ta cùng xem hình sau để th y rõ B n th y rõ ràng đây là bi n kiểu int chi m 4 byte trong b nh . Nó bắt đầu t địa chỉ 1600 (địa chỉ mà bi n đ c chỉ định lúc đầu) và k t thúc địa chỉ 1603. Bi n ti p theo s không thể l u vào địa chỉ nào trong 4 địa chỉ trên, nó s đ c bắt đầu t 1604. Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 127 - Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 N u chúng ta làm điều t ơng tự v i m t bi n kiểu char, bi n s chỉ chi m m t byte trong b nh nh hình sau: Hưy thử t ng t ng chúng ta có m t m ng các bi n kiểu int. Mỗi m t ô trong m ng s chi m 4 byte. V y n u m ng c a chúng ta có 100 ô thì sao: C code: int mang[100]; V y chính xác là chúng ta s chi m 4 x 100 = 400 byte trong b nh . V y th m chí m ng không ch a gì trong đó thì nó v n chi m tr c 400 byte b nh sao? Dĩ nhiên rồi! Các không gian b nh đư đ c dự trữ s n, không có m t ch ơng trình nào khác có thể ch m vào nó (ngo i tr sự tác đ ng c a b n). M t khi bi n đư đ c kh i t o, nó s ngay l p t c chi m m t vùng trong b nh . Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 128 - Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 L u ý n u b n t o m t m ng kiểu Toado thì: C code: Toado mang[100]; … Lần này chúng ta s sử d ng 8 x 100 = 800 byte trong b nh . Điều quan trọng là b n ph i cố gắng hiểu những tính toán nhỏ c a phần sau. Cấp phát động. Nào bây gi chúng ta s tìm hiểu sâu về nó. Tôi s nhắc l i m c tiêu c a chúng ta: học cách yêu cầu c p phát b nh bằng cách th công. Chúng ta cần khai báo th viện <stdlib.h>. N u b n tin những gì tôi khuyên thì tốt nh t là nên khai báo th viện này trong t t c các ch ơng trình c a b n. Th viện này ch a 2 hàm mà chúng ta cần đ n:   malloc (“Memory allocation”, nghĩa là “c p phát b nh đ ng”): hàm s yêu cầu hệ điều hành để đ c sử d ng b nh máy tính. free (“free”, nghĩa là “gi i phóng”): hàm s gi i phóng vùng nh đư đ c hệ điều hành chỉ định cho yêu cầu c p phát b nh c a chúng ta tr c đó, t lúc này, các ch ơng trình khác có thể tự do sử d ng vùng nh đó. Mỗi khi muốn thực hiện việc c p phát b nh đ ng theo cách th công, b n nên thực hiện lần l t 3 b c sau: 1. Gọi hàm malloc để yêu cầu c p phát b nh . 2. Kiểm tra giá trị tr về c a hàm malloc để bi t hệ điều hành có c p phát b nh thành công hay không. 3. Sau khi sử d ng xong b n ph i ti n hành gi i phóng b nh bằng hàm free. N u chúng ta không làm thao tác này, ch ơng trình c a b n s dễ g p ph i v n đề tràn b nh , khi đ c hoàn t t ch ơng trình c a b n s chi m m t dung l ng b nh khổng lồ mà trong đó có những vùng nh bị sử d ng không cần thi t. Ba b c này có g i l i cho b n những gì đư học về thao tác v i t p tin không? Có đ y! Về nguyên tắc, nó gần nh giống v i những gì b n đư học khi thao tác v i các t p tin: đầu tiên nó s đ c c p phát b t nh , sau đó kiểm tra xem việc c p phát có thành công không, và khi đư đ c c p phát nó s sử d ng vùng b nh đó, sử d ng xong thì gi i phóng vùng nh cho ch ơng trình khác có thể sử d ng ti p. Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 129 - Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 Hàm malloc: Yêu cầu cấp phát bộ nhớ. Đây là prototype c a hàm malloc, nó trông khá ng : C code: void* malloc(size_t soByteCansudung); Hàm chỉ cần 1 tham số: đó là số byte cần sử d ng. Vì v y, chỉ cần sử d ng sizeof ( int) trong tham số này để dự trữ đ không gian l u trữ m t bi n kiểu int. Nh ng hình nh có gì đó hơi l về giá trị tr về c a hàm: nó tr về 1 void* …! N u các b n còn nh về những gì đư học bài học về hàm (function), tôi đư t ng nói v i b n void có nghĩa là tr về “không gì c ”, và chúng ta sử d ng kiểu void này để chỉ định hàm này s không tr về giá trị nào h t. V y đây chúng ta có 1 hàm s tr về “con trỏ rỗng” sao? Điều này có ổn không? Xem ra các l p trình viên khá hài h c nhỉ. B n c bình tĩnh, có lý do h t đ y. Thực t là, hàm này s tr về m t con trỏ đ n địa chỉ mà hệ điều hành đư c p phát cho bi n c a b n. N u hệ điều hành đư c p cho bi n c a b n ô nh địa chỉ 1600 thì nó s tr về giá trị con trỏ t ơng ng v i địa chỉ 1600. V n đề là hàm malloc không bi t đ c kiểu bi n mà b n muốn t o. Thực t là b n đ a cho hàm m t tham số: số l ng byte cần sử d ng trong b nh . N u nó là 4 byte, nó có thể là bi n kiểu int ho c long int chẳng h n. Vì malloc không bi t nên tr về giá trị kiểu nào, nó tr về kiểu void*. Nó s là m t con trỏ đ n b t kỳ kiểu nào cǜng đ c. Chúng ta cǜng có thể xem nó là m t con trỏ linh đ ng. Nào chúng ta thực hành thôi. N u tôi muốn vui v m t chút (e hèm!), tôi s tự mình t o m t bi n kiểu int trong b nh , tôi đang đề c p đ n việc sử d ng malloc v i sizeof (int) byte trong b nh . Tôi nh n đ c k t qu c a malloc là con trỏ đ n int. C code: int* capphatBonho = NULL; // Tao mot con tro kieu int capphatBonho = malloc(sizeof(int)); // Nhung dia chỉ danh cho con tro da duoc danh rieng K t thúc đo n code trên, capphatBonho là m t con đang ch a địa chỉ mà b n muốn hệ điều hành dự trữ s n vùng nh cho b n, vd nh địa chỉ 1600. Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 130 - Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 Kiểm tra con trỏ. Hàm malloc đư gọi m t bi n capphatBonho để ch a địa chỉ con trỏ mà hệ điều hành dành riêng cho b n. Có 2 tr ng h p có thể x y ra:   N u việc c p phát thành công, con trỏ c a chúng ta s mang giá trị ng v i địa chỉ đư đ c máy tính chỉ định. N u việc c p phát không thành công, con trỏ s mang giá trị NULL. Việc c p phát b nh th t b i r t hi m th y nh ng nó có x y ra, chẳng h n b n yêu cầu 34GB b nh RAM, khó mà yêu cầu máy tính đáp ng cho b n đ c. Chúng ta s g p m t hàm cơ b n mà b n ch a t ng th y những bài tr c: exit ( ). Nó s ng ng ch ơng trình ngay l p t c. Nó cần m t tham số: đó chính là giá trị mà ch ơng trình ph i tr về (chính xác hơn thì đây là giá trị tr về c a hàm main ( )). C code: int main (int argc, char *argv[ ]) { int* capphatBonho = NULL; capphatBonho = malloc(sizeof(int)); if (capphatBonho == NULL) // Neu viec cap phat bo nho ko thanh cong { exit(0); // Chung ta se ngung chuong trinh ngay lap tuc } // Chuong trinh se tiep tuc hoat dong neu moi viec dien ra thuan loi return 0; } N u giá trị con trỏ khác NULL, ch ơng trình có thể ti p t c ho t đ ng, ng c l i nó s hiển thị thông báo lỗi ho c ng ng ch ơng trình ngay l p t c. Nguyên nhân có thể là do dung l ng b nh b n yêu cầu v t quá kh năng c a máy tính. Hàm free: Giải phóng bộ nhớ. Cǜng giống nh khi ta sử d ng fclose để đóng t p tin khi không còn sử d ng đ n, chúng ta s sử d ng hàm free để gi i phóng những vùng b nh không còn cần sử d ng nữa. C code: void free(void* contro); Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 131 - Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 Chúng ta cần hàm free để giúp gi i phóng các vùng địa chỉ c a b nh . Vì v y chúng ta s gửi cho nó tham số là những con trỏ, VD nh con trỏ capphatBonho trong ví d lúc nưy. Sau đây là toàn b ch ơng trình c a chúng ta t đầu đ n gi . Nhìn chung thì cách ho t đ ng cǜng không có quá nhiều điểm khác biệt so v i những gì ta đư học khi thao tác v i t p tin. C code: int main (int argc, char *argv[ ]) { int* capphatBonho = NULL; capphatBonho = malloc(sizeof(int)); if (capphatBonho == NULL) // chung ta kiem tra xem bo nho da duoc cap phat chua { exit(0); // Error: co loi va chuong trinh se bi ngung ngay lap tuc } // Bo nho da duoc cap phat va san sang de su dung free(capphatBonho); // Chung ta ko can su dung bo nho nua, giai phong no thoi return 0; } Ví dụ cụ thể. Chúng ta s sắp x p l i m t số ki n th c b n đư đ in nó ra. c học tr c đây: Hỏi tuổi c a ng i dùng và Điều khác biệt duy nh t so v i những gì chúng ta đư t ng làm tr c đó là lần này các bi n s đ c phân bổ m t cách th công (còn gọi là c p phát đ ng) ch không ph i tự đ ng nh tr c đây. Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 132 - Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 Vì v y cho nên những dòng code nhìn s ph c t p hơn. Nh ng các b n hưy cố gắng hiểu đ đi, điều này th t sự quan trọng đ y: c nó C code: int main (int argc, char *argv[ ]) { int* capphatBonho = NULL; capphatBonho = malloc(sizeof(int)); // Cap phat bo nho if (capphaBonho == NULL) { exit(0); } // Su dung bo nho printf ("Ban bao nhieu tuoi ? "); scanf ("%d", capphatBonho); printf ("Ban %d tuoi\n", *capphatBonho); free(capphatBonho); // Giai phong bo nho return 0; } Console: Ban bao nhieu tuoi ? 69 Ban 69 tuoi Hưy l u ý: capphatBonho là m t con trỏ, cách sử d ng nó khác v i cách b n làm việc v i m t bi n bình th ng. Để có đ c giá trị c a con trỏ, b n ph i đ t d u * tr c capphatBonho (xem l i dòng code c a printf để th y rõ hơn). Trong khi đó, n u muốn chỉ ra địa chỉ c a con trỏ thì chỉ cần vi t tên c a nó capphatBonho (xem dòng scanf nhé). Những điều này b n đư đ c học trong bài về con trỏ rồi. Tuy nhiên, cần ph i có m t th i gian nh t định để b n có thể quen v i con trỏ, và kh năng nhầm l n v n r t cao. Trong tr ng h p này b n nên đọc l i m t lần bài học về con trỏ, đây là những ki n th c cơ b n r t quan trọng. Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 133 - Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 Tr l i v i đo n code c a chúng ta. Bi n kiểu int đư đ c c p phát đ ng. Suy cho cùng những gì b n đư vi t cǜng giống nh tr c đây khi ti n trình này diễn ra m t cách tự đ ng: C code: int main (int argc, char *argv[ ]) { int bienCuatoi = 0; // Cap phat bo nho (qua trinh dien ra hoan toan tu dong) // Su dung bo nho printf ("Ban bao nhieu tuoi ? "); scanf ("%d", &bienCuatoi); printf ("Ban %d tuoi\n", bienCuatoi); return 0; } // Giai phong bo nho (dien ra tu dong moi khi ket thuc ham) Tóm l i có 2 cách để t o m t bi n, hay có thể nói là yêu cầu máy tính c p phát b nh . Hai cách đó là:   Tự đ ng: Đây là những gì b n đư t ng học tr c đây mỗi khi t o ra m t bi n. Th công (c p phát đ ng): Cách mà tôi d y cho b n trong bài học này. Tôi không hiểu sao chúng ta ph i học cái cách ph c t p này và liệu có cần thi t không? Đúng là có ph c t p hơn m t chút … nh ng nó có vô d ng không? Không! Đôi khi chúng ta bu c ph i tự mình phân bổ b nh , tôi s cho b n th y m t tr ng h p ngay sau đây. Cấp phát động trong mảng. T i bây gi chúng ta chỉ sử d ng ph ơng pháp “c p phát đ ng” để t o những bi n bình th ng đơn gi n. Nh ng nhìn chung mọi ng i v n không th ng dùng cách này, ph ơng pháp tự đ ng rõ ràng là đơn gi n hơn nhiều. Có ph i b n đang tự hỏi, v y thì khi nào chúng ta nên dùng ph ơng pháp này? Th ng thì ng i ta sử d ng ph ơng pháp “c p phát đ ng” khi t o ra m t m ng (array) mà ta không bi t tr c kích th c c a nó khi ch y ch ơng trình là bao nhiêu. Ví d , t ng t ng b n s t o m t ch ơng trình l u trữ tuổi những ng m ng. B n có thể m t m ng kiểu int để l u trữ tuổi c a họ, nh sau: i b n c a b n vào m t C code: int tuoiBanbe[15]; Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 134 - Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 Nh ng ai dám đ m b o v i b n rằng ng i dùng chỉ có 15 ng i b n? Có thể nhiều hơn ch . Khi vi t ch ơng trình, b n không thể nào bi t tr c đ c kích th c nào là phù h p cho m ng c a b n. B n chỉ có thể bi t khi ch ơng trình đư ch y, và b n ph i hỏi ng i dùng có bao nhiêu b n bè. L i ích c a “c p phát đ ng” là đó: Chúng ta s hỏi số l ng b n bè c a ng i dùng tr c, và sau đó “c p phát đ ng” s t o ra m ng v i kích th c chính xác nh đ c yêu cầu (không quá nhỏ mà cǜng không quá l n). N u ng i dùng có 15 ng i b n, chúng ta s t o m t m ng v i 15 giá trị int, t ơng tự n u họ có 28 ng i b n, nó s t o ra m t m ng v i 28 giá trị int, … Nh tôi đư t ng d y b n, ngôn ngữ C không thể t o ra m t m ng v i kích th bằng m t bi n: cđ c chỉ định C code: int banbe[soluongBanbe]; Đo n code trên có thể s ho t đ ng v i m t số trình biên dịch trong m t số tr thể, nh ng nó đ c khuy n cáo là không nên sử d ng nh v y. ng h p c L i ích c a “c p phát đ ng” là nó cho phép ta t o ra m t m ng có kích th c kh p v i số l ng b n bè nh bi n soluongBanbe, dựa vào đó đo n code có thể ho t đ ng b t kỳ đâu, v i b t kỳ trình biên dịch nào. Chúng ta s dùng hàm malloc để yêu cầu c p phát soluongBanbe * sizeof(int) byte trong b nh : C code: banbe = malloc(soluongBanbe * sizeof(int)); Đo n code này s giúp ta t o ra m t m ng kiểu int v i kích th c a ng i dùng. Sau đây là những gì mà ch ơng trình s lần l 1. Yêu cầu ng c đúng theo nh số l ng b n bè t thực hiện: i dùng cho bi t họ có bao nhiêu b n bè. 2. T o ra m t m ng có kích th c bằng v i số l ng b n bè c a ng i dùng (bằng cách sử d ng malloc) 3. Lần l t hỏi tuổi c a t ng ng 4. Hiển thị t t c tuổi đư đ i b n và l u trữ vào trong m ng. c l u vào m ng tr c đó. 5. Cuối cùng, vì chúng ta không còn cần đ n m ng tuổi c a những ng i b n này nữa, ta s gi i phóng b nh bằng hàm free. Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 135 - Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 C code: int main (int argc, char *argv[ ]) { int soluongBanbe = 0, i = 0; int* tuoiBanbe = NULL; // con tro nay se duoc su dung nhu mot mang sau khi dung malloc // Chung ta se yeu cau nguoi dung cho biet so luong ban be cua ho printf ("Ban co bao nhieu nguoi ban ? "); scanf ("%d", &soluongBanbe); if (soluongBanbe > 0) // Phai co it nhat mot nguoi ban (xin chia buon neu ko co ai ^^!) { tuoiBanbe = malloc(soluongBanbe * sizeof(int)); // Phan phoi bo nho cho mang if (tuoiBanbe == NULL) // Kiem tra xem viec cap phat bo nho co thanh cong ko? { exit(0); // Chuong trinh ngung lai ngay lap tuc } // Yeu cau nhap tuoi tung nguoi ban for (i = 0 ; i < soluongBanbe ; i++) { printf ("Nguoi ban thu %d bao nhieu tuoi ? ", i + 1); scanf ("%d", &tuoiBanbe[i]); } // Lan luot hien thi tuoi cua ban be printf ("\n\nTuoi cua ban be ban la :\n"); for (i = 0 ; i < soluongBanbe ; i++) { printf ("%d tuoi\n", tuoiBanbe[i]); } // Giai phong bo nho da duoc cap phat cho mang boi malloc, no ko con can thiet nua. free(tuoiBanbe); } return 0; } Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 136 - Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 Console: Ban co bao nhieu nguoi ban ? 5 Nguoi ban thu 1 bao nhieu tuoi ? 18 Nguoi ban thu 2 bao nhieu tuoi ? 19 Nguoi ban thu 3 bao nhieu tuoi ? 20 Nguoi ban thu 4 bao nhieu tuoi ? 21 Nguoi ban thu 5 bao nhieu tuoi ? 22 Tuoi cua ban be ban la : 18 tuoi 19 tuoi 20 tuoi 21 tuoi 22 tuoi Ch ơng trình có v không ng d ng đ c gì nhiều trong thực t nh ng tôi chọn làm nó vì nó khá đơn gi n để các b n có thể hiểu đ c cách làm việc c a hàm malloc. Tôi đ m b o v i b n rằng những bài học sau, chúng ta s có cơ h i sử d ng hàm malloc để l u trữ những th thú vị hơn là tuổi c a b n bè ng i dùng.     Tổng kết: M t bi n s chi m không gian b nh nhiều hay ít là tùy thu c vào kiểu c a nó (int hay double hay char …) Ng i ta có thể bi t đ c số byte mà kiểu bi n đó s chi m trong b nh bằng cách sử d ng sizeof ( ). C p phát đ ng là hành đ ng dự trữ vùng nh cho bi n ho c m ng. Việc phân bổ b nh đ c thực hiện v i hàm malloc ( ) và điều quan trọng đ ng nên quên đó là hưy nh gi i phóng b nh bằng hàm free ( ) ngay sau khi b n không cần đ n  vùng nh đó nữa. Đ c biệt c p phát đ ng cho phép t o ra m ng có kích th cđ c xác định b i m t bi n trong khi ch y ch ơng trình. Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 137 - Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 Bài 9: Test Program Trò chơi người treo cổ Chỉ nói ko thôi thì ch a đ : Chắc hẳn là các b n hiểu rõ tầm quan trọng c a việc thực hành đúng không? Tôi chắc chắn việc thực hành th t sự cần thi t đối v i b n, chúng ta v a đ c học r t nhiều những khái niệm, lý thuy t … và b t c điều gì b n đ c đọc, những gì b n nói ra, b n s không bao gi thực sự hiểu sâu về nó cho đ n khi b n bắt tay vào thực hành. Trong lần học này, tôi đề nghị chúng ta s làm ra trò chơi “Ng i treo cổ”. Đây là m t trò chơi cổ điển quen thu c về các t (b n nào xài kim t điển thì bi t ngay trò này), b n s ph i đoán những chữ cái bị ẩn trong các t . Trò ng i treo cổ lần này s đ c chơi theo kiểu c a ngôn ngữ C trên màn hình console. M c đích chính c a chúng ta là giúp b n có thể nắm vững t t c những ki n th c đư đ c học tr c gi . Những con trỏ (pointer), chuỗi ký tự (string), t p tin (file), m ng (array), c u trúc (structure)… ok, t t c s ổn thôi. Một số chỉ dẫn. Tôi muốn nói m t chút về những nguyên tắc ho t đ ng c a trò “Ng i treo cổ”. Bây gi tôi s đ a cho b n m t số chỉ d n, đồng th i tôi nghĩ sau này b n cǜng nên gi i thích cho ng i khác bi t cách ho t đ ng c a trò ch i mà b n t o ra. Đa phần chúng ta đều đư t ng bi t qua trò “ng i treo cổ” rồi đúng ko? Nh ng mà bây gi nhắc l i m t chút về cách chơi cǜng ch m t gì đúng ko: M c tiêu c a trò chơi là tìm ra t bị ẩn sau tối đa 10 lần đoán (b n cǜng có thể tự mình thay đổi số lần đoán tối đa để tùy chỉnh đ khó). Quy cách chơi. Gi sử t khóa bị ẩn là RED. B n đoán chữ A và máy tính s kiểm tra xem chữ A có nằm trong t đang bị ẩn ko. Hãy nh là có s n m t hàm trong th viện string.h có thể tìm m t chữ cái trong m t t ! (hàm strchr). Tuy nhiên, b n không cần sử d ng nó (b n thân tôi cǜng ít khi dùng đ n). Có 2 kh năng s x y ra:   Chữ cái b n đoán có ch a trong t bị ẩn, lúc này màn hình s hiển thị chữ mà b n tìm th y trong t bị ẩn đó. Chữ cái b n đoán không có trong t đang bị ẩn (chẳng h n nh khi nưy b n đoán chữ A nh ng t bị ẩn là RED thì làm gì có chữ A): Máy tính s thông báo cho ng i chơi là chữ v a đ c đoán không có ch a trong t đang bị ẩn và tự đ ng tr b t đi m t lần đoán c a họ. Cho t i khi chúng ta sử d ng h t t t c các l t đoán mà v n ch a tìm ra t bị ẩn thì xác định là thua cmnr!!! Đ n đây thì GAME OVER. Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 138 - Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 B n cǜng bi t trong thực t khi chơi trò này trên máy tính ho c kim t điển thì mỗi lần chúng ta đoán sai, hình nh m t ng i bị treo cổ s đ c v thêm vào 1 nét cho đ n khi b n đoán đúng t bí ẩn ho c đoán sai h t thì hình v hoàn thành (cǜng có nghĩa là bị treo cổ và thua). V i console thì chúng ta ko thi t k sinh đ ng nh v y đ c và chỉ hiển thị chữ cái đ c thôi, vì v y có gì xài n y, chúng ta s hiển thị m t câu thông báo “B n còn xxx lần đoán tr c khi ng i này bị treo cổ” mỗi khi đoán sai cho đ n khi h t l t đoán. Gi sử ng i chơi đoán chữ D (trong tr ng h p t bị ẩn là RED). Rõ ràng là chữ D đ c ch a trong t RED, vì v y số l t đoán c a ng i chơi s không bị gi m đi. Màn hình s hiển thị t bị ẩn cùng v i chữ cái mà b n v a đoán đúng, nó s trông nh th này: Console: Tu bi an: **D Và n u sau đó ng i ta đoán chữ R, l i ti p t c có chữ R, màn hình s hiển thị m t lần nữa t bí ẩn kèm theo những t b n đư đoán đúng, nh sau: Console: Tu bi an: R*D Trường hợp từ chứa nhiều ký tự giống nhau? Trong m t số t , s có thể xu t hiện 2 ho c nhiều chữ cái giống nhau. VD: có 2 chữ R trong t PROGRAM ho c có 3 chữ E trong EXCELLENT. V y điều gì s x y ra trong những tr ng h p trên? Lu t c a trò này là: n u ng i chơi đoán đúng chữ E trong t EXCELLENT thì màn hình s hiển thị c 3 chữ cùng m t lúc: Console: Tu bi an: E**E**E** Ng i chơi s không cần ph i gõ chữ E 3 lần. Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 139 - Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 Xem thử trò “người treo cổ” hoàn chỉnh. Sau đây là những gì nên đ vi t: c t o ra và b n s th y chúng xu t hiện trong trò chơi mà b n s Console: Chao mung ban tham gia tro choi NGUOI TREO CO! Ban co 10 luot doan. Tu bi an la gi? ******** Hay doan mot chu cai: Z Ban co 9 luot doan. Tu bi an la gi? ******** Hay doan mot chu cai: A Ban co 9 luot doan. Tu bi an la gi? *A****** Hay doan mot chu cai: K Ban co 9 luot doan. Tu bi an la gi? *A*****K Hay doan mot chu cai: Và c nh v y cho t i khi ng phép): i chơi đư tìm đ c t bí ẩn (ho c v t quá số l t đoán cho Console: Ban co 3 luot doan. Tu bi an la gi? FACEB**K Hay doan mot chu cai: O Xin chuc mung! Tu bi mat la: FACEBOOK Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 140 - Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 Nhập chữ cái vào màn hình console. Đọc m t chữ cái trên màn hình console có l là ph c t p hơn b n th y. Bằng chút trực giác, để có thể nh p m t chữ cái b n nên nghĩ về: C code: scanf ("%c", &chuCaiBiMat); Và thực t thì đây là m t ý t ng tốt, %c s đ c thay bằng m t chữ cái, chúng ta s l u trữ trong bi n chuCaiBiMat (t t nhiên đây ph i là m t bi n kiểu char). T t c mọi việc đang diễn ra r t tốt … miễn là chúng ta không thay đổi scanf. B n có thể thử ch y đo n mã sau: C code: int main (int argc, char* argv[ ]) { char chuCaiBiMat = 0; scanf ("%c", &chuCaiBiMat); printf ("%c", chuCaiBiMat); scanf ("%c", &chuCaiBiMat); printf ("%c", chuCaiBiMat); return 0; } Th ng thì theo những gì chúng ta đư đ c học b n s hiểu đo n code trên yêu cầu ng nh p vào m t chữ cái 2 lần sau đó in chúng ra 2 lần. i dùng Ok b n ch y thử nó đi! Oh, chuyện gì đang x y ra v y? B n nh p m t chữ cái, ok, nh ng … ch ơng trình ng ng ngay sau khi b n m i nh p chữ cái m t lần, còn m t lần nh p nữa cơ mà, ch ơng trình đư không yêu cầu b n nh p ti p chữ cái ti p theo. T i sao v y, sao nó l i bỏ qua chữ cái th 2 trong khi đo n code trên rõ ràng dùng hàm scanf 2 lần để yêu cầu nh p chữ cái 2 lần cơ mà. Chuyện gì đư x y ra? Th t ra thì, khi b n nh p m t chữ cái màn hình console, t t c mọi th b n gõ vào đư đ trữ đâu đó trong b nh , và nó cǜng có bao gồm c ký tự ENTER (\n). Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 141 - cl u Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 Vì v y khi b n nh p ký tự đầu tiên (VD: ký tự A) và b n b n ENTER, ký tự A này đ c tr về b i hàm scanf đầu tiên, sau đó scanf ti p t c tr về ký tự đ c biệt \n t ơng ng v i phím ENTER mà b n đư b m. Để tránh tình tr ng này, tốt hơn h t là hãy t o m t hàm (function) c a chúng ta docKytu( ): C code: char docKytu ( ) { char kytuNhapVao = 0; kytuNhapVao = getchar ( ); // Doc ky tu duoc nhap dau tien kytuNhapVao = toupper (kytuNhapVao); // Viet hoa ky tu do // Lan luot doc tiep cac ky tu khac cho den khi gap \n while (getchar ( ) != '\n') ; return kytuNhapVao; // Tra ve ky tu dau tien doc duoc } Hàm này sử d ng getchar ( ) đây là m t hàm cơ b n có ch a trong th viện stdio cǜng nh scanf (“%c” &kytu); Hàm getchar s đọc dữ liệu đ c nh p vào, chỉ m t ký tự t i m t th i điểm t bàn phím. Khi sử d ng hàm này, các ký tự nằm trong vùng đệm cho đ n khi ng i dùng nh n phím xuống dòng. Vì v y nó s đ i cho đ n khi phím Enter đ c gõ. Hàm getchar( ) không có tham số, nh ng v n ph i có các c p d u ngo c đơn. Nó đơn gi n là l y về ký tự ti p theo và s n sàng đ a ra cho ch ơng trình. Chúng ta nói rằng hàm này tr về m t giá trị có kiểu char. Sau đó, chúng ta sử d ng m t hàm tiêu chuẩn mà b n ch a có cơ h i đ c học những bài tr c: hàm toupper ( ) giúp vi t hoa ký tự. Bằng cách đó, trò chơi v n s ho t đ ng dù cho ng i chơi có nh p vào m t ký tự không vi t hoa. Chúng ta s ph i khai báo th viện #include ctype.h đầu để có thể sử d ng hàm này (đ ng quên nhé). Và bây gi là phần thú vị nh t: Chúng ta s xóa đi t t c những ký tự mà ng i dùng đư nh p để làm trống b nh . Việc gọi hàm getchar s l y m t ký tự ti p theo nh tôi đư gi i thích cho b n trên. Gi sử khi ng i dùng nh n Enter thì ký tự mà hàm l y vào s là \n. Những gì chúng ta ph i làm vô cùng đơn gi n: chỉ cần m t dòng, chúng ta gọi hàm getchar trong vòng l p cho t i khi g p ký tự \n thì ng ng vòng l p. Sau khi vòng l p k t thúc, nó đư đọc t i ký tự \n khi ng i dùng nh n Enter, điều đó cǜng có nghĩa là hàm getchar đư đọc h t những ký tự đ c ng i dùng nh p vào tr c đó, nh đó vùng đệm đư đ c làm trống theo đúng ph ơng th c ho t đ ng c a hàm getchar mà tôi đư nói v i b n trên. Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 142 - Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 Sao l i xu t hiện d u ch m phẩy vị trí k t thúc dòng ch a vòng l p while và chúng ta cǜng không th y c p ngo c nhọn c a vòng l p nh ta đư học các bài tr c. Nh b n đư th y, vòng l p while c a chúng không ch a những câu lệnh (instructions), nó chỉ ch a duy nh t hàm getchar trong phần điều kiện. Trong tr ng h p này, sử d ng c p ngo c nhọn cho vòng l p là không cần thi t, vì v y tôi đư sử d ng d u ch m phẩy để thay cho c p d u { }. D u ch m phẩy này cǜng có nghĩa là “Không cần làm gì mỗi lần qua vòng l p”. Đây là m t chút kỹ thu t đ c biệt đ c sử d ng b i r t nhiều l p trình viên và tôi nghĩ b n cần ph i bi t để ng d ng cho những vòng l p cực ngắn và đơn gi n. Nh ng n u lỡ b n không bi t kỹ thu t đ c biệt trên thì vòng l p trong code c a b n s nh sau: C code: while (getchar( ) != '\n') { } Không có gì trong c p d u ngo c nhọn là hoàn toàn bình th ng, b i vì trong tr ng h p này chúng ta th t sự không có câu lệnh nào cần thực hiện c . Việc sử d ng d u ch m phẩy thay th cho c p d u ngo c nhọn chỉ giúp code c a b n gọn gàng hơn thôi. Cuối cùng, hàm docKytu s tr về ký tự đầu tiên nó đọc đ Tóm l i, để có đ c, cǜng chíng là giá trị c a bi n kytu. c m t t trong code c a b n, chúng ta s không sử d ng: C code: scanf ("%c", &chuCaiBiMat); Thay vào đó chúng ta s dùng m t cách cao c p hơn: C code: chuCaiBiMat = docKytu( ); Danh mục từ bí ẩn: Bắt đầu ch ơng trình test c a b n, tôi s yêu cầu b n đ t những t bí m t trực ti p vào những dòng code. Ví d nh sau: C code: char tuBimat [ ] = "LOVE" Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 143 - Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 N u chúng ta làm nh trên, dĩ nhiên là t t c các t bí m t c a ng i chơi s giống nh nhau, và ch có gì vui c đúng không. Tôi yêu cầu b n làm v y lúc đầu chỉ để giúp b n tránh g p ph i những v n đề rắc rối thôi (chỉ vào th i điểm đó thôi nhé). Sau khi b n chắc rằng trò chơi đư ho t đ ng đúng nh ý b n muốn, chúng ta đư có thể tho i mái c i ti n nó sang giai đo n hai: chúng ta s t o ra c m t danh m c đầy những t bí m t khác nhau. Cái gì gọi là “danh m c các t bí m t”? Nó chính xác là m t t p tin ch a r t nhiều t bí m t cho trò “Ng chắn là mỗi t s nằm trên 1 dòng. VD nh sau: i treo cổ” c a b n. Và chắc LOVE MONEY PROGRAM FUNCTION POINTER LOOP INSTRUCTION STRING VARIABLE CONSTANT DEFINE Mỗi lần trò chơi bắt đầu, ch ơng trình c a b n s m t p tin này lên và chọn ng u nhiên m t t trong danh sách. Bằng cách này, b n s có m t t p tin riêng biệt để có thể chỉnh sửa theo ý b n muốn và thêm vào những t bí m t m i cho trò “Ng i treo cổ” thêm thú vị. Có thể b n đư nh n ra rằng t đầu đ n gi những t bí m t trong trò chơi đều đ c tôi vi t hoa toàn b . Chúng ta s không phân biệt giữa những ký tự vi t hoa và vi t th ng, v y nên tốt nh t là nên nói rõ ngay t đầu “t t c các t s đ c vi t hoa”. B n có thể thông báo tr c cho ng i chơi phần h ng d n, yêu cầu họ nh p ký tự vi t hoa ch không ph i chữ th ng.\ Hơn nữa, chúng ta cǜng bỏ qua những tr ng h p t có d u để đơn gi n hóa trò chơi (n u ch ơng trình test c a chúng ta có những t có d u nh v y thì t i gi này nó v n ch a xong đâu). B n s cần ph i vi t những t bí m t vào t p tin danh m c t bí m t và chúng ph i đ c vi t hoa không d u. Có m t v n đề là máy tính s hỏi b n, có bao nhiêu t bí m t trong danh m c. Th t v y, n u b n muốn chọn ng u nhiên m t t , s có r t nhiều lựa chọn giữa t mang số th tự 0 v i m t số th tự ng u nhiên b t kỳ, và máy tính làm sao bi t số b t kỳ đó là bao nhiêu. Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 144 - Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 Để gi i quy t v n đề đó, có hai gi i pháp. B n có thể định nghĩa số t ch a trong danh m c ngay dòng đầu tiên c a t p tin: 3 LOVE MONEY LIFE Tuy nhiên, cách này hơi nhàm chán, b i vì chúng ta s ph i tự mình c p nh t l i thông số mỗi lần thêm vào 1 t trong danh m c (cách này v n còn 1 chút b t tiện điểm này). Vì v y tôi g i ý các b n có thể t o ra ch c năng tự đ ng đ m số t có ch a trong t p tin danh m c t bí ẩn cho ch ơng trình c a b n. Cǜng đơn gi n thôi, ý t ng liên quan đ n việc đ m ký tự \n mỗi dòng trong t p tin. Mỗi lần ch ơng trình đọc t p tin, nó s đ m ký tự \n l i t đầu. Sau đó b n s l y k t qu đ m đ c đó th vào con số mà ch ơng trình s chọn ng u nhiên giữa số 0 và nó để chọn ra t s đ c l u vào b nh khi trò chơi bắt đầu. Tôi nh ng cho b n suy nghĩ về gi i pháp này đ y. Tôi s không giúp b n thêm nữa n u không đây đâu còn là ch ơng trình test c a b n nữa. Hãy sử d ng t t c những ki n th c mà b n đư đ c học, b n hoàn toàn có thể thi t k trò chơi này theo nh tôi đư nói. Có thể s m t ít nhiều th i gian cǜng nh b n s c m th y dễ ho c khó nh ng hưy cố gắng tự mình sắp x p, tổ ch c mọi th theo cách c a b n (nh t o đ function cho những gì b n muốn ch ơng trình làm nhé), rồi b n cǜng s làm đ c thôi. Chúc may mắn, và hơn nữa là “Đ ng n n chí” !!! Giải pháp thứ 1: Mã số trò chơi Khi b n đang đọc những dòng này, có thể là b n đư hoàn t t ch ơng trình ho c cǜng có thể là không. B n thân tôi đư m t nhiều th i gian hơn mình nghĩ để bi n trò chơi này trông có v khá ngu ngốc. Nó th ng là nh v y: nhìn qua thì ta th ng nghĩ “ui x i, dễ ẹt” nh ng thực t thì có m t số tr ng h p khá ph c t p cần ph i gi i quy t. Nh ng tôi đư nói rồi, b n v n có thể tự mình gi i quy t đ c h t, chỉ là m t ít nhiều th i gian hơn thôi. Đây đâu ph i là m t cu c ch y đua, b n c tho i mái ra, dành thêm chút th i gian suy nghĩ tr c khi tìm đ n phần gi i pháp này, thử suy nghĩ thêm 5 phút nữa xem sao, bi t đâu l i n y ra đ c ý t ng gì đó. Đ ng có nghĩ rằng tôi chỉ việc đ t đít xuống m t chút là đư vi t xong ch ơng trình. Tôi cǜng nh các b n thôi, cǜng ph i bắt đầu t những gì đơn gi n nh t và c i ti n chúng t t để có đ c k t qu cuối cùng nh mình muốn. Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 145 - Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 Tôi cǜng đư t ng quên những ki n th c cơ b n nh : quên kh i t o giá trị khi khai báo bi n, quên đ t các nguyên m u hàm (prototype) ho c xóa đi những bi n không còn giá trị sử d ng trong ch ơng trình để gi i phóng b nh . Th m chí, tôi ph i thú nh n v i b n là tôi còn quên c việc đ t d u ch m phẩy cuối câu lệnh (instruction). Tôi nói v i b n những điều trên để làm gì? Tôi không hoàn h o, và cu c sống cǜng nh v y, luôn có những sai lầm. “L P TRÌNH CǛNG NH V Y … B N S TI P T C V I NÓ CH !!! YES OR NO ???” Ok, bây gi tôi s cho b n th y gi i pháp qua 2 giai đo n: 1. Đầu tiên tôi s cho các b n th y làm th nào để ch ơng trình xử lý các ký tự đ c ẩn sau mỗi l t đoán c a ng i dùng. Tôi chọn t FACEBOOK vì nó cho phép tôi kiểm tra xem mình có gi i quy t đ c tr ng h p có 2 ký tự giống nhau trong m t t không. 2. Sau đó tôi s cho b n th y làm th nào để qu n lý t p tin danh m c t bí ẩn để thêm vào các t bí ẩn cho trò chơi. Dù có thể nh ng tôi s không vi t toàn b code cho b n th y m t lần. S r t dài và nhìn khá kinh kh ng, tôi s s làm các b n bị choáng. Tôi s cố gắng gi i thích lý do t i sao tôi không làm v y. Hưy khoan quá khao khát đ t đ c k t qu b n muốn, mà quan trọng là cách t duy c a chúng ta về v n đề đó. Hưy t p trung phân tích vào cách gi i quy t v n đề tr c nhé. Phân tích hàm main: Nh mọi ng i v n th ng nói rằng “mọi th đều bắt đầu t đôi tay”. Hehe. Đ ng quên thêm vào những th viện stdio, stdlib, ctype (th viện này để sử d ng hàm toupper ( ) nhé). Chúng th t sự hữu ích và cần thi t cho ch ơng trình c a b n đ y: C code: #include <stdio.h> #include <stdlib.h> #include <ctype.h> int main(int argc, char* argv[ ]) { return 0; } Ok, đ n lúc này thì chúng ta nên làm theo tôi. Rồi chúng ta s th y đ c sự hữu ích c a những th viện, chúng ta s có thể qu n lý ch ơng trình và hầu h t các function trong ch ơng trình s cần đ n những th viện này. Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 146 - Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 Nào, hãy bắt đầu bằng việc khai báo những bi n cần thi t. Hưy yên tâm, tôi đư không nghĩ h t về t t c những bi n cần khai báo cho ch ơng trình cùng m t lúc, ít ra thì v n ít hơn lần đầu tiên tôi t p tành vi t code: C code: #include <stdio.h> #include <stdlib.h> #include <ctype.h> int main(int argc, char* argv[ ]) { char kytu = 0; // Bien nay se luu tru ky tu cua nguoi choi (duoc tra ve boi ham scanf) char tuBimat[ ] = "FACEBOOK"; // Day la tu bi mat can tim ra int sokytuBimat[8] = {0}; /* Mot mang co chua cac thanh phan dang Bolean. Moi o trong mang se tuong ung voi mot ky tu nguoi choi se doan. Neu doan dung thi gia tri = 1, va neu sai thi gia tri = 0 */ int soluotDoan = 10; // So luot doan con lai cua nguoi choi (0 = thua) int i = 0; // Bien ho tro return 0; } Tôi đư cố tình khai báo mỗi bi n trên m t dòng riêng biệt và ghi chú thêm cho t ng bi n để các b n dễ hiểu. Trong thực t , b n không bắt bu c ph i ghi chú t t c ra nh th và có thể khai báo t t c những bi n cùng kiểu trên cùng m t dòng. Các bi n trên đ c khai báo khá h p lý đúng không: bi n kytu để l u trữ ký tự ng i chơi đoán đ c, bi n tuBimat để l u trữ t bí m t c a chúng ta, boeén soluotDoan để đ m số l t đoán còn l i c a ng i chơi (l t đoán = 0 nghĩa là thua) … Bi n i là bi n hỗ tr tôi làm việc v i m ng, vòng l p, nó s giúp b n k t thúc vòng l p. Cuối cùng, bi n mà b n cần ph i nghĩ đ n nh t, th khác biệt v i những bi n còn l i, m ng sokytuBimat ch a các thành phần kiểu Bolean. Để ý thì b n s th y tôi đư quy định kích th c s n cho nó đúng v i số ký tự c a t bí ẩn (là 8 ký tự). Không ph i m t số ng u nhiên nhé: mỗi m t ô thành phần trong m ng đ i diện cho m t ký tự c a t bí m t. Ô đầu tiên đ i diện cho ký tự đầu tiên, ô th 2 là ký tự th 2 và c th cho đ n ký tự cuối. Giá trị c a các thành phần trong m ng đ c kh i t o lúc đầu bằng 0, điều này cǜng có nghĩa là “không tìm th y ký tự nào”. Và theo ti n trình ho t đ ng c a game, giá trị này s đ c thay đổi t ơng ng v i diễn bi n trò chơi. Mỗi m t khi có ký tự đ c tìm đúng, giá trị c a ô thành phần trong m ng sokytuBimat s mang giá trị 1, ng c l i là giá trị 0. Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 147 - Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 Ví d , n u đ n m t lúc ng i chơi tìm đ c đ n *A*E*OO* thì lúc này m ng c a chúng ta s có các giá trị thành phần là 01010110 (giá trị 1 cho mỗi ký tự tìm đúng). Khá dễ để bi t khi nào thì chúng ta thắng trò chơi đúng không. Đó là lúc các giá trị Bolean chỉ toàn là giá trị 1. Ng c l i n u đoán sai thì giá trị s là 0. Ok, ti p t c thôi: C code: printf ("Chao mung den voi tro choi Nguoi treo co !\n\n "); Đơn gi n chỉ là m t l i chào m ng thôi mà. Hihihi. Nh ng vòng l p sau đây m i là điều thú vị: C code: while (soluotDoan > 0 && !win (sokytuBimat)) { Trò chơi s ti p t c n u soluotDoan v n l n hơn 0 ho c ch a thắng. N u không còn l t đoán nào ho c chi n thắng trò chơi, trong c 2 tr ng h p này, ng ng trò chơi ngay l p t c, và vòng l p cǜng s đ c k t thúc. win là m t function đ c phân tích dựa vào m ng sokytuBimat. Nó tr về giá trị “true” t ơng ng v i giá trị 1 n u ng i chơi chi n thắng (m ng sokytuBimat chỉ ch a toàn giá trị 1), “false” t ơng ng giá trị 0 n u ng i chơi thua. Tôi không thể gi i thích chi ti t cho b n về function win ngay bây gi nh ng chúng ta s nói về nó sau ít phút nữa. Bây gi b n t m th i chỉ cần bi t ch c năng mà nó s làm trong ch ơng trình. Xem thử nhé: C code: printf("\n\n Ban co %d luot doan de choi", soluotDoan); printf("\n Tu bi mat la gi ?"); /* Hien thi nhung ky tu bi mat va an di nhung ky tu chua duoc tim thay Vi du: *A***OO*/ for (i = 0 ; i < 6 ; i++) { if (sokytuBimat[i]) // Neu nguoi choi tim duoc ky tu thu i printf("%c", tuBimat[i]); // Hien thi ky tu thu i duoc tim thay else printf("*"); // Hien thi dau * doi voi nhung ky tu chua duoc tim thay } Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 148 - Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 Phân tích đo n code trên, ch ơng trình s hiển thị t ng ký tự tìm đ c và phần còn l i c a t bí mât (phần này đ c ẩn đi b i những d u *). Mỗi khi vòng l p k t thúc, màn hình s hiển thị những ký tự ch a đ c tìm th y bằng d u * và đồng th i phân tích xem ký tự đ c ng i chơi đoán có ch a trong m ng sokytuBimat hay không (dựa vào (if sokytuBimat [i])).N u ký tự đ c tìm th y có ch a trong m ng đó thì hiển thị lên, ng c l i thì hiển thị d u * để ẩn đi. Bây gi chúng ta đư có những th cần hiển thị rồi, hãy yêu cầu ng thôi: i chơi nh p ký tự họ đoán C code: printf ("\n Xin moi ban doan mot ky tu: "); kytu = docKytu( ); Tôi đư gọi hàm docKytu ( ) c a chúng ta. Nó đọc các ký tự đầu tiên đ lên và làm trống b nh đệm. c nh p vào, vi t hoa nó C code: // Neu ky tu nhap vao khong dung if (!kiemtraKytu (kytu, tuBimat, sokytuBimat)) { soluotDoan--; // Giam bot mot lan doan cua nguoi choi } } Chúng ta kiểm tra xem ký tự ng i chơi nh p vào có ch a trong t bí m t không. Đo n code trên gọi hàm kiemtraKytu để thực hiện điều đó và tí nữa b n s th y c thể hàm đó trông nh th nào. T m th i bây gi những gì chúng ta cần bi t là hàm này tr về giá trị “true” n u ký tự đ vào có ch a trong t bí m t, ng c l i thì tr về giá trị “flase”. c nh p Các b n có để ý if c a chúng ta bắt đầu bằng m t d u ch m than, điều này có nghĩa là “không”. V y bây gi điều kiện c a chúng ta s đ c hiểu là “N u không tìm th y ký tự”. Và điều gì s x y ra? Nó s gi m b t m t l t đoán c a ng i chơi. L u ý rằng hàm kiemtraKytu cǜng s đ t giá trị vào các ô trong m ng sokytuBimat. Nó s đ t giá trị 1 vào những ô nào ch a ký tự đúng đ c tìm th y. Vòng l p chính c a trò chơi cần ph i ng ng l i. Vì v y chúng ta s quay l i đầu vòng l p và kiểm tra xem ng i chơi còn l t đoán nào không và liệu rằng họ đư thắng hay ch a. Khi ra khỏi vòng l p chính c a trò chơi, ch ơng trình s kiểm tra xem liệu ng hay ch a tr c khi ch ơng trình ng ng l i. Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 149 - i chơi đư thắng Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 C code: if (win(sokytuBimat)) printf ("\n\n Chuc mung, ban da chien thang ! Tu bi mat la : %s", tuBimat); else printf ("\n\n Xin chia buon, ban da thua !\n\n Tu bi mat la : %s", tuBimat); return 0; } Chúng ta dùng hàm win để kiểm tra xem ng cho họ bi t. i chơi có thắng hay không để hiển thị thông báo Phân tích hàm win: Bây gi chúng ta s th y code c a hàm win: C code: int win(int sokytuBimat[ ]) { int i = 0; int nguoichoiChienThang = 1; for (i = 0 ; i < 6 ; i++) { if (sokytuBimat[i] == 0) nguoichoiChienThang = 0; } return nguoichoiChienThang; } Hàm này l y tham số là m ng sokytuBimat ch a giá trị d ng Boolean. Hàm s tr về m t giá trị “true” n u ng i chơi thắng, ho c giá trị “flase” n u không thắng. Code c a hàm này nhìn khá đơn gi n đúng không, chắc là các b n đều đọc hiểu đúng không. Chúng ta s kiểm tra thử xem trong m ng sokytuBimat có ô nào ch a giá trị 0 không. N u xu t hiện b t kỳ ô nào trong m ng có giá trị 0, điều đó có nghĩa là ng i chơi không thắng đ c, lúc này bi n nguoichoiChienThang (bi n kiểu Boolean) s mang giá trị “false” t ơng đ ơng bằng 0. Và n u t t c các ký tự đều đ c tìm th y, bi n này s có giá trị “true” t ơng đ ơng bằng 1, và hiển nhiên hàm này cǜng s tr về giá trị 1 luôn. Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 150 - Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 Phân tích hàm kiemtraKytu: Hàm kiemtraKytu có 2 nhiệm v chính:   Tr về m t Boolean chỉ ra rằng ký tự do ng i dùng đoán có ch a trong t bí m t hay không. Gửi giá trị (vd: giá trị 1) vào các ô giá trị c a m ng sokytuBimat t ơng ng v i vị trí c a ký tự đó trong m ng. C code: int kiemtraKytu(char kytu, char tuBimat[ ], int sokytuBimat[ ]) { int i = 0; int kytuChinhXac = 0; // Kiem tra xem ky tu cua nguoi choi da doan co nam trong tu bi mat ko for (i = 0 ; tuBimat[i] != '\0' ; i++) { if (kytu == tuBimat[i]) // Neu ky tu co chua trong tu bi mat { kytuChinhXac = 1; // Ky tu se duoc luu tru gia tri the hien no la ky tu chinh xac sokytuBimat[i] = 1; // Gui gia tri 1 vao o tuong ung voi vi tri cua ky tu do trong mang } } return kytuChinhXac; } Hàm s kiểm tra ký tự mà ng 2 điều s diễn ra:   i dùng đư nh p vào có nằm trong ký tự bí m t không. N u có, có Giá trị Boolean c a bi n kytuChinhXac s bằng 1, và giá trị 1 s đ c gửi vào vị trí t ơng ng c a ký tự đó trong kytuBimat. Chúng ta c p nh t giá trị trong m ng sokytuBimat t ơng ng v i vị trí c a ký tự đó trong m ng. L i ích c a kỹ thu t này là chúng ta s kiểm tra m t l t toàn b các giá trị trong m ng (ch không ng ng l i ngay sau khi tìm đ c ký tự đầu tiên). Việc này cho phép chúng ta c p nh t chính xác các giá trị trong m ng sokytuBimat, kể c trong tr ng h p có 2 ký tự giống nhau trong m t t nh 2 ký tự O trong t FACEBOOK. Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 151 - Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 Giải pháp 2: Quản lý tập tin “danh mục từ bí mật” Chúng ta đư có bi t t o ra hầu h t các ch c năng cơ b n c a trò chơi, và các b n đư có thể qu n lý ch ơng trình c a mình nh ng v n còn m t phần ch a hoàn thiện, đó là cách chọn ng u nhiên m t t bí m t trong “danh m c t bí m t”. B n có thể t ng t ng danh m c đó nó giống nh tôi đư cho b n xem trên, tôi không cho b n th y c thể toàn b n i dung t p tin đó đây đ c vì có thể nó dài t i vài trang gi y ch không ít đâu. Tr c khi ti p t c, việc đầu tiên cần làm là t o ra m t “danh m c t bí m t” cho trò chơi. th i điểm này. dù nó ngắn hay dài không quan trọng, chỉ là để làm thử nghiệm cho bài học này thôi. Tôi s t o m t t p tin danhmuc.txt trong th m c ch a project c a tôi. Và t m th i n i dung t p tin s trông nh sau: LOVE MONEY PROGRAM FUNCTION POINTER LOOP INSTRUCTION STRING VARIABLE CONSTANT DEFINE Sau khi hoàn thành ch ơng trình, dĩ nhiên tôi s có thể quay l i danh m c trên và thêm vào t p tin những t bí m t khác để tăng thêm sự phong phú cho trò chơi. Những chuẩn bị cho một tập tin mới. Riêng t “danh m c” trong tên c a t p tin này cǜng đ làm b n t ng t ng ra m t danh sách có đ dài gần nh vô h n đúng không. Do đó, tôi s thêm m t t p tin m i vào project c a mình, nó là t p tin danhmuc.c (t p tin này có nhiệm v đọc danhmuc.txt). Và đối v i quá trình này, tôi s t o m t t p tin danhmuc.h, t p tin này s ch a những prototype c a danhmuc.c. Trong danhmuc.c tôi s thêm vào các th viện cần thi t và dĩ nhiên là có c danhmuc.h. Nh th ng lệ v n là những th viện chuẩn đ c u tiên tr c stdlib, stdio bên c nh đó, chúng ta cần chọn m t số ng u nhiên t danh m c, vì v y ta s ph i thêm vào th viện time.h giống nh đư t ng làm v i ch ơng trình test c a ch ơng 1 (trò “l n hơn hay nhỏ hơn”, b n còn nh ko). Và thêm nữa, ta s sử d ng hàm strlen nên b n ph i thêm vào string.h nữa nhé: Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 152 - Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 Xem thử nào: C code: #include <stdio.h> #include <stdlib.h> #include <time.h> #include <string.h> #include "danhmuc.h" Hàm chonTu: Hàm này s cần m t tham số. Đó là m t con trỏ đ n vùng b nh , nơi mà nó có thể l u trữ t ng u nhiên trong danh m c vào. Con trỏ này s đ c cung c p b i main ( ). Hàm s tr về m t giá trị kiểu int và nó có kiểu Boolean: giá trị 1 n u mọi việc diễn ra tốt đẹp và 0 n u có b t kỳ lỗi gì x y ra. Sau đây là đo n đầu c a hàm: C code: int chonTu(char *tuDuocChon) { FILE* danhmuc = NULL; // Con tro tap tin de chua cac tap tin cua chung ta int soThuTuCuaTu = 0, soThuTuCuaTuDuocChon = 0, i = 0; int luuKytu = 0; Tôi t o m t số bi n cần thi t cho ch ơng trình c a chúng ta. Trong main ( ), có thể b n đư th y tôi đư không t o ra t t c bi n cùng m t lúc ngay khi m i bắt đầu, có những th b n có thể t o ra sau n u b n nh n th y b n cần đ n chúng. Tên c a các bi n trên cǜng cho b n th y nhiệm v , ch c năng c a chúng rồi. Chúng ta đư có con trỏ danhmuc để đọc t p tin danhmuc.txt, những bi n t m th i để l u trữ ký tự … B n có để ý tôi khai báo kiểu int cho bi n luuKytu để l u trữ ký tự? Hơi l đúng không, b i vì hàm fgetc mà tí nữa tôi s dùng nó tr về m t giá trị kiểu int, v y nên tốt nh t là nên l u trữ k t qu c a chúng ta kiểu int. Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 153 - Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 Nào xem thử nhé: C code: danhmuc = fopen("danhmuc.txt", "r"); // Tap tin duoc mo trong che do "read-only" // Chung ta kiem tra xem thao tac mo tap tin co thanh cong khong if (danhmuc == NULL) // Neu ban khong the mo tap tin { printf ("\n Khong the mo danh muc tu bi mat"); return 0; // Tra ve gia tri 0 cho biet thao tac mo tap tin that bai // Sau khi nhan duoc gia tri tra ve cua return, ham ket thuc. } Không có quá nhiều điều m i m đây. Tôi chọn ch đ m t p tin danhmuc.txt là “read-only” (bằng cách chọn “r”) và kiểm tra xem thao tác này có thành công hay không bằng cách sử d ng if n u giá trị tr c a danhmuc là NULL thì rõ ràng việc m t p tin đư th t b i (có thể ch ơng trình không tìm th y t p tin danhmuc.txt ho c nó đang đ c ch ơng trình khác sử d ng). Trong tr ng h p này, màn hình s hiển thị thông báo lỗi và giá trị tr về là 0. T i sao return l i nằm vị trí đó. Th t ra thì, return có ch c năng d ng các ho t đ ng c a hàm l i. N u không thể m đ c t p tin, hàm s d ng l i t i đó và máy tính cǜng s không ti p t c đọc thêm gì nữa. Nó tr về giá trị 0 để cho bi t rằng hàm này không thực hiện đ c. Và đây là phần còn l i c a hàm, gi sử việc m t p tin đư thành công: C code: // Dem cac tu duoc chua trong tap tin (chi viec dem co bao nhieu ky tu \n thoi) do { luuKytu = fgetc(danhmuc); if (luuKytu == '\n') soThuTuCuaTu ++; } while(luuKytu != EOF); B n th y gì không, chúng ta s đọc qua toàn b t p tin nh vào hàm fgetc (và đọc lần l t t ng ký tự m t nhé). Ch ơng trình chỉ việc đ m số lần xu t hiện ký tự \n. Mỗi lần ký tự \n xu t hiện, giá trị c a bi n soThuTuCuaTu s tăng thêm 1. V i phần code này, chúng ta s bi t đ c có bao nhiều t bí m t nằm trong t p tin. Và nh là mỗi m t dòng trong t p tin chỉ ch a 1 t thôi nhé. C code: soThuTuCuaTuDuocChon = tuNgauNhien(soThuTuCuaTu); // Chon mot tu ngau nhien Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 154 - Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 Nh b n th y, tôi gọi m t hàm theo kiểu riêng c a mình, chọn ra t có số th tự ng u nhiên t 1 đ n giá trị c a soThuTuCuaTu (tham số này s đ c gửi vào hàm). Đây là m t hàm đơn gi n mà tôi đư đ t vào file danhmuc.c (tôi s cho b n th y rõ chi ti t tí nữa). Nó s tr về m t giá trị số (t ơng ng v i số th tự c a dòng ch a t đó trong t p tin), giá trị này s đ c gửi vào bi n soThuTuCuaTuDuocChon. C code: // Chuong trinh doc lai tu dau tap tin va ngung lai khi tim thay tu ngau nhien duoc chon rewind(danhmuc); while (soThuTuCuaTuDuocChon > 0) { luuKytu = fgetc(danhmuc); if (luuKytu == '\n') soThuTuCuaTuDuocChon --; } Bây gi thì chúng ta đư có đ c số thự tự c a t đ c chọn ng u nhiên, bằng việc gọi hàm rewind ( ) ch ơng trình s bắt đầu đọc t p tin t đầu. Nó s đ m lần l t t ng ký tự \n trong t p tin. Lần này, giá trị c a bi n soThuTuCuaTuDuocChon s gi m xuống. Vd t đ c chọn ng u nhiên là t th 5 trong t p tin, thì sau mỗi vòng l p giá trị này s gi m xuống còn 4, 3, 2, 1 và 0. Khi giá trị c a soThuTuCuaTuDuocChon bằng 0 thì chúng ta s thoát ra khỏi vòng l p, vì lúc này nó không còn thỏa mưn điều kiện bi n này có giá trị l n hơn 0 nữa. B n cần ph i hiểu đ c ý nghĩa c a đo n code này, nó giúp ta có thể tìm đ c vị trí hiện t i c a “d u nháy o” trong t p tin. Điều này cǜng không thực sự quá khó hiểu nh ng b n ph i cố gắng nhìn nh n mọi việc m t cách rõ ràng. Hãy cố h t s c đ m b o rằng b n hiểu đ c những gì tôi đang nói và đang làm. Bây gi chúng ta cần m t “d u nháy” để chỉ ra vị trí c a t đ c chọn trong t p tin. Chúng ta s gửi đ n con trỏ tuDuocChon (đây là tham số mà hàm cần) v i fgets để đọc các t : C code: /* Con tro tap tin danhmuc duoc dat dung vi tri cua no. Chung ta su dung ham fgets va quy dinh ham khong doc qua so luong ky tu cho phep*/ fgets(tuDuocChon, 100, danhmuc); // Chung ta se thay the ky tu \n tuDuocChon[strlen(tuDuocChon) - 1] = '\0'; Chúng ta yêu cầu hàm fgets không đ c đọc quá 100 ký tự (cǜng t ơng ng v i kích th m ng). Hãy nh rằng hàm fgets s đọc toàn b m t dòng bao gồm luôn ký tự \n. Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 155 - cc a Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C Khi b n không muốn g p ký tự \n này m t chuỗi tr c khi g p ký tự \n. www.siteduzero.com Tác giả: M@teo21 cuối, có thể thay th bằng \0, ký tự này có tác d ng cắt Và … nó đư ho t đ ng … chúng ta đư l u trữ ký tự bí m t t i địa chỉ con trỏ tuDuocChon. Chúng ta v n ch a đóng t p tin l i, m t giá trị tr về 1 để ng ng hàm l i và cho th y rằng ch ơng trình ho t đ ng tốt: C code: fclose(danhmuc); return 1; // Gia tri tra ve = 1, tat ca deu hoat dong tot } Và tôi cǜng không có gì để nói thêm về hàm chonTu. Hàm tuNgauNhien: Đây là hàm mà tôi đư h a s gi i thích cho b n khi nãy. Nó s chọn m t số th tự ng u nhiên và gửi nó về: C code: int tuNgauNhien(int sothutuLonNhat) { srand(time(NULL)); return (rand( ) % sothutuLonNhat); } Dòng đầu tiên srand(time(NULL)); s giúp ch ơng trình c a b n không chọn trùng các số ng u nhiên, giống những gì chúng ta đư t ng làm v i ch ơng trình “l n hơn hay nhỏ hơn” trong ch ơng 1. Dòng th 2 s chọn ra m t số ng u nhiên trong chuỗi giá trị t sothutuLonNhat. 0 đ n giá trị c a bi n Tập tin danhmuc.h Sau đây là prototype c a các function. Và m t điều nữa, b n có còn nh về “ng i b o vệ” #ifndef mà tôi đư t ng yêu cầu các b n nên thêm vào t t c những t p tin .h c a b n (tham kh o l i những ki n th c c a các bài học về prototype và tiền xử lý nhé). C code: #ifndef DEF_DANHMUC #define DEF_DANHMUC int chonTu(char *tuDuocChon); int tuNgauNhien(int sothutuLonNhat); #endif Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 156 - Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 Tập tin danhmuc.c Và sau đây là toàn b n i dung c a t p tin danhmuc.c, xin m i các b n th ng th c: C code: /* Nguoi Treo Co danhmuc.c -----Nhung function nay se chon ra mot tu ngau nhien trong tap tin chua danh muc tu bi an cua tro choi Nguoi Treo Co */ #include <stdio.h> #include <stdlib.h> #include <time.h> #include <string.h> #include "danhmuc.h" int chonTu(char *tuDuocChon) { FILE* danhmuc = NULL; // Con tro tap tin de chua cac tap tin cua chung ta int soThuTuCuaTu = 0, soThuTuCuaTuDuocChon = 0, i = 0; int luuKytu = 0; danhmuc = fopen("danhmuc.txt", "r"); // Tap tin duoc mo trong che do "read-only" // Chung ta kiem tra xem thao tac mo tap tin co thanh cong khong if (danhmuc == NULL) // Neu ban khong the mo tap tin { printf ("\n Khong the mo danh muc tu bi mat"); return 0; // Tra ve gia tri 0 cho biet thao tac mo tap tin that bai // Sau khi nhan duoc gia tri tra ve cua return, ham ket thuc. } Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 157 - Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 // Dem cac tu duoc chua trong tap tin (chi viec dem co bao nhieu ky tu \n thoi) do { luuKytu = fgetc(danhmuc); if (luuKytu == '\n') soThuTuCuaTu++; } while(luuKytu != EOF); soThuTuCuaTuDuocChon = tuNgauNhien(soThuTuCuaTu); // Chon mot tu ngau nhien // Chuong trinh doc lai tu dau tap tin va ngung lai khi tim thay tu ngau nhien duoc chon rewind(danhmuc); while (soThuTuCuaTuDuocChon > 0) { luuKytu = fgetc(danhmuc); if (luuKytu == '\n') soThuTuCuaTuDuocChon--; } /* Con tro tap tin danhmuc duoc dat dung vi tri cua no. Chung ta su dung ham fgets va quy dinh ham khong doc qua so luong ky tu cho phep*/ fgets(tuDuocChon, 100, danhmuc); // Chung ta se thay the ky tu \n tuDuocChon[strlen(tuDuocChon) - 1] = '\0'; fclose(danhmuc); return 1; // Gia tri tra ve = 1, tat ca deu hoat dong tot } int tuNgauNhien(int sothutuLonNhat) { srand(time(NULL)); return (rand( ) % sothutuLonNhat); } Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 158 - Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 Chúng ta sẽ làm vài thứ trong main.c: Bây gi t p tin danhmuc.c đư s n sàng, chúng ta cùng tr l i v i main.c để thêm vào m t số th cho phù h p. V i những gì đư làm t đầu đ n gi , tr c h t chúng ta s thêm vào file danhmuc.h n u b n muốn sử d ng các function c a danhmuc.c. Ngoài ra, chúng ta cǜng s không quên khai báo thêm th viện string.h b i vì chúng ta s ph i sử d ng hàm strlen: C code: #include <string.h> #include "dico.h" Để bắt đầu, việc khai báo các bi n s có m t chút thay đổi. B n không cần kh i t o m t chuỗi ký tự tuBimat b i vì chúng ta đư có s n m t m ng kiểu char cho nó (ch a s n 100 ô luôn). V i m ng sokytuBimat, kích th c c a nó ph thu c vào đ dài c a t bí m t. B i vì chúng ta ch a bi t đ c kích th c đó, nên s có m t con trỏ đ c t o ra, cùng v i malloc chúng ta có thể gửi con trỏ đ n vị trí b nh mà nó s đ c c p phát. Đây là m t vd tuyệt v i cho bài học về c p phát đ ng c a chúng ta tr c đây: chúng ta không thể nào bi t tr c đ c kích th c c a m ng n u ch ơng trình ch a ch y, vì v y bắt bu c b n ph i t o ra m t con trỏ và sử d ng hàm malloc. B n không đ c quên gi i phóng b nh để còn sử d ng cho những m c đích khác nhé. Đó cǜng chính là lý do cho sự xu t hiện c a free ( ) cuối main.c. Chúng ta s cần m t bi n dodaiTu để l u trữ giá trị thể hiện số l ng ký tự c a t . Nh b n đư th y trong phần đầu, chúng ta đư gi định t bí m t s có 8 ký tự (b i vì chúng ta chọn FACEBOOK làm vd mà). Nh ng bây gi chúng ta đư có thể làm việc linh đ ng v i mọi t v i kích th c tùy bi n. Và sau đây là đầy đ t t c những bi n cần cho ch ơng trình c a chúng ta: C code: int main(int argc, char* argv[ ]) { char kytu = 0; // Bien nay se luu tru ky tu cua nguoi choi (duoc tra ve boi ham scanf) char tuBimat[100] = {0}; // Day la tu bi mat can tim ra int *sokytuBimat = NULL; /* Mot mang co chua cac thanh phan dang Bolean. Moi o trong mang se tuong ung voi mot ky tu nguoi choi se doan. Neu doan dung thi gia tri = 1, va neu sai thi gia tri = 0 */ int soluotDoan = 10; // So luot doan con lai cua nguoi choi (0 = thua) int i = 0; // Bien ho tro int dodaiTu = 0; Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 159 - Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 Trên đây ch y u là những thay đổi ban đầu, bây gi chúng ta s đi sâu hơn m t chút: C code: if (!chonTu(tuBimat)) exit(0); Đầu tiên chúng ta đ t hàm chonTu vào phần điều kiện c a if, hàm này s l y tham số là bi n tuBimat. Và nh ta đư bi t, hàm s tr về m t giá trị Boolean (1 ho c 0) để cho bi t nó có đ c thực hiện thành công hay không. Vai trò c a if là phân tích các giá trị Boolean đ c tr về b i hàm chonTu, n u hàm KHÔNG ho t đ ng (hưy l u ý trong if có ch a d u ! để thể hiện ph định), thì ng ng l i t t c bằng exit (0). C code: dodaiTu = strlen(tuBimat); Giá trị thể hiện kích th c c a tuBimat đ c l u trữ trong bi n dodaiTu. C code: sokytuBimat = malloc(dodaiTu * sizeof(int)); /* mang sokytuBimat se duoc cap phat dong bo nho (luc dau chung ta khong biet duoc kich thuoc cua mang nay) */ if (sokytuBimat == NULL) exit(0); Bây gi chúng ta ph i c p phát b nh cho m ng sokytuBimat. Kích th đ c cung c p b i giá trị c a bi n dodaiTu. c c a m ng này đư Sau đó dùng if để kiểm tra giá trị c a con trỏ có bằng NULL không. Trong tr ng h p n u bằng NULL, ch ng tỏ việc c p phát b nh đư th t b i, chúng ta s d ng ch ơng trình ngay l p t c (bằng cách gọi hàm exit (0) ) Đây là t t c những gì b n cần chuẩn bị cho ch ơng trình. Sâu đây chúng ta cần thay đổi nốt phần còn l i c a main.c để thay th t t c các con số 8 (số thể hiện đ dài c a t FACEBOOK mà ta gi định là t bí m t lúc đầu bài học) bằng bi n dodaiTu. VD: C code: for (i = 0 ; i < dodaiTu ; i++) sokytuBimat[i] = 0; Ban đầu, đo n code trên đ t giá trị 0 vào các ô trong m ng sokytuBimat và có thể hiểu là không có ô nào trong m ng, dần dần giá trị đó s tăng lên cho đ n khi nào bằng v i giá trị c a bi n dodaiTu thì ng ng l i, và đó cǜng chính là kích th c chính xác cho m ng. Tôi cǜng ph i thi t k l i prototype c a function win để thêm vào bi n dodaiTu. N u không các hàm s không bi t khi nào ph i d ng vòng l p. Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 160 - Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 Sau đây là t p tin main.c hoàn thiện: C code: /* Nguoi Treo Co main.c -----Nhung function nay se chon ra mot tu ngau nhien trong tap tin chua danh muc tu bi an cua tro choi Nguoi Treo Co */ #include <stdio.h> #include <stdlib.h> #include <ctype.h> #include <string.h> #include "danhmuc.h" int win(int sokytuBimat[ ], long dodaiTu); int kiemtraKytu(char kytu, char tuBimat[ ], int sokytuBimat[ ]); char docKytu( ); int main(int argc, char* argv[ ]) { char kytu = 0; // Bien nay se luu tru ky tu cua nguoi choi (duoc tra ve boi ham scanf) char tuBimat[100] = {0}; // Day la tu bi mat can tim ra int *sokytuBimat = NULL; /* Mot mang co chua cac thanh phan dang Bolean. Moi o trong mang se tuong ung voi mot ky tu nguoi choi se doan. Neu doan dung thi gia tri = 1, va neu sai thi gia tri = 0 */ long soluotDoan = 10; // So luot doan con lai cua nguoi choi (0 = thua) long i = 0; // Bien ho tro long dodaiTu = 0; printf ("Chao mung den voi tro choi Nguoi treo co !\n\n "); if (!chonTu(tuBimat)) exit(0); Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 161 - Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 dodaiTu = strlen(tuBimat); sokytuBimat = malloc(dodaiTu * sizeof (int)); /* mang sokytuBimat se duoc cap phat dong bo nho (luc dau chung ta khong biet duoc kich thuoc cua mang nay) */ if (sokytuBimat == NULL) exit(0); for (i = 0 ; i < dodaiTu ; i++) sokytuBimat[i] = 0; /* Chung ta se tiep tuc tro choi neu con it nhat mot luot doan Va van chua tim duoc tu bi mat*/ while (soluotDoan > 0 && !win(sokytuBimat, dodaiTu)) { printf ("\n\n Ban co %d luot doan de choi ", soluotDoan); printf ("\n Tu bi mat la gi ?"); /* Hien thi nhung ky tu bi mat va an di nhung ky tu chua duoc tim thay Vi du: *A***OO*/ for (i = 0 ; i < dodaiTu ; i++) { if (sokytuBimat[i]) // Neu nguoi choi tim duoc ky tu thu i printf ("%c", tuBimat[i]); // Hien thi ky tu thu i duoc tim thay else printf ("*");// Hien thi dau * doi voi nhung ky tu chua duoc tim thay } printf ("\n Xin moi ban doan mot ky tu: "); kytu = docKytu( ); // Neu ky tu nhap vao khong dung if (!kiemtraKytu(kytu, tuBimat, sokytuBimat)) { soluotDoan--; // Giam bot mot lan doan cua nguoi choi } } if (win(sokytuBimat, dodaiTu)) printf ("\n\n Chuc mung, ban da chien thang ! Tu bi mat la : %s", tuBimat); Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 162 - Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 else printf ("\n\n Xin chia buon, ban da thua !\n\n Tu bi mat la : %s", tuBimat); free (sokytuBimat); // Giai phong bo nho da duoc phan bo (boi ham malloc) return 0; } char docKytu( ) { char kytuNhapVao = 0; kytuNhapVao = getchar( ); // Doc ky tu duoc nhap dau tien kytuNhapVao = toupper(kytuNhapVao); // Viet hoa ky tu do // Lan luot doc tiep cac ky tu khac cho den khi gap \n while (getchar( ) != '\n') ; return kytuNhapVao; // Tra ve ky tu dau tien doc duoc } int win(int sokytuBimat[ ], long dodaiTu) { long i = 0; int nguoichoiChienThang = 1; for (i = 0 ; i < dodaiTu ; i++) { if (sokytuBimat[i] == 0) nguoichoiChienThang = 0; } return nguoichoiChienThang; } int kiemtraKytu(char kytu, char tuBimat[ ], int sokytuBimat[ ]) { long i = 0; int kytuChinhXac = 0; Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 163 - Editor: M0N1M thaibaminh2512@gmai.com Tài liệu hướng dẫn lập trình C Apprenez à programmer en C www.siteduzero.com Tác giả: M@teo21 // Kiem tra xem ky tu cua nguoi choi da doan co nam trong tu bi mat ko for (i = 0 ; tuBimat[i] != '\0' ; i++) { if (kytu == tuBimat[i]) // Neu ky tu co chua trong tu bi mat { kytuChinhXac = 1; // Ky tu se duoc luu tru gia tri the hien no la ky tu chinh xac sokytuBimat[i] = 1; // Gui gia tri 1 vao o tuong ung voi vi tri cua ky tu do trong mang } } return kytuChinhXac; } Ý tưởng cải tiến: Chà, trò “Ng i treo cổ” này có hơi ph c t p v i b n không. Bây gi b n đư có m t ch ơng trình chọn ra t ng u nhiên t m t t p tin rồi đúng không. Sau đây là m t số ý t    ng c i ti n để b n thử s c: Hiện nay, chúng ta chỉ m i cho phép mọi ng i chơi m t lần. Nghĩa là ch ơng trình s ng ng l i khi có ng i chi n thắng ho c sử d ng h t l t đoán. Bây gi , b n hãy thử t o m t yêu cầu ng i chơi xem họ có muốn chơi l i không. B n cǜng có thể t o ra ch đ chơi 2 ng i, ng i th nh t s nh p t bí ẩn vào cho ng i th 2 đoán. M c dù không bắt bu c nh ng sao b n không thử v hình ng i bị treo cổ trên màn hình console (nh các kim t điển hay có) g i ý là b n có thể làm bằng hàm printf. Hãy cố gắng bỏ th i gian ra để hiểu bài học này và c i ti n nó h t m c có thể nhé. Cố lên nào, đ ng n n lòng. Dịch giả: Mr. Hung daihung.pham@yahoo.fr - 164 - Editor: M0N1M thaibaminh2512@gmai.com