Giáo trình Ngôn ngữ lập trình C (Căn bản)

pdf 108 trang ngocly 1510
Bạn đang xem 20 trang mẫu của tài liệu "Giáo trình Ngôn ngữ lập trình C (Căn bản)", để tải tài liệu gốc về máy bạn click vào nút DOWNLOAD ở trên

Tài liệu đính kèm:

  • pdfgiao_trinh_ngon_ngu_lap_trinh_c_can_ban.pdf

Nội dung text: Giáo trình Ngôn ngữ lập trình C (Căn bản)

  1. Ngon ngu lap trinh C (can ban ) CHƯƠNG 1 : Cá C KHÁ I NIÊṂ CƠ BẢ N CỦ A NGÔN NGỮ C 1.1/ Tâp̣ ký tư ̣hơp̣ lê ̣dùng trong ngôn ngữ C - Các chữ cái : A, B, C , 2, a,n,c, z ( 26 chữ cái thường) - Các chữ số : 0,1, , 9. - Ký tư ̣gac̣ h nối _ ( chú ý phân biêṭ dấu - ). - Dấu cách ( space) : dùng để phân biêṭ các từ : Vi ́ du ̣: lop Hoc̣ ( 7 ki ́ tư)̣ - LopHoc( 6 kí tư)̣ . 1.2/ Tên ( điṇh danh ) : là 1 dãy ki ́ tư ̣bắt đầu bằng chữ hoăc̣ ký tư ̣gac̣ h dưới, theo sau là chữ cái, chữ số hoăc̣ ký tư ̣gac̣ h nối (-). - Tên : dùng làm tên hằnp, tên biến , nhãn , tên hàm
  2. Vi ́ du ̣: Tên đúng : _abc, Delta_1, BETA. Tên sai : 1xyz ( vi ̀ bắt đầu là 1 chữ số ) A#B ( vi ̀ có dâu #) Delta ( vi ̀ có khoảng trống) , X-1 (vì sử duṇ g dấu gac̣ h ngang). * Chú ý : + Tên : chữ hoa và chữ thường đươc̣ xem là khác nhau ( ( # pascal ) + Thông thường : . Đăṭ chữ hoa cho các hằng, chữ thường cho các đai ̣ lươṇ g còn lai(̣ biến, hàm ). . Nên đăṭ 1 cách gơị nhớ ( 8 ki ́ tư ̣đầu là có nghiã và tuỳ thuôc̣ chương triǹ h ). 1.3/ Từ khoá : là từ dành riêng cho ngôn ngữ. Tên biến, hằng, hàm không đươc̣ trùng với từ khoá, luôn luôn viết bằng chữ thường. Các từ khoá trong C gồm : Break, char, continue, case, do, double, default, else, float, for, goto, int,if, long, return, struct, switch, unsigned, while, typedef, union voi, volatile, 1.4/ Các kiểu dữ liêụ cơ bản trong C : 4 kiểu : char, Int, float, double.
  3. - Kiểu char ( 1 byte ) : biễu diễn 1 ký tư ̣ thuôc̣ ASCII ( thưc̣ chất là số nguyên từ 0 đến 255) Vi ́ du ̣: Ký tư ̣ASCII 0 048 A 065 a 097 - Kiểu Int : 3 loai ̣ : Int, long Int ( long ) và unsigned Int ( unsigned). - Kiểu Float : biểu diễn các số thưc̣ đô ̣chiń h xác điṇh. - Kiểu double : biễu diễn các số thưc̣ đô ̣ chiń h xác kép. Stt Kiểu Phaṃ vi Kić h thước 1 Char 0 255 1 byte 2 Int -32768 32767 2 bytes 3 Long -2147483648 2147484647 4bytes 4 Unsigned 0 65535 2 bytes 5 Float 3.4e - 38 3.4e + 38 4 bytes 6 double 1.7e - 308 1.7e + 308 8 bytes - Kiểu void: Kiểu không giá tri, ̣ củ dùng để
  4. biểu diễn kết quả hàm cũng như nôị dung củ pointer. Kiểu này sẽ nói chi tiết ở các phần liên quan. 1.5/ Biến và mảng : a/ Biến : Biến đai ̣ lươṇ g thay đổi; mỗi biến có 1 tên và điạ chi ̉ vùng nhờ danh riêng cho nó. Khai báo biến : Cú pháp ; Vi ́ du ̣: Int i,j ; long cucdai; double tongsothue; Int a,b = 20; float e = 35.1; x=30.5; b/ Mảng: là tâp̣ hơp̣ các phần tử có cùng 1 kiểu và chung 1 tên. Khai báo : Vi ́ du ̣: Int Mang1[ 10 ]; Float Bang [10][10]; - Mảng môṭ chiều : là môṭ dãy các ký tư ̣ phần tử tuần tư ̣ trong bô ̣nhớ, mỗi môṭ phần tử chiếm môṭ số byte tương ứng với kiểu của nó. - Mảng nhiều chiều : Gồm các phần tử sắp liên tiếp từ hàng này sang hàng kia. Các chi ̉ số đươc̣ đánh số từ 0 trở đi. Vi ́ du ̣:
  5. - Mãng 1[0] Mãng1[9] - Bang [0][0] Bang [0][1] Bang [0][9]. Bang[][] Bang[2][0] Bang[1][9] Bang[9][0] Bang[9][9] * Chú ý : &Mang1[3] đúng nhưng &Bang[2] [5]sai ( Đúng đối với 1 chiều và sai đối với nhiều chiều) 1.6 / Hằng : Đai ̣ lươṇ g không thay đổi a/ Hằng nguyên ( Int ): có giá tri ̣từ -32768 đến 32767 - Có thể viết theo hê ̣ 16 bằng cách thêm tiền tố Ox hoăc̣ theo cơ số 8 bằng cách thêm tiền tố O ( Octal = bát phân ). * Vi ́ du ̣: O306 viết theo cơ số 8 : Giá tri =̣ 6 * 8 0 + 3* 8 * 8 = 198 trong hê ̣10. O345 = 3*8*8 + 4*8 + 5 = 229 Ox147 = 1*16*16 + 4*16 +7 =327 trong hê ̣ 10. OxAa= 10*16+13=173 - Lý do a hoăc̣ A =10 b hoăc̣ B =11 c hoăc̣ C =12 d hoăc̣ D =13
  6. e hoăc̣ E = 14 f hoăc̣ F = 15 b/Hằng long ( long Int : nguyên lớn ) : giống như hằng nguyên, chi ̉ khác thêm L hoăc̣ l ở đầu. * Vi ́ du ̣ : 180L, 123456789l ( Hằng nguyên là giá tri ṿ ươṭ quá số nguyên là hằng nguyên lớn (long) c/Hằng thưc̣ ( float và double ) : Có 2 cách viết - Cách 1 : ( daṇ g thâp̣ phân) Số gồm : phần nguyên, dấu chấm thâp̣ phân và phần phân. * Vi ́ du ̣: 214.35 , - 234.34. - Cách 2 : Viết theo daṇ g khoa hoc̣ * Vi ́ du ̣: 1.543e7 = 15430000 123.456E-4 = 0.123456 ( 123.456/105) d/Hằng ký tư ̣ : Viết trong 2 dấu nháy đơn. Giá tri ̣ của hằng chiń h là mã ASCII của chữ. * Vi ́ du ̣: 'A' = 65; 'd' = 100, '9 ' - '0 ' = 57 - 48 = 9 - Hằng ký tư ̣ còn có thể viết \X1X2X3 , \x1x2x3 : trong đó x1,x2,x3 là số nguyên hê ̣8. *Vi ́ du ̣: chữ a mã hê ̣10 là 97 đổi ra hê ̣8 là O141 => \141='a'; \ 101='A'; \ 142 ='b'
  7. * Môṭ số hằng đăc̣ biêṭ đươc̣ viết theo qui ước như sau : Viết Ký tư ̣Diễn giải ' \ " ' dấu nháy đơn ' \" ' " dấu nháy kép ' \\ ' \ dấu chéo ngươc̣ '\n ' \n ký tư ̣xuống dòng ' \0 ' \0 ký tư ̣rỗng ( null) Chú ý : - Phân biêṭ ký tư ̣ '0 ' và ' \0 '. Hằng ' 0 ' cùng với chữ số 0 có mã = 48 - Hằng '\0 ' cùng với ký tư ̣\0 (null) có mã 0. - Hằng ký tư ̣thưc̣ sư ̣là số nguyên => có thể dùng số nguyên hê ̣10 đê biểu diễn ký tư ̣. * Vi ́ du ̣: printf( " %c%c", 65,66) sẽ in ra AB. e/Hằng xâu ký tư ̣ : đăṭ trong dấu nháy kép ( " "). Hằng đươc̣ lưu trữ trong 1 mãng ký tư ̣ mà ký tư ̣ cuối cùng là rỗng (null) ' \0 '. Vi ́ du ̣: "Lơp Hoc" - Hằng có thể đươc̣ đinh nghiã đối với toán tử define . + Cú pháp : # define ̣ Trong chương triǹ h moị biến max đều đươc̣ thay đổi giá tri 1̣ 00.
  8. Vi ́ du ̣: # define MAX 100 # Define pi 3.141593 1.7/ Phép toán : + Phép toán số hoc̣ gồm : +,-,*, / ( Phép chia lấy phần nguyên ), % ( phép chia lấy phần dư). + Phép toán quan hê ̣ : , =, = =, ! = ( khác). + Phép toán logic : || ( hoăc̣ ) , && ( và) ! ( not ), #0 hay =1 : True( đúng) ; =0 : Falsse ( sai) + Phép toán tăng giảm : ++ côṇ g thêm 1 vào toán haṇ g. * Vi ́ du ̣: Int n=10; n++;=> n=11 n=n+1; Chú ý : - n++ : giá tri ṇ đươc̣ lấy trước khi tăng n. - ++n : giá tri ṇ đươc̣ lấy sau khi tăng n - tương tư ̣n , n ; + Toán tử thao tác bit : Không áp duṇ g cho kiểu float hoăc̣ double. & : phép hôị các bit́ ( và) | : phép tuyển các bit ( hoăc̣ ) ^ : phép tuyển các bit loai ̣ trừ > : phép dic̣h phải. : phép lấy phần bù. Vi ́ du ̣: 105 & 7 = 1 /* 0111 1001 & 0000 0111 =
  9. 0000 0001 */ 105 | 17 = 127 /* 0111 1001 | 0000 0111 = 0111.1111 */ 0x60 = 0x96 /* 0110 1001 = 1001 0110 */ + Toán tử chuyển đổi kiểu : ta có thể dùng toánt ử chuyển kiểu để chuyển 1 kiểu bất kỳ sang kiểu mong muốn bằng cách dùng toán tử sắc thái ( cast) theo quy tắc sau : ép Kiểu ( type cast ) : ( kiểu ) Biến Kiểu mong muốn * Vi ́ du ̣: int i = 10 ă ( float ) i => 10.0 - Chú ý : + Môṭ số kiểu float khi chuyển sang kiểu Int sẽ bi c̣ hăṭ cuṭ phần thâp̣ phân. + Môṭ số kiểu long khi chuyển sang kiểu Int sẽ cắt bỏ vài chữ số. * Vi ́ du ̣: n = 2560.70 thi ̀ (int)n = 2560 + Toán tử gán : - Cú pháp : = * Vi ́ du ̣: c = a + b ; d= t + 3 ; i= i+2 (Viết goṇ i+=2; ) i= i*2 (i*=2; ) x = x >> 1 (x >> = 1;) Chú ý : Các phép toán trong C có đô ̣ưu tiên khác
  10. nhau và quy tắc kết hơp̣ khác nhau => Bảng liêṭ kê các phép toán theo thứ tư ̣ưu tiên từ trên xuống dưới, các phép toán trên dòng có thứ tư ̣như nhau. Phép toán Triǹ h tư ̣kết hơp̣ (),[ ], ă trái qua phải |, dấu ngã, &*, - -, + + , ( type ) size of phải qua trái *,/, % trái qua phải +, - trái qua phải > trái qua phải ,>= trái qua phải & trái qua phải | trái qua phải && trái qua phải || trái qua phải ? phải qua trái = =, !=, +=, -= phải qua trái 1.8/ Biểu thức : đươc̣ xây dưṇ g bằng các toán tử , toán haṇ g là các hằng, biến, hàm - Biểu thức gán : Vi ́ du ̣: A = B =C =5 => A=5, B = 5, C = 5. - Biểu thức điều kiêṇ có daṇ g : B1?E1 : E2 : Nếu B 1 đúng giá tri ḅ iểu thức = E1. ngươc̣ lai ̣ E2. * Vi ́ du ̣: S=x>y ? x:y cho giá tri ḷ ớn nhất của x và
  11. y. 1.9/ Cấu trúc tổng quát của chương triǹ h viết bằng ngôn ngữ C : #include những khai báo, những chỉ thi ṭ iền xử lý. #define /* Các khai báo kiểu dữ liêụ , hằng */ Type of { Các biến toàn cuc̣ , biến ngoài} prototype { khai báo tiêu đề hàm} main () { x1,x2, xn} * Vi ́ du ̣: Viết chương triǹ h số lớn nhất cho trước a, b, c /* Chương triǹ h tim̀ số lớn nhất trong 3 số*/ # include # Include void main (void) { int n1,n2, n3, nmax ; do { /* đoc̣ 3 số từ bàn phiḿ */ printf(" nhâp̣ số thứ nhất : "); scanf( " %d", &n1);
  12. printf(" nhâp̣ số thứ hai : "); scanf( " %d", &n2); printf(" nhâp̣ số thứ ba : "); scanf( " %d", &n3); /* tim̀ số lớn nhất */ nmax = n1>n2 ? n1:n2; nmax = nmax > n3 ? nmax : n3; /* In ra kết quả */ printf ( " số lớn nhất trong 3 số %d%d%d là : %d \ n ", n1,n2,n3 ,nmax); } printf ( " ấn ESC để kết thúc ); while ( getch ()! = 27 ); } 2/ CÁ C LÊṆ H XUẤ T NHÂP̣ CHUẩN: 2.1/ Hàm Printf - Printf (" formated string ", ); Biểu thức có thể là : const ( hằng), var ( biến), function (hàm). * Vi ́ du ̣: int Siso= 30; Printf ( " In si ̃ số lớp hoc̣ là %d, Siso ); a/ Các ký tư ̣điều khiển : \n : sang dòng mới \b : lùi lai ̣ 1 tab. \f : sang trang mới \t : dấu tab
  13. \' : In ra dấu ' \" : in ra dấu " \ \ : in ra dấu \ b/ ký tư ̣chuyển daṇ g : Ký tư ̣chuyển daṇ g Kiểu của đối Mô tả c char đối là ký tư ̣ d/di int đối là số nguyên ld /li long đối là số nguyên dài f float hoăc̣ doubl đối là số thưc̣ s xâu ký tư(̣ chuỗi) đối là chuỗi u int số nguyên hê ̣10 không dấu O int số nguyên hê ̣8 không dấu lo long số hê ̣8 không dấu x int số hê ̣16 không dấu lx long số hê ̣16 không dấu g float hay double không in ra các số không vô nghiã c float hoăc̣ double đối trong daṇ g thâp̣ phân Đô ̣rôṇ g dành cho biến , trước in ra. Lưu ý ra màn hiǹ h printf( stdpm, "\n si ̃ số ") 2.2/ Hàm scanf : - scanf (" formated string ", các điạ chi ̉ biến ); * Vi ́ du ̣: int a ;
  14. float x,y; char cr[6], ct[6]; scanf (" %f %5f3d%35%5 ", &x , &y , &a , c r, ct); Nhâp̣ vào 5.4 25 124 523 48ab Enter . => kết quả là : x=5.4 ; y=25.0; a = 124; cr= "523"; ct = "48ab" 2.3/ Dòng vào STDIN (standard in) và các hàm scanf, gets, getchar. - StdIn dòng vào chuẩn( bàn phiḿ ). - Lưu ý : nếu từ Stdin có đủ dữ liêụ thi ̀ các hàm trên sẽ nhâṇ 1 phần dữ liêụ mà dòng yêu cầu. Phân còn lai ̣ ( chưa nhâp̣ ) vâñ trên StdIn Nếu chưa đủ đơị đến khi Enter. * Vi ́ du ̣: char ht[20] ; print ( " \n hoten: ") ; gets(ht); - Hàm getchar() nhâṇ 1 ký tư ̣từ stdIn và trả về ký tư ̣nhâṇ đươc̣ . * Vi ́ du ̣ : Int ch; ch = getchar(); nếu nhâp̣ A và enter => ch='A' '\'n vâñ còn trên stdIn và hàm getchar sau đó hàm scanf cũng như vâỵ . - Làm sach stdIn : fflush(stdin);
  15. Vi ́ du ̣: Print("\ tuoi : n"); scanf ( " %d " , &tuoi); Printf ("\n hoten :"); fflush( stdin); get(ht); Vi ́ du ̣: scanf( %d", &a); ch =getchar(); gets(ht); Nhâp̣ vào liên tuc̣ : 12E Trần Văn T ( Enter ). => kết quả là : a =12, ch = 'E', ht = " Trần văn T" ã Hàm puts : đưa môṭ chuỗi ký tư ̣ra stdout ( màn hiǹ h ) Vi ́ du ̣: puts('\n lophoc"); đưa dòng chữ lơp̣ hoc̣ lên 1 dòng mới. * Hàm putchar : đưa 1 ký tư ̣lên stdout . Vi ́ du ̣: putchar('A') ; > in ra ký tư ̣A. Chú ý : Tất cả các hàm trên khai báo trong stdio.h. 2.4 các hàm vào ra màn hiǹ h , bàn phiḿ thuôc̣ hàm conio.h - Hàm getch() : nhâṇ 1 ký tư ̣ trưc̣ tiêp̣ từ dô ̣đêṃ bàn phiḿ và trả về ký tư ̣nhâṇ đươc̣ - Hàm getchc () : nhâṇ 1 ký tư ̣trưc̣ tiếp từ và hiển thi ṭ rên monitor - Hàm Putch ( Int ch) : hiển thi ḳ ý tư ̣ch theo miền xác điṇh trong hàm textcolor #putchar () hiển thi ṭ heo màu trắng. Int Kbhit(void) = 0 nếu bô ̣đêṃ bàn phiḿ rỗng. # 0 nếu bô ̣đêṃ bàn phiḿ khác rỗng.
  16. Chú ý : Nếu gõ phiḿ khi máy dừng chờ trong các hàm scanf, gets, getchar thi ̀ ký tư ̣ vào stdin => if (Kbhit ()) ch = getch() ; hoăc̣ scanf(" %d%*c, &i); ( để khử ' \n '). + clrscr(); hàm xoá màn hiǹ h. + goto xy (x,y): di chuyển con trỏ đến toa ̣ đô ỹ(x,y) : x : côṭ ( 1 80); y : dòng 1 25. * Vi ́ du ̣: viết chương triǹ h nhâp̣ vào tên ban và in ra lời chào : # Include #Include main () { char name[30], ch; printf( " nhâp̣ tên của baṇ : "); scanf (" % s ", &name); printf(" \n chào %s!\n", name); ch = getch(); /* ( đơị nhâṇ số 1 ký tư ỷ=> dừng màn hiǹ h*/ CHƯƠNG 3 : CẤ U TRÚ C ĐIỀU KHIỂ N VÀ VÒ NG LĂP̣ 3.1/ a/Khái niêṃ : moị chương triǹ h đều có
  17. thể biểu diễn qua 3 cấu trúc : - tuần tư ̣: măc̣ điṇh ( default) - lưạ choṇ ( lêṇ h if hoăc̣ lêṇ h switch) - lăp̣ ( for, while hoăc̣ do while) b/ Khối lêṇ h : là tâp̣ hơp̣ các câu lêṇ h đươc̣ khai báo bởi 2 dấu { và } . không đăṭ dấu chấm phẩy ( ; ) sau môṭ khối lêṇ h trừ môṭ vài trường hơp̣ đăc̣ biêṭ. 3.2 / Các câu lêṇ h 3.2.1 Lêṇ h If : - Cú pháp : If ( biểu thức) ; - Diễn giải : nếu Biểu thức đúng ( khác 0 ) > thưc̣ hiêṇ ngươc̣ lai ̣ nếu biểu thức sai ( = 0 ) -ă thưc̣ hiêṇ lêṇ h đứng sau câu lêṇ h if. - Hoăc̣ : If ( biểu thức) ; else =0 ( sai ) > . * Vi ́ du ̣: tim̀ số lớn nhất trong 2 số a, b : if (a b)? a:b; ( Viết lai ̣ hoàn chin̉ h
  18. chương triǹ h). - Chú ý : trong trường hơp̣ có nhiều lêṇ h If lồng nhau thi ̀ else sẽ gắn liền với if gần nhất. If(bt1) ; Else If (bt2) If(bt3) ; else ; /* bt3 = = 0 */ else ; /* bt2= = 0 */ *Vi ́ du ̣ : Viết chương triǹ h giải phương triǹ h bâc̣ nhất : Ax + B = 0 (A, B : số thưc̣ ). Giải : Xét các trường hơp̣ xảy ra : - Nếu A! =0 thi ̀ nghiêṃ x = -B/A - Nếu A = 0 +> B=0 => Nếu B=0 : vô số nghiêṃ B != 0 ( ngươc̣ lai)̣ : vô nghiêṃ . /* Giải phương triǹ h bâc̣ nhất : Ax + B = 0 */ #Include #Include void main ( void) { float a, b ; /* nhâp̣ dữ liêụ từ bàn phiḿ */
  19. print ( "\ nhâp̣ 2 số a,b : "); scanf(" %f %f ", &a, &b); /* giải phương triǹ h*/ If ( a= = 0 ) If( b= =0 ) Printf (" Phương triǹ h có vô số nghiêṃ ! \n " ); Else Printf (" phương triǹ h vô nghiêṃ \n "); Else / * a khác 0 */ Printf (" phương triǹ h có nghiêṃ là : x= %f \n ", -b/a); Printf( " ấn phiḿ bất kỳ tiếp tuc̣ "); Getche(); } Bài tâp̣ 1 : Tim̀ những lỗi cú pháp các đoaṇ chương triǹ hh sau : A/ scanf ( "d", value); B/ printf ("tić h các %d và %d là %d " \n, x,y); C/ printf (" phần dư của %d chia cho %d là \n ", x , y , x%y ); D/ if(x=y); Printf (" %d bằng %d \n ", x,y); E/ If ( age>=65);
  20. Printf (" gia i!̀ '); Else Printf(' Tre! '); 3.2.2 Lêṇ h switch - Cú pháp : Switch (biểu thức nguyên). { Case N1 : lêṇ h 1; Case N2 : lêṇ h 2; [ default : lêṇ h;] } - Biểu thức nguyên là giá tri ̣nguyên : Ni(i=1,2 ) là các số nguyên. - Với biểu thức khác với moị Ni => thưc̣ hiêṇ lêṇ h sau default. - Chú ý : nếu nhóm câu lêṇ h sau nhãn case Ni không có câu lêṇ h break thi ̀ máy sẽ chuyển sang nhóm câu lêṇ h sau nhãn case Ni+1 *Vi ́ du ̣ : đổi 1 số nguyên sang chuỗi ký tư ̣ là tên các môn hoc̣ #Include #Include main( ) {
  21. Int ma ; Do { printf(" \n cho mã cần chuyển "); scanf(" %d ", &ma); switch(ma) { case 0 : printf(" \n lớp tin hoc̣ a "); break; case 1 : printf( " \n lớp tin hoc̣ b"); break; case 2 : printf(" \n lớp trung cấp "); break; case 3 : printf (" \n lóp chuyên viên "); break; default : printf( " \n lơp̣ thiế tiền hoc̣ phi"́ ); } printf( " \n có tiếp tuc̣ không ?(Y/N)"); } while( toupper ( getch () ! = 'N '); /* Chuyển san ký tư ̣hoa */ } 3.2.3 / Lêṇ h For : - Cú pháp : for ( bt1; bt2 ; bt3) lêṇ h;
  22. - Giải thić h : + bt1 : là toán tử gán để taọ giá tri ̣ban đầu cho biến điều khiển. + bt2 : biểu thức điều kiêṇ để thưc̣ hiêṇ vòng lăp̣ . + bt3 : biểu thức tăng giá tri ̣của biến điều khiển của vòng lăp̣ . *Vi ́ du ̣: Tiń h Tổng S=1+2+3+ +n For ( int i=1, s=0; i<=n; s+ =i, ++i ); * Cơ chế hoaṭ đôṇ g : a/Tiń h giá tri c̣ ủa biểu thức bt1 . b/Tiń h giá tri c̣ ủa bt2 c/ + Nếu giá tri c̣ ủa bt2(=0) là sai máy sẽ ra khỏi lêṇ h For. + Nếu giá tri c̣ ủa bt2(!=0) là đúng thi ̀ máy sẽ thưc̣ hiêṇ lêṇ h. d/ Tiń h giá tri ̣của bt3 và quay lai ̣ bước kiểm tra 2(b) Chú ý : + Khi bt2 vắng măṭ thi ̀ nó đươc̣ coi là luôn luôn đúng * Vi ́ du ̣: for (i=0; ; i++) lêṇ h ; + bt1 , bt3 có thể bao gồm nhiều biểu thức cách nhau bởi dấu phẩy. + bt2 có thể gồm nhiều biểu thức, tuy nhiên tiń h đúng sai của nó đươc̣ xem là tiń h đúng sai của
  23. biểu thức cuối cùng. * Vi ́ du ̣ : tiń h tổng : S=1! + (1+2)! + + ( 1+2+ i )! ( 1 + 2 + n)! #include #include #include /* int i, j, t, n ; double gt, s; */ main() { int i, j, t, n ; double gt, s; clrscr () ; printf ("nhâp̣ n= "); scanf(" %d ", &n); Cách 1 : s=0 ; t=0; for (s= 0,t= 0,i=1; i<=n ; ++i ) { t=t+i; for ( gt=1,j=1; j<=t ; ++j) gt = gt*j ; s = s+gt; } printf ( " tong s = %15.0f ", s); getch(); Cách 2 : for ( s=0, t=1, i=1; i<=1; ++i , t = t + 1)
  24. { for ( gt=1,j=1;j<=t; ++j) gt*=j; s+= gt; } Cách 3 : thân for là câu lêṇ h rỗng For (s=0, t=1,i=1; i<=n; ++i, t=t+i,s+=gt) For( gt=1,j=1; j<=t; gt* = j , ++j ); Cách 4 : không có bt 1 và bt3; Int i=1, j=1, t=1, n ; double gt = 1, s= 0 ; For ( ; i<=n ; ++i, t = t + i , s+ = gt) /* không có biếu thức 1*/ { For ( ; j<=t ; ) /* không có bt1 , bt3*/ Gt* = j ++ /* gt = j ; ++j */ } Cách 5 : không có bt1, bt2, bt3 For (; ;) { for (; ;) { gt* = j++ ; if ( j < t ) goto tong ; } tong : s+ = gt; ++i , t = t + i ;
  25. if( i #include main () { Int dayso [ 10 ] ; int i = 0 ; While ( i < 10) { printf ( "\n Số thu %d : ", i ); scanf ( " %d", & dayso [i]);
  26. i ++ ; } 3.25/ Câu lêṇ h Do while ( làm trước hỏi sau ) - Cú pháp : do lêṇ h 1 ; while ( biểu thức 1 ) ; - Nguyên tắc thưc̣ hiêṇ : +b1. Máy thưc̣ hiêṇ câu lêṇ h 1 ; +b2. Sau đó tiń h giá tri c̣ ủa biểu thức 1, nếu giá tri ̣ của biểu thức 1 sai thi ̀ chương triǹ h thoát ra khỏi vòng lăp̣ . Nếu giá tri c̣ ủa biểu thức 1 đúng thi ̀ quay lai ̣ bước 1. Chú ý : - while : Điều kiêṇ đươc̣ kiểm tra trước, nếu đúng mới thưc̣ hiêṇ . - do while : câu lêṇ h đươc̣ thưc̣ hiêṇ trước khi kiểm tra. Câu lêṇ h thưc̣ hiêṇ bao giờ it́ nhất là 1 lần. ( do while ngươc̣ với Repeat until của Pascal : lêṇ h Do while sai thì dừng, còn lêṇ h repeat until đúng thi ̀ dừng ). -Biểu thức 1 có thể gồm nhiều biểu thức, tuy nhiên tiń h đúng sai căn cứ theo biểu thức cuối cùng. * Vi ́ du ̣: tiń h pi với sai số eps = 1E - 4 , pi = 4 - 4/3 + 4/5 - 4/7 + eps #include #include
  27. main () { float pi, dau, i , eps, saiso ; i=1.0; dau = -1; saiso = 1e -4 ; pi = 4.0; printf ( "\n đang xử lý vui lòng đơị !"); do { eps = 4.0 / ( 2.0 * i + 1.0 ); pi + = dau * eps ; dau = dau * - 1.0 ; i + = 1.0; } while ( eps > saiso ); printf ("\n số pi là : " % f ", pi ) ; getch (); } 3.2.6/ Câu lêṇ h Break : - Cú pháp : Dùng để thoát khỏi vòng lăp̣ . Khi găp̣ câu lêṇ h này trong vòng lăp̣ , máy ra khỏi và chỉ đến câu lêṇ h sau các lêṇ h trên. Nếu nhiều vòng lăp̣ > break sẽ thoát ra khỏi vòng lăp̣ gần nhất. 3.2.7/ Lêṇ h continue : - Cú pháp continue; : khi găp̣ lêṇ h này trong các vòng lăp̣ , máy sẽ bỏ qua phần còn lai ̣ trong vòng
  28. lăp̣ và tiếp tuc̣ thưc̣ hiêṇ vòng lăp̣ tiếp theo. - Đối với lêṇ h For máy sẽ tiń h lai ̣ biểu thức 3 (bt3) và quay lai ̣ bước 2. - Đối với lêṇ h while, do while máy sẽ tiń h lai ̣ giá tri ̣ của biểu thức 1 và quay lai ̣ bước 1. * Vi ́ du ̣: Nhâp̣ 1 chuỗi ký tư ̣kể cả ký tư ̣trống và bỏ qua các ký tư ̣ không hơp̣ lê ̣và kết thúc khi ấn ESC hoăc̣ số ký tư ̣vươṭ quá kić h thước mãng. char xau [MAXL], kytu ; int i = 0 ; while (1) /* luôn luôn đúng vòng lăp̣ viñ h cử u */ { kytu = getch ( ) ; if ( kytu = = 27 ) break ; if ( i >= MAXL ) break ; if ( kytu > 122 || kytu tiep tuc là nhãn của lêṇ h st = a [ i ]; - Lêṇ h goto nhãn => nhảy đến câu lêṇ h đứng sau nhãn.
  29. - CHÚ Ý : PHAṂ VI NHÃ N TRONG CÙ NG 1 HÀ M. BÀ I TÂP̣ CHƯƠNG 3 1/ Kiểm tra tim̀ lỗi : while ( x<.= 10 ) Total t=x; ++x ; 2/ Giải phương triǹ h bâc̣ 2 : ax2 + bx + c = 0 với a, b, c là số thưc̣ nhâp̣ từ bàn phiḿ . 3/ Nhâp̣ số liêụ vào bàn phiḿ , kết thúc nhâp̣ bằng cách ấn ^Z hoăc̣ F6 ( mũ = 255 ). Biết : InWord = on khi ở trong 1 từ. InWord = off khi ngươc̣ lai.̣ Đếm số dòng, số từ, số ký tư.̣ 4/ Tim̀ các số nằm trong khoảng từ 150 đến 140 thoả tiń h chất số bằng tổng lâp̣ phương các chữ số của chúng : Vi ́ du ̣: 153 = 13 + 53 + 33 hoăc̣ 370 = 33 + 73 + 03 5/ Số tuyêṭ hảo là số bằng tổng các ước số thưc̣ sư ̣ của nó. Vi ́ du ̣: 6 = 1 + 2 + 3.Tim̀ các số tuyêṭ hảo trong khoảng từ 1 đến 3000. 6/Nhâp̣ số liêụ vào mãng A gồm 10 phần tử và sắp xếp theo thứ tư ̣tăng dần. 7/ Tim̀ tất cả các số nguyên tố từ 2 đến 100 bằng
  30. lêṇ h For. 8/ Tim̀ các số nguyên có 3 chữ số sao cho tổng 3 chữ bằng tić h 3 chữ. Vi ́ du ̣: 123. 9/ a/ Dùng lêṇ h while để viết chương triǹ h tiń h : S1 = 1 x 3 x 5 x 7 x 9. . . . . x ( 2n - 1 ). S2 = 2 x 4 x 6 x 8 x x (2n). b/ làm lai ̣ bài trên bằng cách dùng do while. 10/Giải bài toán cổ điển vừa gà vừa chó bó lai ̣ cho tròn 36 con 100 chân chăñ . CHƯƠNG 4 : HÀM CHƯƠNG TRIǸ H VÀ CẤ U TRÚ C CHƯƠNG TRIǸ H. Chương triǹ h viết bằng ngôn ngữ C gồm 1 dãy các hàm trong đó có 1 hàm chiń h là main và chương triǹ h bắt đầu từ main. 4.1/ Khái niêṃ : - Hàm là đoaṇ chương triǹ h thưc̣ hiêṇ troṇ veṇ môṭ công viêc̣ nhất điṇh. - Hàm chia cắt viêc̣ lớn bằng nhiều viêc̣ nhỏ. Nó giúp cho chương triǹ h sáng sủa, dễ sử a, nhất là
  31. đối với các chương triǹ h lớn. 4.2/ Khai báo hàm : ( ) { } - Trong đó : + Tên hàm : buôc̣ phải có. + Danh sách các đối số : không bắt buôc̣ . Có hay không tuỳ theo chúng ta điṇh dùng hàm đó làm gi.̀ + Khai báo biến : Nếu Danh sách các đối số mà có thi ̀ phần này buôc̣ phải có. Còn nếu không thì ngươc̣ lai ̣ có thể bỏ qua. + Phần trong { } : là thân hàm. Dấu { } là bắt buôc̣ đối với moị hàm. + : ngay sau { và goị là biến cuc̣ bô ̣dành riêng cho hàm sử duṇ g. + đối số luôn luôn truyền theo tri (̣ không thay đổi giá tri)̣. *Vi ́ du ̣: Hàm tiń h giai thừa : S = x 1 /1! + x 2 /2! + + x n / n! Cách 1 :
  32. #Include #Include float giaithua ( int n) { int i ;float KQ ; for ( KQ=1,i =1 ; i # Include /*Khai báo prototype*/ muc̣ đić h hàm đăṭ ở đâu cũng đươc̣ không cần trước hàm goị float giaithua ( int n ); void main () {
  33. } /* Chi tiết hàm giai thừa */ float giaithua ( int n) { return KQ }; Chú ý : - Kiểu của hàm cùng kiểu giá tri c̣ ần trả về. - Các hàm đôc̣ lâp̣ , không đươc̣ lồng nhau. - Kiểu void tên hàm () : không cần trả về giá tri ̣ nào, hiểu ngầm là trả về int. - ở cách 1 : hàm ở trên không đươc̣ goị hàm dười. - ở cách 2 : các hàm goị đươc̣ lâñ nhau. 4.3 / Phaṃ vi của biến : - Chẳng haṇ trong vi ́ du ̣trên : biến n trong hàm main ( ) là cuc̣ bô ̣của main() chi ̉ có tác duṇ g trong hàm main() => trong hàm giai thừa có thẻ khai báo biến n trùng biến n của hàm main ( ) nhưng khác nhau và chi ̉ tồn tai ̣ trong 1 hàm. Vi ́ du ̣: float giaithua (m); { int n ; float KQ = 1.0; for ( n = 1; n<= m ; ++n ) 4.4 / Đê ̣quy : giống như trong Pascal : hàm goị đến chiń h nó. * Vi ́ du ̣: Tiń h giai thừa :
  34. giaithua ( n ); int n ; { if ( n = 0 ) return ( i ) ; else return (giaithua ( n - 1 )*n ); } - Chương triǹ h sử duṇ g đê ̣quy thi ̀ dễ hiểu nhưng không tiết kiêṃ đươc̣ bô ̣nhớ, không nhanh hơn. 4.5/ So sánh Lêṇ h trong Pascal và trong lâp̣ triǹ h ngôn ngữ C. - Giống nhau : + Cả Pascal và C đều có chương triǹ h con. - Khác nhau : Pascal Ngôn ngữ C Có thủ tuc̣ Chi ̉ có hàm Có hàm Hàm có thể khai báo kiểu void ( không trả về giá tri ṇ ào cả, giống như thủ tuc̣ của Pascal - Khai báo hàm function Tên hàm ( ; ̣ Begin end; tên hàm ( ) {
  35. ̣ Các câu lêṇ h } Khai báo biến : ; Vi ́ du ̣: Function max ( a, b : integer ) : integer Begin if a > b then max = a Else max = b ; End. Trả về giá tri ḅ ằng phép gán max = giá tri (̣ trong đó max là tên hàm ). Khai báo biến ; Vi ́ du ̣: int max ( a, b ) { If ( a > b ) return ( a ); else return ( b ); } - Trả về giá tri ḅ ằng câu lêṇ h return ( giá tri)̣ Kiểu tham số + Tham biến : truyền theo điạ chỉ + Tham tri :̣ truyền theo giá tri.̣ Tham biến trong Pascal Procedure swap ( var x, y : real );
  36. Var temp : real ; Begin Temp : = x ; x : = y ; y : = temp; End. - goị hàm : swap ( a, b) Kiểu tham số + Chi ̉ có tham tri.̣ + Muốn có tham biến bằng cách đưa con trỏ hiǹ h thức tham biến trong C. Tham biến trong C Void swap ( float *x, float * y ) { float temp ; temp = * x ; *x = * y ; * y = temp ; } swap ( &s, &b ) CHƯƠNG 5 : MẢ NG VÀ BIẾ N CON TRỎ 5.1/ Mảng : là tâp̣ hơp̣ của các biến cùng kiểu đươc̣ xếp liên tiếp nhau trong bô ̣nhớ trong. 5.1.1/ Mảng 1 chiều : a/ Khái niêṃ : [ ]
  37. Vi ́ du ̣: int a [5 ] ; => a [0] a[1] a[2] a [3] a [4] ( chi ̉ số chaỵ từ 0 đến n - 1 ). Char S [20] ; => 'A' 'B' 'X ' S[0]S[1] S[19] b/ Cách nhâp̣ số liêụ cho mảng từ bàn phiḿ ( có thể dùng hàm Random C). + Mảng số nguyên : Vi ́ du ̣: Nhâp̣ vào mảng số nguyên 5 phần tử #include #include #define n 5 main () { int a [ n ] ; int i ; for ( i = 0 ; i < n ; i ++ ) { printf ( " a [ %d ] = " , i ); scanf ( " % d" , & a [ i ]); } /* Xuất số liêụ mảng ra màn hiǹ h */ for ( i = 0 ; i < n ; ++ i) printf ( " \ n a [ % d ] = % d ", i , a [ i ]); getch (); }
  38. + Mảng số thưc̣ float : #include #include #define n 5 ; main () { float a [ n ] , tam ; scanf ( " % f " , &tam) ; /*nhâp̣ qua biến trung gian taṃ */ a [ i ] = tam ; c/Khởi taọ mảng : a [ 5 ] = { 1,2,3,5,4 }a[0]=1 a[2]=2 a[4]=4 d/ Mảng ký tư ̣: - là chuỗi ký tư ̣kết thúc bằng ký tư ̣NULL có mã ASCII là 0 . - Vi ́ du ̣ : char S [3] = { 'L', '0', 'P'] : chuỗi này không đúng do thiếu chỗ cho ký tư ̣ kết thúc là NULL. - Ta có thể gán : char S [ 4 ] = " Lop "; Ngôn ngữ C sẽ tư ̣đôṇ g ghi ký tư ̣kết thúc là NULL, tức là ' \0 '. char S[ ] = " Lop " ; Không cần khai báo số phần tử mãng. * Vi ́ du ̣1 : Nhâp̣ vàò môṭ mảng số nguyên sau đó
  39. sắp xếp theo thứ tư ̣tăng dần : #include #define n 5 main ( ) { int a [ n ] ; int i , j, t ; for ( i = 0 ; i > n ; i ++ ); { printf ( " nhâp̣ a [ % d] = " , i ); scanf ( " %d", & a [i ]); } /* Sắp xếp tăng dần */ for ( i = 0 ; i < n - 1 ; i ++) for ( j = i + 1 ; j < n ; j ++ ) if ( a [ i ] < a [j ] ) { t = a [ i ] ; a [ i ] = a [ j ]; a [j ] = t ; } /* in kết quả */ for ( i = 0 ; i < n ; i ++ ) printf ( " % 5d " , a [ i ] ); getch ( ); } Vi ́ du ̣2 : Làm lai ̣ vi ́ du ̣1 nhưng viết riêng hàm sắp
  40. xếp và truyền tham số cho mảng 1 chiều #include #include #define N 5 void sapxep ( int a [ ] , int n ); void main ( ) { int a [ N ] ; int i ; /* nhâp̣ 1 số liêụ cho mãng */ for ( i = 0 ; i < N , i ++ ) { printf ( " A [ %d ] = ", i ); scanf ( " %d ", & a [ i ] ); } /* goị hàm sắp xếp để sắp tăng dần */ sapxep ( a, N ); /* in kết quả */ for ( i = 0 ; i < N ; i ++ ) printf ( " %5d ", a [ i ] ); getch ( ); } /* hàm sắp xếp tăng dần */ void sapxep ( int a [ ], int n ) { int i, j, t ;
  41. for ( i = 0 ; i > n - 1 ; i ++) for ( j = i + 1 ; j a [ j ] { t = a [ i ] ; a [ i ] = a [ j ] ; a [j ] = t ; } * Vi ́ du ̣3 : chuyển đổi 1 chuỗi ký tư ̣thường thành Hoa. Chú ý : + Hàm tolower ( ch ) : đổi 1 ký tư ̣ ch thành thường. + Hàm toupper ( ch ) : đổi ký tư ̣ch thành Hoa. + Cả 2 hàm trên đều năm trong thư viêṇ : Giải : #include # include #define n 20 main ( ) { char s [ n ] ; int i ; for ( i = 0 ; i < n ; i ++ ) s[ i ] = toupper ( getchar ( ) ) ; /* nhâp̣ ký tư ̣ và đổi thành hoa lưu vào mãng */ /* kết xuất chuỗi s */ for ( i = 0 ; i < n ; i ++ )
  42. putchar ( s [ i ] ) ; /* putchar ( ch ) : in ký tư ̣ ch ra màn hiǹ h */ getch ( ) } Bài tâp̣ : 1/ viết chương triǹ h nhâp̣ số liêụ cho mảng A gồm N phần tử và mảng B gồm n phần tử , sau đó ghép 2 mãng A và B thành mãng C gồm m + n phần tử và sắp xếp tăng dần ( Bài này phải dùng hàm nhâp̣ số liêụ cho mảng và hàm sắp xếp). - Tiń h tổng các phần tử âm, dương, số chẳn, số lẽ và tổng tất cả các phần tử của mãng C [ m + n ].In các số lẻ trên 1 hàng và các số chăñ trên 1 hàng. - Nhâp̣ vào môṭ giá tri ̣và tim̀ xem giá tri ̣đó có thuôc̣ vào mãng C không. Nếu có in ra tất cả các phần tử tim̀ đươc̣ . 5.2/ Mãng nhiều chiều : a/ Khai báo : [ ] [ ] *Vi ́ du ̣1 : int a [ 3 ] [ 2 ] ; float b [ 3 ] [ 4 ] ; char c [5 ] [6 ] ; => a [ 0 ] [0 ] a [ 0 ] [ 1 ] a [ 1 ] [ 0 ] a [ 1 ] [ 1] a [ 2 ] [ 0 ] a [ 2 ] [ 1 ]
  43. Vi ́ du ̣2 : #define Hang 5 # define Cot 6 int a [ Hang ] [ Cot ] ; => ta có các biến chaỵ i ( chi ̉ số chaỵ từ 0 đến ( Dong - 1)). ta có các biến chaỵ j ( chi ̉ số chaỵ từ 0 đến ( Cot - 1 )) . a [0] [0] a [0][1] a [ 0 ][Cot - 1] a [1] [0] a [1][1] a [a][Cot - 1] . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . a[Dong-1][0] . . . . . . . . a[Dong-1][Cot-1] *Vi ́ du ̣ : Viết chương triǹ h tiń h tổng, tić h các số trong mãng số thưc̣ a[3][2] ; #include #define N 3 #define N 2 main ( ) { int i , j ; float a [M][N] ; float tong, tich, tam ; /* nhâp̣ số liêụ */ for ( i = 0 ; i < M ; i ++ ) for ( j = 0 ; j < N ; j ++ ) { printf ( " nhâp̣ a [ %d][%d] = " , i , j ); scanf ( " %f " , & tam ) ; a [i][j] = tam ;}
  44. /* tiń h tổng */ Tong = 0 ; Tich = 1; for ( i = 0 ; i < M ; i ++ ) for ( j = 0 ); j < N ; j ++ ) { Tong = Tong + a [ i ][j] ; Tich = Tich * a [i][j] ; } /* in kết quả */ printf ( " Tổng là tổng = %f, TONG ); printf ( " tić h là TICH = %F, TICH ); getch ( ) ; } b/ Truyền tham số mãng nhiều chiều cho hàm ( tham số thưc̣ là tên mãng nhiều chiều ) - giả sử a là mãng 2 chiều : float a[M][N] + Chương triǹ h goị : { float a [M][N] Tong ( a ) ; ( truyền điạ chi ̉ của mãng cho hàm ) } + Chương triǹ h bi g̣ oị ( chương triǹ h con ) : float tong ( float a[ ][N] ) /* khai báo đối để nhâṇ điạ chi ̉ của mãng */ { }
  45. Note : hàm tong chi ̉ dùng đươc̣ đối với các mãng hai chiều có N côṭ và số hàng không quan troṇ g, không khai báo ) : * Vi ́ du ̣: Viết chương triǹ h tiń h tổng của 2 ma trâṇ cấp m x n theo công thức : C[i][j] = a[i][j] + b [i][j] #include #define m 3 #define n 4 /* các prototype ( khai báo hàm )*/ void nhap ( int a[ ][N] , int M, int N ); void TongMT ( int a[ ][N], int b[ ][N] , int c [ ] [N], int M , int N ); void InMT ( int c [ ][N], int M, int N ); /* chương triǹ h chiń h */ { int a [M][N], b[M][N], c[M][N] ; /* goị các hàm */ Nhap ( a, M ,N ) ; nhap ( b, M,N); TONGMT ( a, b, c , M, N ); InMT ( c, M, N ); Getch ( ) ; } /* Hàm nhâp̣ số liêụ cho mãng 2 chiều m x n phần tử */
  46. void Nhap ( int a [ ][N] , int M , int N ) { int i , j ; for ( i= 0 ; i < M ; i ++ ) for ( j = 0 ; j < N ; j++ ) { printf ( " a[%d][5d] = " , i , j ) ; scanf ( " %d " , &a [i][j]) ; } return ; } Void TongMT ( int a [ ][N], int b [ ][N], int c [ ] [N], int M , int N ) { int i, j ; for ( i = 0 ; i < M ; i ++ ) for ( j = 0 ; j < N ; j ++ ) c [i][j] = a [i][j] + b [i][j] ; return ; } /* in kết quả */ void inMT ( int c[ ][N], int M, int N ) { int i, j ; for ( i = o ; i < M ; i ++ )
  47. { for ( j = 0 ; j < N ; j ++ ) printf ( " % 3d", a[i][j] ); printf ( " \n " ) ; /* xuống dòng */ } return ; } BàI TâP̣ MãNG : 1/ cho mãng 2 chiều A, là ma trâṇ vuông cấp n x n , lâp̣ chương triǹ h : a/ tiń h tổng tất cả các phần tử dương của mãng. b/ tiń h tổng các phần tử A[i][j] mà i + j chia hết cho 5 . c/ In ra các số nguyên tố theo từng hàng. d/ Sắp xếp theo hàng. e/ Sắp xếp theo côṭ . f/ Tiń h tổng các phần tử trên đường chéo ( i = j ) , đường biên. g/ Tim̀ max ; min theo từng hàng, côṭ và toàn bô ̣ ma trâṇ . 2/ Môṭ chuỗi goị là palindrone nếu nó không thay đổi khi ta đảo ngươc̣ thứ tư ̣của các ký tư ̣trong nó ( vi ́ du ̣" 12321 " ) . Lâp̣ chương triǹ h đoc̣ môṭ chuỗi ( xâu ) ký tư ̣và xác điṇh xem có tiń h palondrone không.
  48. 5.3/ Biến con trỏ : 5.3.1/ Khái niêṃ con trỏ ( pointer ) và điạ chi ̉ : - Mỗi biến trong ngôn ngữ C đều có 1 tên và tương ứng với nó là môṭ vùng nhớ dùng để chứa giá tri ̣của nó. Tuỳ theo biến mà vùng nhớ dành cho biến có đô ̣dài khác nhau. Điạ chi ̉ của biến là sô thứ tư ̣của byte đầu tiên tương ứng với biến đó. Điạ chi ̉ của biến có kiểu khác nhau là khác nhau. Điạ chi ̉ và biển kiểu int liên tiếp cách nhau 2 byte , biến kiểu float là 4 byte. - Con trỏ là biến dùng để chứa điạ chi ̉ của biến khác hoăc̣ có thể là môṭ hàm. Do có nhiều loai ̣ điạ chi ̉ nên cũng có nhiều loai ̣ biến con trỏ. Con trỏ kiểu int dùng để chứa điạ chi ̉ của kiểu int. Con trỏ kiểu float dùng để chứa điạ chi ̉ kiểu float. - Muốn sử duṇ g đươc̣ pointer, trước tiên phải có đươc̣ điạ chi ̉ của biến mà ta cần quan tâm bằng phép toán lấy điạ chi ̉ & . Kết quả của phép lấy điạ chi ̉ & sẽ là 1 phần tử hằng. * Vi ́ du ̣: int num ; => &num là điạ chi ̉ của num. int pnum ; /* pnum là 1 pointer chi ̉ đến môṭ int */ pnum = & num ; /* pnum chứa điạ chi ̉ biến int num*/ giả sử : num = 5 ; => * pnum = 5 /* do * là toán tử
  49. nôị dung */ Hai câu lêṇ h sau đây là tương đương Num = 100 ; ( * pnum ) = 100 ; - Quy tắc khai báo biến con trỏ : * *Vi ́ du ̣2 : int a, *p ; a = 5 ; /* giả sử điạ chi ̉ của a là */ p = & a ; /* p = */ p = a ; /* phép gán sai */ * p = a ; /* phép gán đúng */ scanf ( " %d " , &a ) ; tương đương scanf ( " %d , p ) ; 5.3.2/ tiń h toán trên biến con trỏ ( pointer ) a/ Hai biến con trỏ cùng kiểu có thể gán cho nhau : Vi ́ du ̣1 : int a, * p, *a ; float * f; a = 5 ; p = &a ; q = p ; /* đúng */ f = p ; /* sai do khác kiểu */ f = ( float * )p ; /* đúng nhờ ép kiểu con trỏ nguyên về kiểu float */ Vi ́ du ̣2 : int a ; char *c ; c = &a ; /* sai vi ̀ khác kiểu */
  50. c = ( char*) /* đúng */ b/ Môṭ biến pointer có thể đươc̣ côṇ g, trừ với môṭ số nguyên ( int , long ) để cho kết quả là môṭ pointer. * Vi ́ du ̣: int a , *p , * p10 ; a = 5 ; p = &a ; p10 = p + 10 ; Vi ́ du ̣: int V[10] ;/* mãng 10 phần tử */ int *p ; p = & V[0]; for ( i = 0 ; i < 10 ; i ++ ) { *p = i ; /* gán giá tri ị cho phần tử mà p đang trỏ đến */ p ++ /* p đươc̣ tăng lên 1 để chi ̉ đến phần tử kế tiếp */ } /* kết quả V[0] = 0 , V [ 1] = 1 V[9] = 9 * / c/ Phép trừ 2 pointer cho kết quả là môṭ số int biểu thi ḳ hoảng cách ( số phần tử ) giữa 2 pointer đó. d/ Phép côṇ g 2 pointer là không hơp̣ lê,̣ pointer không đươc̣ nhân chia với 1 số nguyên hoăc̣ nhân chia vơi nhau. e/ p = NULL : là con trỏ p không trỏ đến đâu cả.
  51. Chú ý : không đươc̣ sử duṇ g biến con trỏ khi chưa đươc̣ khởi gán . Vi ́ du ̣: int a , *p ; Scanf ( "%d", p ) ( sai ) => thay bằng các lêṇ h : p = &a và scanf ( "%d" p ) ( đúng) 5.4/ Con trỏ mảng : 5.4.1/ Mãng 1 chiều và con trỏ : - Trong ngôn ngữ C : giữa mãng và con trỏ có mối quan hê ̣ chăṭ chẽ. Các phần tử của mãng có thể xác điṇh nhờ chi ̉ số hoăc̣ thông qua con trỏ. - Vi ́ du ̣: int A[5] ; * p ; P = A ; + mãng bố tri ́ 5 ô nhớ liên tiếp ( mỗi ô chiếm 2 byte ). + Tên mãng là 1 hằng điạ chi ̉ ( không thay đổi đươc̣ ), chiń h là điạ chi ̉ của phần tử đầu tiên. => A tương đương với &A[0] (A + i ) tương đương với &A[i] *(A + i ) tương đương với A[i] p = A => p = &A[0] ( p trỏ tới phần tử A[0]) *(p + i ) tương đương với A[i]. =>bốn cách viết như sau là tương đương : A[i], * ( a + i ), * ( p + i ), p[i].
  52. Vi ́ du ̣2 : int a [5] ; *p ; p = a ; for ( i = 0; i #define n 5 main ( ) { int a [n], t , *p, i , j, ; int s ; p = a ; for ( i = 0; i * ( a + j )
  53. { t = * ( a + i ) ; *(a + i ) = * ( a + j) ; *(a + j ) = t ; } s= 0 ; for ( j=0 ; i gồm 2 x 3 = 6 phần tử có 6 điạ chi ̉ liên tiếp theo thứ tư ̣sau : Phần tử : a[0][0] a[0][1] a[0][2] a[1][0] a[1][1] a[1][2] ( * ) Điạ chi ̉ : 0 1 2 3 4 5 - Ngôn ngữ C quan niêṃ mãng 2 chiều là mãng
  54. môṭ chiều của mãng a[2][3] tương đương không phần tử mà mỗi phần tử của nó gồm 3 số nguyên nên : a trỏ tới hàng thứ nhất ( a [0][0] ) a+1 trỏ tới hàng thứ hai ( a[1][0] ) - Do đó để duyêṭ các phần tử của mãng a[2][3] ta dùng con trỏ theo cách sau : + ( theo * ) => ta có công thức a[i][j] = ( int*) a + i * n + j trong đó : int* : con trỏ a ( điạ chi ̉ a ). n : số côṭ. - float a[2][3] , *p ; p = ( float*)a ; /* chú ý lêṇ h này */ khi đó : p trỏ tới a[0][0] /* p = & a[0][0] */ p + 1 trỏ tới a[0][1] /* *(p+1) = a[0][1] */ P + 2 trỏ tới a[0][2] . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . p + 5 trỏ tới a[1][2] /* *(p+5) = a[1][2] */ * Tổng quát : a[i][j] = * ( p + i* N + 5 ); trong đó N : số côṭ ) Kết luâṇ : Mãng 2 chiều có thể chuyển thành mãng 1 chiều nhờ con trỏ. * Vi ́ du ̣ : để nhâp̣ môṭ số liêụ vào mãng 2 chiều kiểu float a[2][3] ta có thể dùng các cách sau:
  55. + Cách 1 : #include " stdio.h " main ( ) { float a[2][3] , *p ; int i ; p = (float*)a ; /* lưu ý lêṇ h này */ for ( i = 0 ; i < 2*3 ; ++i) scanf ( "%f", (p+i)) ; /* (p_+ i ) là điạ chi ̉ */ ( X ) } + Cách 2 : Sử a lêṇ h ( X ) như sau : scanf ( "%f", (float*)a + 1 ) ; + Cách 3 : #include " stdio.h " #define m 2 #define n 3 main ( ) { float a[m][n] ; int i , j ; float *p ; p = ( float* )a ; for ( i=0 ; i<m ; i++ ) for ( j=0 ; j<n ; j++ ) scanf ( "%f" , ( p +i*n + j ) hoăc̣ lêṇ h scanf ( " %f" , ( float *)a + i * N + j )); } + Cách 4 : sử duṇ g biến trung gian : #include " stdio.h"
  56. #define dong 2 #define cot 3 main ( ) { float a[dong][cot] , tam ; int i , j ; for ( i = 0 ; i [ ]. * Vi ́ du ̣: int *a[5] ; - trong đó : a là mãng gồm 5 ô nhớ liên tiếp, mỗi ô nhớ là 1 biến con trỏ trỏ đến kiểu int ; bản thân a không thể dùng để lưu trữ số liêụ . - Giả sử : a a[0] a[1] a[2] a[3] a[4] a[5]
  57. Điạ chi ̉ 7 8 9 10 11 1 2 3 4 5 6 12 13 - a= &a[0] => a = ( điạ chi ̉ 100 ). - a[0] = ( điạ chi ̉ bằng 30 : tai ̣ điạ chi ̉ 30 con trỏ a[0] trỏ đến điạ chi ̉ và giả sử tai ̣ điạ chi ̉ có giá tri ḷà 6 ). => *a[0] = * ( > = 6 . a[1] = => *a[1] = 1 a [2] = => *a[2] = 7 . Chú ý 1: Xem a là con trỏ 2 lần ( con trỏ của con trỏ ) : - a = => *a = ( do a = &a[0] ) => a = 6 ( do *( )). - *(*(a + 1) + 2 ) *(102) * ( + 2 ) => * = 3 Chú ý 2 : - int a[5] => a là con trỏ hằng không thay dổi điạ chi ̉ của nó đươc̣ ( nên a++ sai) - int *a[5] ; => a laf con trỏ đôṇ g nên thay đổi giá
  58. tri đ̣ ươc̣ ( a++ đúng ). Vi ́ du ̣: int *a[5] For ( i = 0 ; i b[k] = a[i] [k] ; + Công thức : ( a[i] = *(a+i)) => ( b[i] = *(b+i)). b[k] = *(b+k)). b[k] = *(a[i] + k ) = * ( *(a+i) + j). => a[i][k] = *(*(a+i) + k) ; trong đó *(*(a+i) là con trỏ 2 lần. 5.4.4/ Con trỏ và xâu ký tư ̣: - Xâu ký tư ̣: là dãy ký tư ̣đăṭ trong ngoăc̣ kép . Ví du ̣ : " Lớp hoc̣ ". Xâu này đươc̣ chứa trong 1 mãng kiểu char. L O P H O C \0 Điạ chi ̉ : NULL : kết thúc chuỗi => char *lop ;
  59. lop = " Lop Hoc " ; Đúng : gán điạ chi ̉ của chuỗi cho con trỏ lớp. + puts (" Lop Hoc ") ; và puts (lop ) đểu hiển thi ̣ dòng chữ Lop Hoc. Vi ́ du ̣: char Tenlop[10] ; Printf ("\n Tenlop : " ) ; gets( Tenlop ) ; => ( Nhâp̣ vào chuỗi " lớp hoc̣ " ) Còn nếu chúng ta khai báo như sau là sai : Char *lop , tenlop [10] ; Tenlop = " lớp hoc̣ " ; sai vi ̀ Tenlop và chuỗi là 2 con trỏ hằng , không đươc̣ gán cho nhau . Muốn gán ta dùng hàm strcpy (Tenlop , "lớp hoc̣ "); 5.4.5/ Con trỏ và viêc̣ điṇh vi ḅ ô ̣nhớ đôṇ g : - Vi ́ du ̣1 : #define N=10 ; main ( ) { int a[N] ; int m : printf ( " nhâp̣ số phần tử m = "); scanf("%d", &m) ; for ( i= 0 ; i N ( tức là m > 10 ) : thi ̀ chương triǹ h
  60. sẽ chaỵ sai vi ̀ ta không đủ biến mãng. => Do đó ta phải khắc phuc̣ bằng cách : điṇh vi ḅ ô ̣ nhớ đôṇ g. ( Bằng hàm malloc và calloc). * Vi ́ du ̣2 : #include #include hoăc̣ #include main ( ) { int m , *a ; printf (" Nhâp̣ số phần tử m = " ); scanf ( "%d", &m ); /* Cấp phát và điṇh vi ḅ ô ̣nhớ đôṇ g */ a = ( int*) malloc ( m* size of ( int ) ); (1) if ( a!= NULL ) /* cấp phát thành công */ for ( i=0 ; i . Hàm này cung cấp số lươṇ g byte liên tiếp từ phần bô ̣nhớ còn chưa sử duṇ g trên máy tiń h. + Vi ́ du ̣: malloc (num) = num byte và trả về con trỏ kiểu void trỏ đến điạ chi ̉ bắt đầu của ô nhớ. - Size of ( int ) : là số byte mà môṭ biến kiểu int yêu cầu ( giá tri =̣ 2 )
  61. - ( int*) : ép kiểu ( type - casing) : coi điạ chi ̉ bắt đầu là int ( do malloc trỏ về con trỏ kiểu void , đăc̣ biêṭ không có kiểu ) , có thể nhâṇ bất kỳ điạ chỉ kiểu nào ( nhờ ép kiểu ). - Muốn sử duṇ g hàm calloc thay cho hàm malloc => khai báo : a = (int*) calloc ( n, size of (int)); * Chú ý : Luôn gán môṭ điạ chi ̉ cho môṭ con trỏ trước khi sử duṇ g tới nó. Nếu không biến con trỏ sẽ mang môṭ giá tri ̣ngâũ nhiên có thể phá huỷ chương triǹ h. * Cấp phát bô ̣nhớ đôṇ g cho mãng 2 chiều m x n phần tử , m , n nhâp̣ từ bàn phiḿ : + Vi ́ du ̣: #include #include Void main ( ) { int a , m, n, OK ; printf ( " nhâp̣ m = " ); scanf ("%d", &m); printf (nhâp̣ m = n) ; scanf ( "%d", &n ); a = ( int ) malloc ( m*seze of (int *)); if (a!=NULL ) /*Cấp phát thành công */ { OK = 1 ; for ( i=0 ; i < m ; i++ ) } /* giá tri ḅ an đầu cho biến con trỏ*/
  62. a[i] = (int*) break ; for ( i=0 ; i <m ; i ++ ) { if !(OK) break ; a[i] = (int*) malloc ( n * size of (int)); if ( a[i] = NULL ) OK = 0 ; } if(OK) { sử duṇ g a[0][0] , a[0][1] , a[i][j] , a[m][n] } /* giải phóng vùng nhớ cấp phát */ if ( a!=NULL ) { for ( i = 0 ; i < m ; i++) if ( a[i] ! = NULL , free ( a[i]); free (a); } } * Chú ý : ta xem mãng 2 chiều là mãng 1 chiều nên có thể khai báo : a = (int*) malloc ( m*n * size of ( int )); VÀ A[I][J] = A[ I*N + J] BàI TâP̣ : 1/ Làm lai ̣ các bài tâp̣ phần mãng nhưng dùng con trỏ .
  63. 2/ Dùng hàm malloc hay calloc nhâp̣ mãng n phần tử , sau đó tiń h tổng các phần tử và sắp xếp mãng giảm dần. 3/ Dùng hàm malloc hay calloc nhâp̣ ma trâṇ m x n , sau đó tiń h tổng và sắp xếp theo tăng dần 5.4.6/ Mối liên hê ̣ giữa con trỏ và các khái niêṃ quan troṇ g trong : a/ Con trỏ và hàm : - Chú ý 1 : bản thân tham số truyền cho hàm không bao giờ bi ̣thay đổi. Nhưng nếu tham số là con trỏ thi ̀ giá tri c̣ ủa nó không thay đổi nhưng nôị dung đươc̣ chứa ở điạ chi ̉ đó lai ̣ có thể thay đổi. - Chú ý 2 : Truyền cho hàm môṭ tham số hiǹ h thức đươc̣ khai báo là con trỏ, và khi goị hàm truyền cho nó môṭ giá tri đ̣ iạ chi ̉ của biến muốn thay đổi. - Vi ́ du ̣ :giả sử tân xây dưṇ g môṭ hàm dùng để hoán vi ḅ iến thưc̣ , ta viết như sau : Cách 1 : #include void swap (float x , float y ) /* cách 1 sai */ { float temp ; temp = x ; s<y ; y = temp; } main ( )
  64. { float a, b ; a = 10.0 ; b = 20.0 ; printf (" khi chưa hoán vi ̣a = %4.0f; b = %4.0f \n" , a , b ) ; swap ( a , b ) ; printf ( " sau khi hoán vi ̣a = %4.0f ; b = %4.0f \n" , a, b ) ; - Phân tić h cái sai của cách 1 của vi ́ du ̣trên : + Do a, b thuôc̣ hàm main ( ). Khi khai báo sẽ dùng 2 khoảng nhớ ( mỗi khoảng 3 byte) . a, b trong lời goị hàm swap(a,b) là 2 tham số thưc̣ . + Các đối x, y và biến cuc̣ bô ̣ temp đươc̣ cung cấp khoảng nhớ nhưng điạ chi ̉ khác. Do đó xx, y chi ̉ tồn tai ̣ ở hàm swap(_), còn a, b tồn tai ̣ suốt cả quá triǹ h của chương triǹ h nên hàm swap () không làm thay đổi ( tức hoán vi)̣ đươc̣ giá tri c̣ ủa a và b => hàm viết theo cách 1 không đaṭ yêu cầu => yêu cầu viết lai ̣ theo cách 2. * Cách 2 : void swap (float *x , float *y) /* viết đúng*/ { float temp ; temp = *x ; *x = *y ; * y = temp ; } main ( ) b/ Số hoc̣ con trỏ ( có thể thao tác số hoc̣ trên nôị
  65. dung con trỏ ) * Vi ́ du ̣: #include #include main ( ) { #define N 3 int *list , i ; list = int*) calloc ( N, size of(int)); *list = 15 ; * (list + 1) = 20 ; *(list + 2 ) = 30 ; printf ( " các điạ chi ̉ là : "); for ( i=o ; i list trỏ tới môṭ dãi bô ̣nhớ dài 6 byte ( 3*2) có các giá tri ̣là 5,20, 30 . giá tri ̣điạ chi ̉ đầu là 06A => kết quả các điạ chi ̉ là : 06A 06AC 06AE chứa các giá tri ḷà : 5 20 30 c/ Con trỏ và mãng : - Vi ́ du ̣2 : #include
  66. main ( ) { #define N 3 int list [N] , i ; list [0] = 5 ; list [1] = 20 ; list[2]=30; printf ( " Các điạ chi ̉ là : "); for ( i = 0 ; i { list + i) = = &(list[i]) và *(list + i) = = list[i]} d/ Con trỏ và cấu trúc : - Ta có thể khai báo con trỏ như môṭ biến cấu trúc, cũng như con trỏ của bấu kỳ kiểu dữ liêụ nào khác. Điều này cho phép taọ môṭ danh sách móc nối các phần tử ( sẽ triǹ h bày chương sau ). e/ Con trỏ tới hàm : dùng để chứa điạ chi ̉ của
  67. hàm. Nên kiểu của hàm và con trỏ phải giống nhau. Vi ́ du ̣: #include Double fmax ( double x, double y ) /* hàm tiń h max của 2 số */ { return ( x>y ? x:y ) ; } /* khai báo và gán tên hàm cho con trỏ hàm */ double (*pf) (double , double ) = fmax ; main ( ) { printf ( " In max = % f " , pf(15.5, 20.5 )); } CHƯƠNG 6 : MÔṬ SỐ HÀM TRÊN CHUỖI KÝ TỰ 6.1/ Ký tư ̣( character ) : - Vi ́ du ̣: char ch , ch1 ; ch = 'a' ; /* Đúng : ký tư ̣chữ */ ch1 = '1' /* đúng : ký tư ̣số */ - Vi ́ du ̣2 : scanf ( "%c", &ch ) ; /* gõ A và Enter */ printf ("%c", ch) ; /* In ra chữ A */ printf("%d", ch) ; /* In ra 65 là mã ASCII của A
  68. */ * Hàm dùng cho kiểu ký tư ̣: char ch ; ch = getchar ( ) ; ( Nhâp̣ 1 ký tư ̣ từ bàn phiḿ m sau khi ấn Enter và ký tư ̣nhâp̣ vào không hiêṇ lên màn hinh ). putchar (ch) ; in ký tư ̣ nằm trong biến ch ra màn hiǹ h. putch ("\n") ; đưa dấu nháy về đầu dòng.ch = getche ( ) ; Nhâp̣ 1 ksy tư ̣ từ bàn phiḿ và ký tư ̣ nhâp̣ vào sẽ hiển thi ṭ rên màn hiǹ h. 6.2/ Chuỗi ký tư ̣ : Ngôn ngữ C quan niêṃ 1 chuỗi ký tư ̣ là môṭ mãng ký tư ̣ kết thúc bằng ký tư ̣ NULL ('\0') mã ASCII là 0. - Vi ́ du ̣: char s[10] L E V A N A '\0' s[0] s[1 ] s[3] s[4] s[5] s[7] s[8] - Muốn nhâp̣ chuỗi ta thường dùng hàm gets(s) - Muốn in chuỗi ta thường dùng hàm puts(s) : in xong xuống dòng. 6.3/ Môṭ số hàm trên chuỗi : các hàm cơ bản trong thư viêṇ string.h a/ gets(s1) : nhâp̣ dữ liêụ vào chuỗi s1. b/ n = strlen(s1) : cho biết đô ̣dài của chuỗi s1. c/ n= strcmp (s1,s2) : so sánh 2 chuỗi s1,s2 ( so
  69. theo mã ASCII từng ký tư ̣). + nếu n>0 : s1> s2 n = 0 : s1=s2 n strcat(s1,s2 ) ; => " ABCABE"; f/ m = strncmp (s1, s2, n ) ; so sánh n ký tư ̣ đầu tiên của chuỗi s1 với s2. - Vi ́ du ̣: m = strncmp ( s1, s2, 2 ) ; thi ̀ m = 0 do 2 ký tư ̣đầu của chuỗi là : + s1 : "ABC" và s2 : " ABE" là giống nhau. g/ strnpy ( s1, s2, n ) ; chép n phần tử đầu tiên của chuỗi s2 vào chuỗi s1. - Vi ́ du ̣: strnpy ( s1, "xyz", 2 ) ; Puts (s1); -ă " xyC". h/ strncat ( s1,s2, n) ; nối n phần tử đầu tiên của s2 vào đuôi s1.
  70. - Vi ́ du ̣: strncat ( s1 , "xyz", 2); Puts(s1) ; => "ABCxy". * Chú ý : + char s1[10], s2[4] + strcpy (s1,"ABCDE"); + strcpy(s2,"ABCDE"); => "ABCD" ( do s[4] = "\0"). i/ Hàm strstr : - char *p ; p = strstr (s1,s2); - Tim̀ xem chuỗi s2 có trong s1 hay không. Nếu có thi ̀ in ra cuỗi s1 tai ̣ vi ṭ ri ́ đầu tiên mà nó thấy. Nếu không có thi ̀ in ra giá tri Ṇ ULL. - Vi ́ du ̣: s1: "abc abc ac" s2 : "bc", s3 = "cd" p= strstr (s1,s2); puts (p) ; => " bc abc ac " p = strstr ( s1, s3) Đoán thử puts(p) ; => p[(NULL)] . k/ d= atoi ( chuỗi số ) ; chuyển chuỗi số thành int. f = atof ( chuỗi số ) ; chuyển chuỗi số thành số thưc̣ ( float ). l = atol(chuỗi số ); chuyển chuỗi số thành long ( nguyên 4 byte). - Vi ́ du ̣: char s[20] ;
  71. Gets (s) ; nhâp̣ vào s từ bàn phiḿ chuỗi " 123.45" d=atoi(s) ; thi ̀ d = 123. F = atof(s); thi ̀ f = 123.45 l/ toupper (ch) ; làm thay đổi ký tư ̣ ch thành chữ Hoa. tolower(ch); làm thay đổi ký tư ̣ ch thành chữ thường. * Chú ý :Muốn dùng các hàm về chuỗi phải khai báo đầu chương TRÌNH #INCLUDE <STRING.H> BàI TâP̣ : 1/ Nhâp̣ vào chuỗi sau đó xoá các khoảng trắng xong in ra màn hiǹ h. 2/ Nhâp̣ chuỗi và xoá các khoảng trắng thừa phiá trước, sau và giữa 2 từ gút lai ̣ 1 khoảng trắng. 3/ Viết hàm nhâp̣ vào môṭ chuỗi sau đó đổi ký tư ̣ đầu mỗi từ (chữ) thành Hoa, các ký tư ̣còn lai ̣ của 1 từ là chữ thường. 4/ Nhâp̣ chuỗi password nếu kiểm tra đúng mới cho chaỵ chương triǹ h đếm số từ trong 1 chuỗi số nguyên âm, phu ̣âm. 5/ Đảo thức tư ̣ các từ của chuỗi. Vi ́ du ̣: s1="con mèo con cắn con chó con" đổi thành s2=" con chó con cắn con mèo con".
  72. CHƯƠNG 7 : KIỂ U CẤ U TRÚ C - Khái niêṃ : Cấu trúc là môṭ kiểu dữ liêụ kiểu bản ghi(record) , cho phép nhiều loai ̣ dữ liêụ đươc̣ nhóm lai ̣ với nhau. ( Khái niêṃ cấu trúc trong C tương tư ̣như pascal hay Foxpro). 7.1/ Khai báo kiểu cấu trúc : a/ struct tên _ kiểu cấu trúc { khai báo các thành phần của nó ( các field và kiểu dữ liêụ của field) } ; - Vi ́ du ̣1 : struct kieu HV ò-> tên kiểu cấu trúc. { char Ten[30] ; int namsinh ;float diemTB ; } HV ; ( biến HV) - Vi ́ du ̣2 : struct kieu HV { các thành phần } struct kieu HV HV ; /* khai báo biến theo cách 2 */ b/ Dùng toán tử typedef để khai báo kiểu cấu trúc
  73. ( điṇh nghiã kiểu mới) ; - Vi ́ du ̣3 : typedef struct { char Ten[30] int namsinh ; float diemTB ; } kieu HV ; kieu HV Hoc vien ; kieu HV DSLop[20]; kieu HV Lop[ ] = { { "nguyễn văn Đông", 1980, 10.0}, { " Trần văn Tây", 1982, 5.5}, { " Phaṃ văn Nam ", 1979, 9.5} }; - Vi ́ du ̣4 : struct ngay{ int ngay ; char Thang[10]; int nam ; } ; type struct { char Ten[30] ; ngay namsinh ; /* thành phần cấu trúc có kiểu cấu trúc*/ float diemTB; } kieu HV ; kieu HV HV;
  74. * Chú ý : - Khai báo struct phải nằm ở vi ̣tri ́ toàn cuc̣ của chương triǹ h, thường sau các #include. - Cấu trúc thường dùng để xây dưṇ g môṭ bảng các cấu trúc. + Vi ́ du ̣ : kieu HV DSLop[30] ; struct kieu HV person[50]; - Có thể truyền cấu trúc như môṭ tham số hiǹ h thức, nhưng với những cấu trúc kić h thước lớn sẽ không tối ưu về thời gian lâñ đô ̣ nhớ. Khi không nên sử duṇ g con trỏ cấu trúc. + Vi ́ du ̣: struc kieu HV *HV ; 7.2/ Truy câp̣ đến các thành phần của kiểu cấu trúc : Tên cấu trúc. Tên thành phần Hoăc̣ Tên cấu trúc. Tên cấu trúc con. Tên thành phần. - Vi ́ du ̣: + nhâp̣ vào tên, năm sinh, điểm cho biến cấu trúc hoc̣ viên ( vi ́ du ̣3). gets(hoc vien.ten) /* nhâp̣ " Phaṃ thi ̣ Bắc" và Enter */ scanf("%d ", & hoc vien.namsinh ); scanf("%f", &tam); hoc vien.diem = tam; (*) + Nhâp̣ năm sinh cho biến hoc̣ viên ở vi ́ du ̣4 :
  75. scanf("%d",&hv.ngay.namsinh); * Chú ý : Nếu các thành phần không phải là nguyên(int) => nhâp̣ qua trung gian như (*). puts(hoc vien.ten); => " Phaṃ thi Ḅ ắc" printf("%d%f", hoc vien.namsinh, hoc vien.diemTB); * Lêṇ h gán : + Ta có thể gán 2 biến cấu trúc có cùng kiểu cho nhau : Vi ́ du ̣: hv2=hv1; + Gán giá tri ̣đầu cho biến cấu trúc và khai báo môṭ mãng cấu TRÚ C( XEM VÍ DU ̣ 3) BàI TâP̣ : viết chương triǹ h nhâp̣ danh sách hoc̣ viên gồm các trường ho ̣ tên, tuổi, điểm, và tim̀ kiếm trong dánhách có ai tên " Phaṃ Tèo " không. Tên Tuổi điểm HV [ 0] Nguyễn A 20 5.5 HV [1] Trần B 22 6.5 HV [2] Phaṃ Tèo 25 8.5 HV [3] Lê C 21 7.5 #include #define n 10 typedef struct { char Ten[30]; int tuoi ;
  76. float diem ; } kieu HV ; kieu HV HV[11] void main( ) { int i ; float tam ; kieu HV HV; /* nhâp̣ dữ liêụ cách 1*/ for ( i = 0 ; i < n ; i++) { printf ("\n Nhâp̣ số liêụ cho hoc̣ viên thứ %d", i ) ; printf (" Ho ̣và tên = " ) ; gets ( hv[i].ten); printf ("tuổi = "); scanf ( "%d" , &hv[i].tuoi); printf("điểm = "); scanf ("%f*c", &tam ); hv[i].diem = tam ; } /* cách 2 nhâp̣ vào biến cấu trúc và gán hv[i] = h */ for ( i = 0 ; i<n ; i++ ) { printf("Ho ̣và tên = "); gets(h.ten); } hv[i] = h ; /* tim̀ kiếm Phaṃ Tèo */ thay = 0 ; i = 0 ; /* thay = 0 : không thấy, thấy = 1 : tim̀ thấy */ while ((!thay)&&(i <n)) if ( strcmp(hv[i].Ten , " Phaṃ Tèo ") = = 0 )
  77. { thay = 1 ; printf ("%s%d%f ", hv[i].ten , hv[i].tuổi, hv[i].điểm ); } else i++ ; if (!thay ) puts ("\n không tim̀ thấy Phaṃ Tèo !"); getch( ); } BàI TâP̣ : Viết chương triǹ h nhâp̣ danh sách gồm na hoc̣ viên gồm các thông tin như : Ho ̣, tên, điểm pascal , điểm c, sau đó tiń h điểm trung biǹ h (điemTB) = (diemC*2 + diempascal)/3 . - Và xét kết quả đâụ hay rớt theo qui ước sau : + nếu điểm trung biǹ h >= 5 thi ̀ kết quả đâụ . + Nếu điểm trung biǹ h <5 thi ̀ kết qua rớt. + Nếu điểm trung biǹ h = 4 mà phái = "Nữ" thi ̀ kết quả là đâụ . 1/ in danh sách vừa nhâp̣ gồm ho ̣tên, phái , điểm c, điểm pascal, điểm TB , kết quả . 2/ Sắp xếp giảm dần theo điểm trung biǹ h và in ra. 3/ Nhâp̣ vào tên cần tim̀ và tim̀ trong danh sách hoc̣ viên nếu không tim̀ thấy thi ̀ in ra hoc̣ viên có tên không tim̀ thấy. Nếu có nhiều hoc̣ viên có cùng tên cần tim̀ thi ̀ hãy in ra người cuối cùng đươc̣ tim̀
  78. thấy. 4/ Giống câu 3 nhưng in ra 2 người tim̀ thấy đầu tiên ( nếu có nhiều người ). 5/ Giống câu 3 nhưng in ra người đầu tiên và người cuối cùng ( nếu có nhiều ngưòi). Nên viết theo từng hàm. 7.3/ Con trỏ trỏ đến cấu trúc và điạ chi ̉ cấu trúc : a/ Con trỏ và điạ chi ̉ : - Vi ́ du ̣: typedef struct { char Ten[30] ; int tuoi ; float diem ; } kieu HV ; kieu HV *p , HV , lop[50] ; HS [50] ( trong đó : HV là biến cấu trúc, *p : con trỏ cấu trúc dùng để lưu trữ điạ chi ̉ cấu trúc và mãng cấu trúc ) ( *). main ( ) /* ta có thể gán */ p = &HV ; /* Đúng do (*)*/ p = &lop[i]/*đúng do (*) */ p = lơp ; /* đúng : p = điạ chi ̉ Lop[0] , p = &lop[0] ) do Lop = &Lop[0]) b/ truy câp̣ thông qua con trỏ : - Cách 1 : tên con trỏ -ă tên thành phần.
  79. - Cách 2 : (*tên con trỏ).tên thành phần. - Vi ́ du ̣: p = &HV ; p = &Lop[2] ' => HV.Ten i ̃ p ă tên; Lop[2].tuổi i ̃ (p*).tuoi i ̃ p -ă tuổi ; *p = HV ; *P = Lop[2] - Giả sử cần nhâp̣ số liêụ ch vùng trên thi ̀ 3 cách viết sau là tương đương : + (1) : gets(HV.ten) + (2) gets ( pă ten) i ̃ gets( (*p).ten). + (3) scanf("%d",&HV.tuoi) ; i ̃ scanf("%d", p -ă tuổi ); scanf ("%d", (*p).tuoi); - Giả sử cần nhâp̣ dữ liêụ cho mãng cấu trúc thì các cách viết sau đây tương đương : + Vi ́ du ̣: p = lop ; for ( i = 0 ; i < n ; i++) { gets (lop[i].tên); tương đương với : . gets((*(lop* i ) ).ten); .gets(*(p + i ).ten); .gets ( p[i].ten); .gets (p ă ten); p++ ; .gets (*p).ten) ; p++; - Vi ́ du ̣ : làm lai ̣ bài tâp̣ mâũ nhưng sử duṇ g biến
  80. con trỏ : #include #define n 10 typedef struct { char ten[30] ; int tuoi ; float diem ; } kieu HV ; main ( ) { kieu HV hv [n], *p , h; int i ; int thay ; float tam ; int tuổi ; p = hv; for ( i = 0 ; i < n ; i++) { printf (" nhâp̣ hoc̣ viên thứ %d ", i ); printf("Ho ̣và tên"); gets ( p ă ten); printf("tuổi : ") ; scanf ("%d", &tuổi); p ă tuoi = tuoi; printf ("diem : ") ; scanf ("%f%*c ", &tam ); p ă diem = tam; p++ ; printf ("%c", getchar(); } /* nhâp̣ theo cách 2 qua biến h xong gán *p = h */ /* tim̀ Phaṃ Tèo */ thấy = 0 ; i = 0 ; p = hv ; /* để di chuyển con trỏ về đầu danh sách */
  81. for ( i = 0 ; i thừa vùng nhớ. Để cấp phát vừa đủ si ̃ số hoc̣ viên như ta muốn => ta dùng phương pháp cấp phát bô ̣ nhớ đôṇ g hàm malloc hoăc̣ calloc(.) - Vi ́ du ̣ : Nhâ ̣ danh sách n hoc̣ viên gồm ho ̣ tên, điểm và sắp xếp giảm dần theo điểm. #include #include #include
  82. #include typedef struct { char ten[30] ; int diem ; char kq[5] ; } kieu HV; kieu HV *lop , *p , tam ; /* Hàm nhâp̣ dan sách */ void nhapDS ( int n , kieu HV lop[ ]) { int i , diem ; p = lop ; for ( i = 0 ; i < n ; i++) { printf("nhâp̣ Ho ̣ tên người thứ %d : " , i +1 ) ; gets ( p ăten); printf ( " điểm = " ) ; scanf ( "%d" , &diem ) ; p ă diem = diem ; printf ("%c", getchar()); /* khử stdin */ p++ ; } /* Hàm sắp xếp*/ void sapxep ( int n, kieu HV lop[ ]) { int i , j ; kieu HV tam ; for ( i = 0 ; i < n-1 ; i++) for ( j=i + 1 ; j< n ; j++) if ( lop[i].diem < lop[j].diem ) { tam = lop[i] ; lop[j] = lop [j] ; lop [j] = tam ; }
  83. /* hàm in danh sách */ void inds( intn, kieu HV lop[ ] ) { int i ; for ( i = 0 ; i < n ; i++ ) { printf ("%20s%5d ", lop[i].ten,lop[i].diem ); printf ("\n" ; /* xuống hàng */ /* chương triǹ h chiń h */ void main ( ) { int i , j, n , t, diem ; printf ("\n Nhâp̣ si ̃ số : ") ; scanf ( "%d", &n); lop = (kieu HV*)malloc ( n * size of ( kieu HV) ) ; printf ("%c", getchar ()); nhapds (n, lop ) ; sapxep ( n, lop ) ; inds ( in lop ); getch ( ); } KIểU FILE ( TâP̣ TIN/ TêP̣ TIN ) - Trong ngôn ngữ C , môṭ tâp̣ tin là môṭ khái niêṃ logic, đươc̣ áp duṇ g không những đối với các tâp̣ tin trên điã mà cả với các terminal ( bàn phiḿ , màn hiǹ h, máy in ). - File có 2 loai ̣ : + Text file ( file văn bản ). + Binary ( nhi p̣ hân : dbf, doc, bitmap, ). - File văn bản chi ̉ khác binary khi xử lý ký tư ̣
  84. chuyển dòng (LF) ( mã 10 ) đươc̣ chuyển thành 2 ký tư ̣CR (mã 13) và LF ( mã 10) và khi đoc̣ 2 ký tư ̣ liên tiếp CR và LF trên file cho ta môṭ ký tư ̣ LF. - Các thao tác trên file thưc̣ hiêṇ thông qua con trỏ kiểu FILE. Mỗi biến FILE có 1 con trỏ lúc đầu sẽ trỏ vào phần tử đầu tiên của file. Sau mỗi thao tác đoc̣ hay ghi dữ liêụ con trỏ tư ̣ đôṇ g dời xuống mâũ tin kế tiếp. Làm viêc̣ trên kiểu File thường có 3 công đoaṇ : mở file, nhâp̣ xuất thông trên file và đóng file. * Môṭ số hàm thông duṇ g thao tác trên file ( tâp̣ tin/têp̣ tin ) : + Mở file : FILE *fopen ( char *filename, char *mode); . Nếu có lỗi fp sẽ trỏ đến NULL. + Các mode chế đô ̣mở file : " r" " rt " / " rb " : mở file để đoc̣ theo kiểu văn bản / nhi p̣ hân - file phải tồn tai ̣ trước nếu không sẽ có lỗi. "w" "wt" / " wb " : mở ( taọ ) file mới để ghi theo kiểu văn bản/nhi ̣phân - nếu file đã có nó sẽ bi ̣ xóa(ghi đè )( luôn luôn taọ mới ). "a" "at"/ "ab" : mở file để ghi bổ sung (append)
  85. thêm theo kiểu văn bản hoăc̣ nhi p̣ hân( chưa có thì taọ mới ). + Đóng file : int fclose ( file + biến file ) ; Vi ́ du ̣: Void main ( ) { FILE *fp ; fp = fopen ("c:\\THUCTAP\\Data.txt", "wt" ); if (fp = NULL ) printf ( " không mở đươc̣ file c/Thuctap\data.txt"); else { } fclose (fp) ; /* đóng file */ } + Làm đóng tất cả các tâp̣ đang mở : int fclose all(void) ; nếu thành công trả về số nguyên bằng tổng số các file đóng đươc̣ , ngươc̣ lai ̣ trả về EOF. + Hàm xóa tâp̣ : remove (const + char*ten tâp̣ ) ; nếu thành công cho giá tri 0̣ , ngươc̣ lai ̣ EOF. + Hàm kiểm tra cuối tâp̣ : int feof(FILE*fp) : !=0 : nếu cuối tâp̣ = 0 : chưa cuối tâp̣ . + Hàm int putc ( int ch, FILE*fp); Hàm int fputc( int ch, FILE*fp); Công duṇ g của hai hàm này :ghi môṭ ký tư ̣lên tâp̣ fp theo khuôn daṇ g đươc̣ xác điṇh trong chuỗi điều khiển dk. Chuỗi dk và danh sách đối tương tư ̣
  86. hàm printf( ). + Hàm int fscanf ( FILE *fp, const char *dk, ); Công duṇ g : đoc̣ dữ liêụ từ tâp̣ tin fp theo khuôn daṇ g ( đăc̣ tả) làm viêc̣ giống scanf( ). *Vi ́ du ̣: giả sử có file c/data.txt lưu 10 số nguyên 1 5 7 9 8 0 4 3 15 20 . Hãy đoc̣ các số nguyên thêm vào môṭ mãng sau đó sắp xếp tăng dần rồi ghi vào file datasx.txt Giải : #include #include #include #define n 10 void main ( ) { FILE *fp ; int i, j, t, a[n] clrscr ( ) ; fp = fopen (" c :\\data.txt ", "rt" ); /* mở file để đoc̣ vào mãng */ if (fp = NULL) { printf ("không mở đươc̣ file "); exit (1); } /* Sắp xếp mãng */ for ( i=0 ; i<n-1 ; i++)
  87. for (j=i+1; j<n ; j++) if (a[i]<a[j] ) { t = a[i] ; a[i]=a[j] ; a[j] = t ; } fclose (fp); /* mở file datasx.txt để ghi sau khi sắp xếp */ fp = fopen ("c:\\datasx.txt ", "wt"); for ( i=0 ; i<n;i++) printf (fp,"%2d", a[i] ); fclose (fp); /* đoc̣ dữ liêụ từ file cách 2 ( tổng quát hơn ) không phu ̣thuôc̣ vào n */ i = 0 ; while (1) { fscanf (fp,"%d",&a[i] ; i++; if (foef(fp) ) break ; } - Hàm int fputs ( const char *s, file *fp ); Công duṇ g : ghi chuỗi s lên tâp̣ tin fp ( dấu "\0" ghi lên tâp̣ ) nếu có lỗi hàm cho eof. - Hàm char fgets ( char *s, int n , FILE *fp); Công duṇ g : đoc̣ 1 chuỗi ký tư ̣ từ tâp̣ tin fp chứa vào vùng nhớ s. Viêc̣ đoc̣ kết thúc khi : hoăc̣ đã đoc̣ n-1 ký tư ̣ hoăc̣ găp̣ dấu xuống DÒ NG(
  88. CẮ PMÃ 13 10). KHI ĐÓ MÃ 10 ĐƯƠC̣ ĐƯA VÀ O CHUỖ I KẾT QUẢ . CáC HàM ĐoC̣ GHI FILE KIểU CấU TRú C - Hàm int fwrite (void *p, int size , int n , FILE*fp); Đối : p : là con trỏ trỏ tới vùng nhớ chứa dữ liêụ cần ghi. size : là kić h thước của mâũ tin theo byte. n số mâũ tin cần ghi. fp là con trỏ tâp̣ . - Vi ́ du ̣: fwrite(&tam) size of(tam),1,fv); /* tam là 1 mâũ tin(record) nào đó*/ Công duṇ g : ghi môṭ mâũ tin (record) kić h thước sizebyte ( size of (tam)) từ vùng nhớ p(&tam) lên tâp̣ fp. Hàm sẽ trả về môṭ giá tri =̣ số mâũ tin thưc̣ sư ̣ghi đươc̣ . + Hàm int fread (void*p), int size , int n, FILE *fp); Đối : p : là con trỏ trỏ tới vùng nhớ chứa dữ liêụ đoc̣ đươc̣ . size là kić h thước của mâũ tin theo byte n : là số mâũ tin cần đoc̣ , fp là con trỏ tâp̣ tin. Vi ́ du ̣: fread (&tam, size of(KIEUHS) , 1, 4 )>0) Công duṇ g : đoc̣ n(1) mâũ tin kić h thước sizebyte
  89. (size of(tam)) từ tâp̣ tin fp chứa vào vùng nhớ p(&tam). Hàm trả về môṭ giá tri ̣bằng số mâũ tin thưc̣ sư ̣đoc̣ đươc̣ . * Vi ́ du ̣áp duṇ g : Nhâp̣ vào danh sách lớp gồm n hoc̣ viên ("nhâp̣ vào). Thông tin về mỗi hoc̣ viên gồm Ho ̣tên, phái , điểm, kết quả. Xét kết quả theo điều kiêṇ sau : nếu Điểm>= 5 ( đâụ ), điểm #include #include #include typedef struct { char ten[20] ; char phai[4] ; int diem ; char kq[4] ; } KieuHV; KieuHV *lop ,*p, tam; /* Hàm nhâp̣ danh sách n hoc̣ viên */ void nhapds ( int n, KieuHV lop[ ] ) { int i , diem ; p = lop ; for ( i=0; i<n ; i++ ) { printf (" nhâp̣ ho ̣và tên người thứ %d : " , i + 1) ;
  90. gets ( pă ten); printf ("phái (nam/nữ ) : ") ; gets (pă phai ); printf ("nhâp̣ điểm = ") ; scanf ("%d%c*c", &diem); pă diem=diem; if (diem>5)strcpy (p > kq,"Đâụ "); else strcpy (pă kq, "rớt " ) ; p++; } /* Hàm sắp xếp */ void sapxep ( int n , KieuHV lop[ ] ) { int i , j ; for ( i=0 ; i<n-1; i++) for ( j=i+1 ; j<0; j++) if (lop[i].diem< lop[j]diem ) { tam = lop[i] ; lop[i] = lop[j] ; lop [i] = tam ;} } /* Hàm in danh sách */ void inds ( int n, KieuHS lop[ ] ) { int i ; for ( i=0 ; i<n ; i++) printf ("\n %20s %5s%5d%5s, lop[i].ten, lop[i].phai, lop[i].diem, LOP[I].KQ ); /* CHƯƠNG TRiǸ H CHíNH */ void main ( ) { int i , j, n, t, diem ; FILE *fp, *fr ;
  91. printf ("\n nhâp̣ si ̃ số : ") ; scanf("%d%*c",&n); lop = (KieuHV*) malloc (n*size of (KieuHV)); nhapds(n, lop) ; sapxep ( n, lop ); inds( n, lop); getch( ); fp = fopen ( "c :\\lop.txt ", "wb"); for ( i = 0; i 0) { printf ("\n %s %s%d%s", tam.ten, tam.phai, tam.diem, tam.kq); if (tam.diem = = 4 &&strcmp(tam.phai,"nữ")= =0 ) strcmp(&tam.kq, "đâụ "); fwrite(&tam,size of(tam),1, fr); } fclose (fp); fclose(fr); printf ("\n in file ketqua.txt sau khi xét lai ̣ kết qủa ");
  92. fp = fopen ("c:\\ketqua.txt", "rb"); while (fread(&tam, size of (KieuHV) , 1, fp) > 0) printf("\n %s%s%d%s",tam.ten,tam.phai, tam.diem,tam.kq); fclose (fp); getch( ); &NBSP; } CáC HàM XUấT NHâP̣ NGâŨ NHI㎠Và DI CHUYểN CON TRỏ CHi ̉ Vi ̣ (File position locator ) - Khi mở têp̣ tin để đoc̣ hay ghi, con trỏ chi ̉ vi ḷuôn luôn ở đầu tâp̣ tin (byte 0) nếu mở mode "a" (append) => con trỏ chi ̉ vi ợ ̉ cuối tâp̣ tin. + Hàm void rewind (FILE*fp) : chuyển con trỏ chỉ vi c̣ ủa tâp̣ fp về đầu tâp̣ tin. + Hàm int fseek (FILE*fp, long số byte, int xp) Đối : fp : là con trỏ tâp̣ tin; số byte : là số byte cần di chuyển. xp " cho biết vi ̣tri ́ xuất phát mà viêc̣ dic̣h chuyển đươc̣ bắt đầu từ đó. xp = SEEK - SET hay 0 xuất phát từ đầu tâp̣ . xp = SEEK - CUR hay 1 : xuất phát từ vi ṭ ri ́ hiêṇ tai ̣ của con trỏ. xp= SEEK - END HAY 2 : xuất phát từ vi ṭ ri ́ cuối
  93. tâp̣ của con trỏ. + Công duṇ g : hàm di chuyển con trỏ chi ̉ vi ̣của tâp̣ fp từ vi ̣tri ́ xác điṇh bởi xp qua môṭ số byte bằng giá tri ṭ uyêṭ đối của số byte. Nếu số byte > 0 : chuyển về hướng cuối tâp̣ ngươc̣ lai ̣ chuyển về hướng đầu tâp̣ . Nếu thành công trả về tri ̣0. Nếu có lỗi trả khác 0. + Chú ý : không nên dùng fseep trên kiểu văn bản, vi ̀ sư ̣ chuyển đổi ký tư(̣ mã 10) sẽ làm cho viêc̣ điṇh vi ṭ hiếu chiń h xác. + Hàm long ftell(FILE*fp) ; : cho biết vi ṭ ri ́ hiêṇ taị của con trỏ chi ̉ vi ̣(byte thứ mấy trên tâp̣ fp) nếu không thành công trả về tri -̣ 1L. + Vi ́ du ̣1: giả sử tâp̣ fp có 3 ký tư ̣. fseek (fp,0,SEEK-END) => ftell(fp) = 3 fseek(fp,0,2) => ftell(fp) = 3 fseek (fp,-2, SEEK-END) => ftell(fp) = 1 fseek(fp,0,SEEK -SET) => ftell(fp) = 0 fseek(fp,0, 0) =>ftell(fp) = 0 + Vi ́ du ̣2 : giả sử ta có tâp̣ tin c:\lop.txt chứa danh sách các hoc̣ viên. Hãy đoc̣ danh sách và sắp xếp giảm dần theo điểm sau đó ghi lai ̣ file c:\lop.txt ( nối điểm) #include
  94. #include #include #define N 100 typedef struct { char ten[20] ; int tuoi; float diem ; } KieuHV ; void main( ) { KieuHV hv[N] ; t; FILE*fp ; int i, , n ; fp = fopen ("c:\\lop.txt ", "rat"); if (fp = =NULL) { printf ("không mở đươc̣ file "); exit(1); } n = 0 ; i = 0 ; while (!feof (fp)) { fread (&hv[i], size of (KieuHV), 1,fp); i++; n++ ; /* sắp xếp giảm dần theo điểm */ for (i=0, i <n-1, i++) for (j=i+1; j<n, j++) if (hv[i].diem <hv[j].diem) { t =hv[i] ; hv[i] = hv[j] ; hv[j] = t } /* ghi lên điã */ fseek (fp, 0, SEEK-END); for ( i=0; i<n ; i++) fwrite(&hv[i], size of (KieuHV), 1, fp);
  95. } CHƯƠNG 8 : TRUYỀ N SỐ LIÊỤ CHO HÀ M 1/ Truyền đối số cho hàm main( ) : - Vi ́ du ̣ : ta muốn viết môṭ chương triǹ h có tên là Hello.că hello.exe khi chaỵ trên MS-DOS ta nhâp̣ các đối số vào chương triǹ h. Vi ́ du ̣: c:> Tom and Jerry ( enter) máy sẽ in ra câu : Chào Tom and Jerry. - Viết chương triǹ h trên như sau : Void main ( int argc, char*argv[ ]) { } Trong đó : + argc : cho biết tổng số đối số truyền vào tiń h cả tên chương triǹ h đối với vi ́ du ̣trên argc = 4. Mỗi đối số truyền vào đươc̣ xem như là xâu ký tư.̣ + mãng argv [ ] sẽ là con trỏ , trỏ lần lươṭ đến các đối số. argv[0] >"Hello" argv[1] > "Tom" argv[2] > "and" argv[3] > "Jerry"
  96. void main ( int argc, char *argv[ ]) { int i ; printf("Chào !"); for ( i=1 ; i #include #include #define N 100 typedef struct { char ten[20] ; int tuoi ; float diem ; } kieu HV /* khai báo hàm nhâp̣ dữ liêụ */ void nhap ( int n , Kieu HV HV [ ] ) {int i ; float t; for ( i = 0; i< n ; i++) { printf ( " Nhâp̣ hv thử %d ", i++); scanf( ) }
  97. /* chương triǹ h chiń h */ main ( ) { Kieu HV hv[n]; nhap ( n, hv); } * Hàm có thể trả về giá tri ̣cấu trúc hoăc̣ con trỏ cấu trúc : + Vi ́ du ̣ : Hàm Kieu HV *ptim ( char*ten, KieuHV HV[ ] , int n) : có tác duṇ g tim̀ trong danh sách n hoc̣ viên trong mãng HV[ ] người có tên và hàm trả về con trỏ, trỏ tới người tim̀ đươc̣ hoăc̣ trả về NULL nếu không tim̀ thấy . + Hàm Kieu HV tim( char*ten, KieuHV HV[ ], int n); : cũng với muc̣ đić h như hàm trên nhưng trả về giá tri c̣ ủa môṭ cấu trúc. + Vi ́ du ̣: #include"stdio.h" #include"conio.h" #include"string.h" typedef struct { char ten[20] ; int tuổi ; float điểm } Kieu HV ; Kieu HV *ptim ( char*ten, KieuHV HV[ ] , int n ); Kieu HV tim ( char *ten, Kieu HV hv[ ] , int n );
  98. main( ) { Kieu HV *p, ds[100],h ; int i, h, n ; char ten[20] ; float diem; clrscr ( ); printf("\n Số người n = " ) ; scanf ("%d *c ",&n); for ( i=0 ; i<n ; ++i) { printf("\ho ̣tên "); gets(h.Tên) ; /* tư ̣viết lấy*/ ds[i] = h ; } /* tim̀ kiếm 1 theo ten dùng hàm ptim*/ while (1) { printf ("\n Ho ̣tên người cần tim̀ "); gets (tên); if ((p =ptim ( ten, ds, n)) = = NULL) printf("\n không tim̀ thấy "); else indanhsach(*p); } /* tim̀ kiếm theo tên dùng hàm tim*/ while(1) { printf("\n Ho ̣tên cần tim̀ "); gets(ten); if ( tim ( ten, ds, n).ten[0] = = 0 ) printf("\n Không tim̀ thấy"); else indanhsach ( tim ( ten, ds, n)); }
  99. Kieu HV *ptim ( char*ten, Kieu HV hv[ ] , int n) { int i ; for ( i= 0 ; i< n ; ++i) if ( strcmp ( ten, hv[i].ten= = )return (&hv[ i]); return (NULL); } Kieu HV tim (char*ten, Kieu HV hv[ ] , int n) { int i ; HV tam ; tam.ten[0]=0; for ( i=0 ; i<n ; ++i) if(strcmp(ten,hv[i].ten = = )) return (hv[i]); return ( tam); } void indanhsach (Kieu HV p) { printf("\n Ho ̣ tên % tuổi % điểm %f", p.ten, p.tuổi, p.diêm); } CHƯƠNG 9 : DANH SÁ CH LIÊN KẾ T ( MÓ C NỐ I) - Danh sách liên kết : Nếu sử duṇ g mãng để quản lý danh sách sẽ rất tốn kèm và cứng nhắc trong thao tác ă khắc phuc̣ = danh sách liên kết. - Danh sách liên kết gồm các phần tử . Mỗi phần tử có 2 vùng chiń h : vùng dữ liêụ và vùng liên kết.
  100. Vùng liên kết là môṭ hay nhiều con trỏ, trỏ đến các phần tử trước hoăc̣ sau nó tùy thuôc̣ vào yêu cầu của công viêc̣ . - Khai báo danh sách liên kết : Typedef struct Kieu du lieu { ; Kiểu dữ liêụ ; } Kiểu dữ liêụ ; - Dùng typedef struct kieu du lieu điṇh nghiã kiểu dữ liêụ mới. Trong kiểu dữ liêụ này có 2 phần, phần đầu tiên là phần khai báo các trường, phần thứ 2 là các con trỏ, trỏ đến chiń h kiểu dữ liêụ đó, dòng cuối cùng là cần thiết để các con trỏ đươc̣ phép khai báo chiń h là kiểu dữ liêụ mà các con trỏ đó là thành phần. - Vi ́ du ̣: typedef struct sinhvien { char hoten[30] ; int diem ; struct sinhvien *tiep ; } sinhvien ; sinhvien *head ; / con trỏ đăc̣ biêṭ luôn trỏ tới đầu danh sách*/ - Mỗi môṭ phần tử có môṭ con trỏ, trỏ đến phần tử tiếp theo. Riêng phần tử cuối cùng con trỏ sẽ trỏ
  101. đến môṭ kiểu đăc̣ biêṭ : Kiểu NULL( nghiã là con trỏ đó không trỏ đến môṭ phần tử nào cả). Ban đầu con trỏ danh sách (head) đươc̣ gán bằng NULL. - Để cấp phát bô ̣nhớ, ta cần kiểm tra xem có đủ không ( tránh rối loaṇ chương triǹ h) - Vi ́ du ̣: #define size of (sinhvien) sinhvien *sv sv=NULL ; if ((sv = (sinhvien*)malloc (size sv) = = NULL) { printf (" không đủ bô ̣nhớ RAM \n"); getch ( ); return ; } - Hàm size of ( kiểu phần tử ) cho kić h thước của kiểu phần tử bằng byte. sv là con trỏ phu ̣cần thiết cho các thao tác trong chương triǹ h. size sv có kić h thước bằng vùng nhớ môṭ phần tử ( nhờ sử duṇ g hàm size of( )). Cần gán sv = NULL đề phòng sinhvien đang trỏ vào môṭ phần tử của danh sách. Khi thêm vào, chương triǹ h sẽ tư ̣ đôṇ g tim̀ vi ̣tri ́ thić h hơp̣ của phần tử mới. Do trong ngôn ngữ C không điṇh nghiã kiểu
  102. string như trong PASCAL, nên càn dùng hàm so sánh strcmp(st1,st2). Hàm này cho kết quả kiểu int sau khi so sánh st1 và st2 như sau : 0 nếu st1 >st2. - Các trường hơp̣ xảy ra khi thêm môṭ phần tử vào môṭ danh sách : + Nếu phần tử mới ở đầu danh sách , cần sử a laị con trỏ head. + Nếu đã có phần tử đó, phải lưạ choṇ liêụ có ghi đè lên không? + Các trường hơp̣ khác cần sử a lai ̣ con trỏ như sau : Giả sử cần chèn phần tử mới vào giữa phần tử 1 và 2 ta có : - Vi ́ du ̣ : Chương triǹ h qủan lý sinh viên gồm : thêm, bớt, duyêṭ danh sách, tim̀ kiếm phần tử / Chương triǹ h qủan lý sinh viên / #include #include #include
  103. #include #include void taomenu( ) void themsv ( ); void timkiem ( ); void loaibo( ); void danhsach( ); void vitrihv (char st[ ], int d ); /* tim̀ vi ṭ ri ́ hơp̣ lý */ void lietke ( ); #define sizesv size of (sinhvien) typedef(truct sinhvien) { char hoten[30] ; int diem ; struct sinhvien *tiep ; } sinhvien ; sinhvien *head; sinhvien *sv ; void main ( ) { clrscr ( ); gotoxy(1,12); printf (" chương triǹ h quản lý danh sách sinh viên (DSLK)\n"); getch ( ) ; taomenu ( );
  104. } /* kết thúc hàm main ( ) */ void taomenu ( ) { char ch ; do { clrscr( ); printf(" thêm sinh viên tim̀ kiếm loai ̣ bỏ liêṭ kê Quit \n"); ch = toupper (getch()); switch (ch) { case "I' :themsv() ;break ; case ' I ' : timkiem( ) ; break ; case ' L; : loaibo( ) ;break ; case ' D' : lietke( ) ; break ; case ' Q ' : exit (1) ; break ; default : break ; } } while ( ch!= 'Q'); } void themsv ( ) { char tensv [30] ; int diem ; clrscr ( ); printf(" thêm sinh viên vào danh sách \n"); gotoxy(1,10) ; printf(" ho ̣ và tên : "); gets(
  105. tensv); printf("điểm :"); scanf("%d", &diem); vitrihv ( tensv, diem); } void vitrihv( char st [ ] ) int d ) { sinhvien *find = NULL , *next = NULL; int kq ; char ch ; sv = NULL ; if ((sv = ( sinhvien*) malloc ( sizesv )) = = NULL) { printf(" không đủ bô ̣nhớ \n") ; getch( ) ; return } strcpy ( svă hoten, st); svă diem = d ; /* nếu danh sách ban đầu là rỗng */ if ( head = = NULL) { head = sv ; headă tiep = NULL ; } else { /* tim̀ vi ṭ ri ́ mới của phần tử trong danh sách */ find = head ; next = find ; while ((find!=NULL) && ((kq=strcmp(findă hoten, sv ă hoten))< 0) { next = find ; find = findătiep ;} if ( kq = = 0)
  106. { printf("sinh viên đã có trong danh sách . Ghi đè (Y/N) ? \n"); ch = getch( ); ch = toupper (ch); if (ch = 'N') { free(sv) ; return ; } else find > diem = d ; free (sv) ; return ; } /* nếu phần tử thêm vào đầu danh sách */ if (find = = head ) { sv ă tiep = head ; head = sv ; } else { sv ă tiep = find ; next ă tiep = sv ; } } } void timkiem( ) { char tensv[30] ; int kq ; clrscr ( ); printf(" tên sinh viên cần tim̀ :") ; gets(tensv); if((tensv !=" " ) && (head1 = NULL)) { sv = head ; while ((sv! = NULL) &&((kq =
  107. strcmp(svăhoten, tensv))< 0) sv = sv ă tiep ; if(kq = = 0); printf (" Ho ̣và tên %s điểm %d", svăhoten, svă diem); else printf (" không có sinh viên %s \n", tensv); } getch( ) ; } void loaibo( ) { char tensv [30] ; int kq ; sinhvien *next ; clrscr ( ) printf ( " tên sinh viên cần loai ̣ bỏ :"); scanf("%s", tensv ); iF((tensv!=NULL) && (head!= NULL)) { sv = head ; next = sv ; while ((kq = strcmp (svă hoten, tensv )) < 0) { next = sv ; sv = sv ă tiep ; } if ( kq = = 0) { if ( sv = = head ) { head = head ă tiep ; free (sv) ; return ; } next ă tiep = sv ă tiep ;
  108. free(sv); } else { printf (" không có tên %s \n", tensv ); } } } void lietke( ) { clrscr( ) sv = head ; while ( sv! = NULL) { printf(" Ho ̣và tên : %s \n" , svăhoten ); printf(" điểm : %d \n\n", svă diem); sv = svătiep ; } getch( ); } Bài tâp̣ : Hãy lâp̣ triǹ h quản lý sinh viên sử duṇ g cấu trúc danh sách. Mỗi phần tử cấu trúc như sau : ho ̣và tên, điểm. Yêu cầu : - In danh sách sinh viên có điểm >= 7. - Sắp xếp theo điểm . - Loai ̣ bỏ sinh viên nào đó ( nhâp̣ tên vào ).