Bài giảng Tin học đại cương (Bản đẹp)

Lưu ý:  Nếu có nhiều tham số trong danh sách tham số thì các tham số được phân cách với nhau bằng dấu phẩy  Cho dù hàm có một, nhiều hay không có tham số thì vẫn luôn luôn cần cặp dấu ngoặc đơn đứng sau tên hàm Trong chương trình, khi gặp một lời gọi hàm thì hàm bắt đầu thực hiện bằng cách chuyển các lệnh thi hành đến hàm được gọi. Quá trình diễn ra như sau: - Nếu hàm có tham số, trước tiên các tham số sẽ được gán giá trị thực tương ứng. - Chương trình sẽ thực hiện tiếp các câu lệnh trong thân hàm bắt đầu từ lệnh đầu tiên đến câu lệnh cuối cùng.

pdf166 trang | Chia sẻ: huongthu9 | Lượt xem: 440 | Lượt tải: 0download
Bạn đang xem trước 20 trang tài liệu Bài giảng Tin học đại cương (Bản đẹp), để xem tài liệu hoàn chỉnh bạn click vào nút DOWNLOAD ở trên
, chương trình sẽ chuyển sang thực hiện một vòng lặp mới và bỏ qua việc thực hiện các câu lệnh nằm sau lệnh continue trong thân vòng lặp. Ví dụ sau đây sẽ in ra màn hình các số tự nhiên lẻ và nhỏ hơn 100 #include #include void main() { int i; for(i = 1;i<100;i++) { if(i%2 == 0) continue; printf(“%5d”,i); if((i+1)%20 ==0) printf(“\n”); } getch(); } Kết quả thực hiện 1 3 5 7 9 11 13 15 17 19 21 23 25 27 29 31 33 35 37 39 41 43 45 47 49 51 53 55 57 59 61 63 65 67 69 71 73 75 77 79 Bài giảng tin học đại cương 137 81 83 85 87 89 91 93 95 97 99 III.3.7.2. break Khi gặp lệnh break, chương trình sẽ thoát khỏi vòng lặp (đối với trường hợp lệnh break nằm trong các cấu trúc lặp while, do{...}while, for) hoặc thoát khỏi cấu trúc switch (với trường hợp lệnh break nằm trong cấu trúc switch). Ví dụ sau sẽ thực hiện việc yêu cầu người dùng nhập vào một kí tự và màn hình thông báo về kí tự vừa nhập. Việc này được lặp đi lặp lại cho đến khi kí tự nhập vào là kí tự „t‟ hoặc „T‟ (viết tắt của từ thoát). #include #include void main() { char ch; clrscr(); do { printf(“\n Nhap vao mot ki tu: “); fflush(stdin); scanf(“%c”,&ch); printf(“\n Ki tu vua nhap vao la %c”,ch); if((ch ==‟T‟)||(ch ==‟t‟)) break; }while(1); printf(“\n An phim bat ki de ket thuc chuong trinh...”); getch(); } Kết quả thực hiện chương trình Nhap vao mot ki tu: a Ki tu vua nhap vao la a Nhap vao mot ki tu: 5 Ki tu vua nhap vao la 5 Nhap vao mot ki tu: t Ki tu vua nhap vao la t An phim bat ki de ket thuc chuong trinh... Bài giảng tin học đại cương 138 III.4. Mảng và xâu ký tự (5 tiết LT) III.4.1. Mảng III.4.1.1. Khái niệm mảng Khái niệm mảng Mảng là một tập hợp hữu hạn các phần tử có cùng kiểu dữ liệu được lưu trữ kế tiếp nhau trong bộ nhớ. Các phần tử trong mảng có cùng tên (và cũng là tên mảng) nhưng phân biệt với nhau ở chỉ số cho biết vị trí của chúng trong mảng. III.4.1.2. Khai báo và sử dụng mảng Cú pháp khai báo Trong C để khai báo một mảng ta sử dụng cú pháp khai báo sau: kiểu_dữ_liệu tên_mảng [kích_thước_mảng]; Trong đó kiểu_dữ_liệu là kiểu dữ liệu của các phần tử trong mảng. tên_mảng là tên của mảng. kích_thứớc_mảng cho biết số phần tử trong mảng. Ví dụ: int mang_nguyen[10]; // khai báo mảng 10 phần tử có kiểu dữ liệu int float mang_thuc[4]; // khai báo mảng 4 phần tử có kiểu dữ liệu float char mang_ki_tu[6]; // khai báo mảng 6 phần tử có kiểu dữ liệu char Trong ví dụ trên, mảng mang_nguyen được lưu trữ trên 20 ô nhớ (mỗi ô nhớ có kích thước 1 byte, 2 ô nhớ kích thước là 2 byte lưu trữ được một số nguyên kiểu int) liên tiếp nhau. Do C đánh số các phần tử của mảng bắt đầu từ 0 nên phần tử thứ i của mảng sẽ có chỉ số là i-1 và do vậy sẽ có tên là mang_nguyen[i-1]. Ví dụ: phần tử thứ nhất của mảng là mang_nguyen[0], phần tử thứ 2 là mang_nguyen[1], phần tử thứ 5 là mang_nguyen[4] mang_nguyen[0] ...mang_nguyen[1] mang_nguyen[9]... Kích thước của mảng bằng kích thước một phần tử nhân với số phần tử. Mảng một chiều và nhiều chiều Các mảng trong ví dụ trên là các mảng một chiều. Mảng là tập hợp các phần tử cùng kiểu dữ liệu, nếu mỗi phần tử của mảng cũng là một mảng khác thì khi đó ta có mảng nhiều chiều. Khái niệm mảng rất giống với khác niệm vector trong toán học. Ví dụ sau khai báo một mảng gồm 6 phần tử, trong đó mỗi phần tử lại là một mảng gồm 5 số nguyên kiểu int. Mảng này là mảng 2 chiều Bài giảng tin học đại cương 139 int a [6][5]; Còn khai báo int b [3][4][5]; thì lại khai báo một mảng gồm 3 phần tử, mỗi phần tử lại một mảng 2 chiều gồm 4 phần tử. Mỗi phần tử của mảng 2 chiều lại là một mảng (1 chiều) gồm 5 số nguyên kiểu int. Mảng b ở trên được gọi là một mảng 3 chiều. Sử dụng mảng Để truy nhập vào một phần tử của mảng thông qua tên của nó. Tên một phần tử của mảng được tạo thành từ tên mảng và theo sau là chỉ số của phần tử đó trong mảng được đặt trong cặp dấu ngoặc vuông tên_mảng[chỉ_số_của_phần_tử] Ví dụ với khai báo int mang_nguyen[3]; Thì mang_nguyen[0] sẽ là phần tử thứ nhất của mảng mang_nguyen[1] sẽ là phần tử thứ 2 của mảng mang_nguyen[2] sẽ là phần tử thứ 3 của mảng Với mảng nhiều chiều như int a[6][5]; thì a[0] là phần tử đầu tiên của một mảng, phần tử này bản thân nó lại là một mảng một chiều. Phần tử đầu tiên của mảng một chiều a[0] sẽ là a[0][0]. Phần tử tiếp theo của a[0] sẽ là a[0][1] Và dễ dàng tính được a[2][3] sẽ là phần tử thứ 4 của phần tử thứ 3 của a. Một cách tổng quát a[i][j] sẽ là phần tử thứ j+1 của a[i], mà phần tử a[i] lại là phần tử thứ i+1 của a. III.4.1.3. Các thao tác cơ bản trên mảng a. Nhập dữ liệu cho mảng Sau khi khai báo mảng ta phải nhập dữ liệu cho mảng. Nhập dữ liệu cho mảng là nhập dữ liệu cho từng phần tử của mảng. Mỗi một phần tử của mảng thực chất là một biến có kiểu dữ liệu là kiểu dữ liệu chung của mảng. Để nhập dữ liệu cho các phần tử của mảng ta có thể dùng hàm scanf() hoặc lệnh gán tương tự như biến thông thường. Ví dụ float a[10]; // khai bao mot mang so thuc co 10 phan tu int i; // Nhap tu ban phim mot so thuc va gan gia tri so thuc do // cho phan tu thu 2 cua mang, tuc la a[1] Bài giảng tin học đại cương 140 scanf(“%f”,&a[1]); // Gán giá trị cho phần tử a[2] a[2] = a[1] + 5; Nếu ta muốn gán giá trị cho các phần tử của mảng một cách hàng loạt, ta có thể dùng lệnh for. Ví dụ int b[10]; int i; // Nhap gia tri tu ban phim cho tat ca cac phan tu cua mang b for(i = 0; i < 10; i++) { printf(“\n Nhap gia tri cho b[%d]”, i); scanf(“%d”,&b[i]); } Trường hợp ta không biết trước mảng sẽ có bao nhiêu phần tử mà chỉ biết số phần tử tối đa có thể có của mảng. Còn số phần tử thực sự của mảng thì chỉ biết khi chạy chương trình. Khi đó cần khai báo mảng với số phần tử bằng số phần tử tối đa, ngoài ra cần một biến để lưu giữ số phần tử thực sự của mảng. int a[100]; // khai báo mảng, giả sử số phần tử tối đa của a là 100 int n; // biến lưu giữ số phần tử thực sự của mảng int i; printf(“\n Cho biet so phan tu cua mang: “); scanf(“%d”,&n); for(i = 0; i < n; i++) { printf("\n a[%d] = ", i); scanf("%d",&a[i]); } Mảng có thể được khởi tạo giá trị ngay khi khai báo, ví dụ int a[4] = {4, 9, 22, 16}; float b[3] = {40.5, 20.1, 100}; char c[5] = {„h‟, „e‟, „l‟, „l‟, „o‟}; Câu lệnh thứ nhất có tác dụng tương đương với 4 lệnh gán a[0] = 4; a[1] = 9; a[2] = 22; a[3] = 16; b. Xuất dữ liệu chứa trong mảng Để hiển thị giá trị của các phần tử trong mảng ta dùng hàm printf(). Ví dụ sau minh họa việc nhập giá trị cho các phần tử của mảng, sau đó hiển thị giá trị của các phần tử đó theo các cách khác nhau. #include #include void main() { Bài giảng tin học đại cương 141 int a[5]; int i, k; // Nhap gia tri cho cac phan tu cua mang a tu ban phim for(i = 0; i < 5; i++) { printf(“\n a[%d] = “, i); scanf(“%d”, &a[i]); } // Hien thi gia tri cua phan tu bat ki, gia su a[3] len man hinh printf(“\n a[3] = %d”, a[3]); // Hien thi gia tri cua tat ca cac phan tu, moi phan tu tren mot dong for(i = 0; i < 5; i++) printf(“\n%d”, a[i]); // Hien thi gia tri cua tat ca cac phan tu tren cung mot dong, cac gia tri cach nhau 2 vi tri printf(“\n”); // Xuong dong moi for(i = 0; i < 5; i++) printf(“%d “, a[i]); // Hien thi gia tri cua tat ca cac phan tu, trong do k phan tu tren mot dong. // Cac phan tu tren cung dong cach nhau 2 vi tri printf(“\n Cho biet gia tri cua k = “); scanf(“%d”,&k); for(i = 0; i < 5; i++) { printf(“%d “,a[i]); if((i+1)%k == 0) // da hien thi du k phan tu tren mot dong thi phai xuong dong printf(“\n”); } getch(); } Kết quả a[0] = 6 a[1] = 14 a[2] = 23 a[3] = 37 a[4] = 9 a[3] = 37 6 14 23 37 9 6 14 23 37 9 Cho biet gia tri cua k = 2 6 14 Bài giảng tin học đại cương 142 23 37 9 c. Tìm phần tử có giá trị lớn nhất, phần tử có giá trị nhỏ nhất Để tìm phần tử có giá trị lớn nhất trong mảng ban đầu ta giả sử phần tử đó là phần tử đầu tiên của mảng. Sau đó lần lượt so sánh với các phần tử còn lại trong mảng. Nếu gặp phần tử nhỏ hơn thì chuyển sang so sánh với phần tử tiếp theo. Nếu gặp phần tử lớn hơn thì ta sẽ coi phần tử này là phần tử lớn nhất rồi chuyển sang so sánh với phần tử tiếp theo. Sau khi so sánh với phần tử cuối cùng thì ta sẽ tìm được phần tử lớn nhất trong mảng. Đoạn chương trình sau minh họa giải thuật tìm phần tử lớn nhất int a[100]; int i, n; int max; printf("\n Cho biet so phan tu cua mang: "); scanf("%d",&n); // Nhap du lieu cho mang for(i = 0; i < n; i++) { printf("\n a[%d] = ",i); scanf("%d",&a[i]); } // Tim phan tu lon nhat max = a[0]; // Ban dau gia su phan tu lon nhat la a[0] // Lan luot so sanh voi cac phan tu con lai trong mang for(i = 1; i < n; i++) if(max < a[i]) // gap phan tu co gia tri lon hon max = a[i]; // coi phan tu nay la phan tu lon nhat printf("\n Phan tu lon nhat trong mang la: %d", max); Ta cũng làm tương tự với trường hợp tìm phần tử nhỏ nhất trong mảng. BUỔI 13. III.4.1.4. Tìm kiếm trên mảng Yêu cầu của thao tác tìm kiếm trên mảng: Cho một mảng dữ liệu và một dữ liệu bên ngoài mảng. Hãy tìm (các) phần tử của mảng có giá trị bằng giá trị của dữ liệu bên ngoài trên. Nếu có (các) phần tử như vậy thì hãy chỉ ra vị trí của chúng trong mảng. Nếu không tồn tại (các) phần tử như vậy thì đưa ra thông báo không tìm thấy. Cách làm là lần lượt duyệt qua từng phần tử của mảng, so sánh giá trị của phần tử đang được duyệt với giá trị của dữ liệu bên ngoài. Nếu phần tử đang xét bằng dữ liệu bên ngoài thì ta ghi nhận vị trí của phần tử đó. Sau đó chuyển qua duyệt phần tử kế tiếp trong mảng. Quá trình được lặp đi lặp lại cho đến khi duyệt xong phần tử cuối cùng của mảng. Để có thể trả lời cho cả tình huống không tồn tại phần tử như vậy trong mảng ta nên sử dụng một biến kiểm tra và khi gặp phần tử bằng giá trị dữ liệu bên ngoài thì ta bật biến đó lên, nếu biến đó không được bật lần nào Bài giảng tin học đại cương 143 thì ta trả lời là không có phần tử như vậy trong mảng. Phương pháp trên được gọi là phương pháp tìm kiếm tuần tự (sequential search). Dưới đây là cài đặt của thuật toán tìm kiếm tuần tự cho trường hợp mảng dữ liệu là mảng các số nguyên kiểu int. #include #include void main() { int m[100], chi_so[100]; int n; // n la số phần tử trong mảng int i, k, kiem_tra; clrscr(); // xóa màn hình để tiện theo dõi // Nhập giá trị dữ liệu cho mảng m // Trước tiên phải biết số phần tử của mảng printf(“ Cho biet so phan tu co trong mang: “); scanf(“%d”,&n); // Rồi lần lượt nhập giá trị cho các phần tử trong mảng for(i = 0;i<n;i++) { int temp; printf(“\n Cho biet gia tri cua m[%d] = “,i); scanf(“%d”,&temp); m[i] = temp; } // Yêu cầu người sử dụng nhập vào giá trị cho dữ liệu k printf(“\n Cho biết giá trị của dữ liêu k: “); scanf(“%d”,&k); // Bắt đầu quá trình tìm kiếm kiem_tra = 0; // Duyệt qua tất cả các phần tử for(i = 0;i<n;i++) if(m[i] = = k)//So sánh phần tử đang xét với dữ liệu k { // Ghi nhận chỉ số của phần tử đang xét chi_so[kiem_tra] = i; kiem_tra ++; //Tăng biến kiem_tra thêm 1 đơn vị } // Kết luận if(kiem_tra > 0) { printf(“\n Trong mang co %d phan tu co gia tri bang %d”,kiem_tra,k); printf(“\n Chi so cua cac phan tu la: “); for(i = 0;i < kiem_tra;i++) printf(“%3d”,chi_so[i]); } else Bài giảng tin học đại cương 144 printf(“\n Trong mang khong co phan tu nao co gia tri bang %d”,k); getch(); // Chờ người sử dụng ấn phím bất kì để kết thúc. } III.4.1.5. Sắp xếp mảng Yêu cầu của bài toán: cho một mảng dữ liệu m[n] với n là số phần tử trong mảng. Hãy sắp xếp các phần tử trong mảng theo một trật tự nào đó, giả sử là theo chiều tăng dần (với chiều giảm dần ta hoàn toàn có thể suy luận từ cách làm với chiều tăng dần). Sắp xếp kiểu lựa chọn (Selection sort): ý tưởng của phương pháp là như sau ta cần thực hiện n – 1 lượt sắp xếp, trong đó:  Ở lượt sắp xếp đầu tiên ta so sánh phần tử đầu tiên của mảng m[0] với tất cả các phần tử đứng sau nó trong mảng (tức là các phần tử m[1], m[2]m[n-1]). Nếu có giá trị m[i] nào đó (i = 1, 2,n–1) nhỏ hơn m[0] thì ta hoán đổi giá trị giữa m[0] và m[i] cho nhau. Rõ ràng sau lượt sắp xếp thứ nhất m[0] sẽ là phần tử có giá trị nhỏ nhất trong mảng.  Ở lượt sắp xếp thứ 2 ta so sánh phần tử thứ 2 của mảng m[1] với tất cả các phần tử đứng sau nó trong mảng (tức là các phần tử m[2], m[3]m[n-1]). Nếu có giá trị m[i] nào đó (i = 2, 3,n–1) nhỏ hơn m[1] thì ta hoán đổi giá trị giữa m[1] và m[i] cho nhau. Sau lượt sắp xếp thứ 2 thì m[1] sẽ là phần tử có giá trị nhỏ thứ 2 trong mảng.  Ở lượt sắp xếp thứ k ta so sánh phần tử thứ k của mảng là m[k-1] với tất cả các phần tử đứng sau nó trong mảng (tức là các phần tử m[k], m[k+1]m[n-1]). Nếu có giá trị m[i] nào đó (i = k, k+1,n–1) nhỏ hơn m[k] thì ta hoán đổi giá trị giữa m[k] và m[i] cho nhau. Sau lượt sắp xếp thứ k thì m[k-1] sẽ là phần tử có giá trị nhỏ thứ k trong mảng.  Ở lượt sắp xếp thứ n-1 ta so sánh phần tử thứ n-1 của mảng m[n-2] với tất cả các phần tử đứng sau nó trong mảng (tức là phần tử m[n-1]). Nếu m[n-1] nhỏ hơn m[n-2] thì ta hoán đổi giá trị giữa m[n-2] và m[n-1] cho nhau. Sau lượt sắp xếp thứ n-1 thì m[n-2] sẽ là phần tử có giá trị nhỏ thứ n-2 trong mảng. Và dĩ nhiên phần tử còn lại là m[n-1] sẽ là phần tử nhỏ thứ n trong mảng (tức là phần tử lớn nhất trong mảng). Kết thúc n-1 lượt sắp xếp ta có các phần tử trong mảng đã được sắp xếp theo thứ tự tăng dần. Cài đặt giải thuật #include #include void main() { int m[100]; int n; // n la số phần tử trong mảng int i, j, k; clrscr(); // xóa màn hình để tiện theo dõi // Nhập giá trị dữ liệu cho mảng m // Trước tiên phải biết số phần tử của mảng printf(“ Cho biet so phan tu co trong mang: “); Bài giảng tin học đại cương 145 scanf(“%d”,&n); // Rồi lần lượt nhập giá trị cho các phần tử trong mảng for(i = 0;i<n;i++) { int temp; printf(“\n Cho biet gia tri cua m[%d] = “,i); scanf(“%d”,&temp); m[i] = temp; } // Hiển thị ra màn hình mảng vừa nhập vào printf(“\n Mang truoc khi sap xep: “); for(i=0;i<n;i++) printf(“%3d”,m[i]); // Bắt đầu sắp xếp for(i = 0; i<n-1;i++) { // Ở lượt sắp xếp thứ i+1 for(j = i+1;j<n;j++) { // So sánh m[i] với các phần tử còn lại // và đổi chỗ khi tìm thấy phần tử < m[i]. if(m[j]<m[i]) { int temp; temp = m[j]; m[j] = m[i]; m[i] = temp; } } // Hiển thị mảng sau lượt sắp xếp thứ i+1 printf(“\n Mang o luot sap xep thu %d”,i+1); for(k = 0;k < n ;k++) printf(“%3d”,m[k]); } getch(); // Chờ người sử dụng ấn phím bất kì để kết thúc. } Kết quả thực hiện: Cho biet so phan tu co trong mang: 5 Cho biet gia tri cua m[0]: 34 Cho biet gia tri cua m[1]: 20 Cho biet gia tri cua m[2]: 17 Cho biet gia tri cua m[3]: 65 Cho biet gia tri cua m[4]: 21 Mang truoc khi sap xep: 34 20 17 65 21 Mang o luot sap xep thu 1: 17 34 20 65 21 Mang o luot sap xep thu 2: 17 20 34 65 21 Bài giảng tin học đại cương 146 Mang o luot sap xep thu 3: 17 20 21 65 34 Mang o luot sap xep thu 4: 17 20 21 34 65 Bài tập về Lập trình (3 tiết BT) BUỔI 14. III.4.2. Xâu ký tự (2 tiết LT) III.4.2.1. Khái niệm xâu ký tự Xâu kí tự (string) là một dãy các kí tự viết liên tiếp nhau. Xâu rỗng: là xâu không gồm kí tự nào cả. Độ dài xâu là số kí tự có trong xâu. Biểu diễn xâu kí tự: xâu kí tự được biểu diễn bởi dãy các kí tự đặt trong cặp dấu ngoặc kép. Các kí tự nằm trong cặp dấu ngoặc kép là nội dung của xâu. Ví dụ:  “String” là một xâu kí tự gồm 6 kí tự: „S‟, „t‟, „r‟, „i‟, „n‟, „g‟ được viết liên tiếp nhau.  “Tin hoc” là một xâu kí tự gồm 7 kí tự: „T‟, „i‟, „n‟, dấu cách („ „), „h‟, „o‟, và „c‟. Lưu trữ dữ liệu kiểu xâu kí tự: các kí tự của xâu được lưu trữ kế tiếp nhau và kết thúc bằng kí tự kết thúc xâu (kí tự '\0' hay kí tự NUL, có số thứ tự 0 trong bảng mã ASCII). Nhờ có kí tự NUL mà người ta xác định được độ dài của xâu kí tự bằng cách đếm các kí tự có trong xâu đến khi gặp kí tự NUL (kí tự NUL không được tính vào độ dài xâu). Ví dụ xâu kí tự “Tin hoc” sẽ được lưu trữ như sau „T‟ „i‟ „n‟ „ „ „h‟ „o‟ „c‟ „\0‟ Lưu ý:  Xâu kí tự khác mảng kí tự ở chỗ xâu kí tự có kí tự kết thúc xâu (kí tự NUL hay „\0‟) trong khi mảng kí tự không có kí tự kết thúc.  Phân biệt giữa một kí tự và xâu kí tự có một kí tự: ví dụ „A‟ là một kí tự, nó được lưu trữ trong 1 byte, còn “A” là xâu kí tự, nó được lưu trữ trong 2 bytes, trong đó byte đầu tiên lưu trữ kí tự „A‟, byte thứ 2 lưu trữ kí tự kết thúc xâu (NUL). Bài giảng tin học đại cương 147 III.4.2.2. Khai báo và sử dụng xâu a. Khai báo xâu kí tự Trong C, một xâu kí tự được khai báo với cú pháp như sau: char tên_xâu [số_kí_tự_tối_đa]; Trong đó số_kí_tự_tối_đa cho biết số lượng kí tự nhiều nhất có thể có trong xâu. Sau khi khai báo, biến xâu kí tự tên_xâu có thể được dùng để lưu trữ một xâu kí tự bất kì, miễn là độ dài xâu kí tự (số kí tự có trong xâu) đó không vượt quá giá trị số_kí_tự_tối_đa. Ví dụ char ho_va_ten[20]; Đây là khai báo của một biến xâu kí tự tên là ho_va_ten, biến này có thể có tối đa 20 kí tự. Lưu ý: Đôi khi ta vẫn có thể nhập một xâu có nhiều hơn 20 kí tự cho xâu ho_va_ten mà trình biên dịch C vẫn không báo lỗi, tuy nhiên cần tránh điều này vì khi chạy chương trình thì chương trình quản lí bộ nhớ của hệ điều hành sẽ bắt lỗi và buộc chương trình kết thúc. b. Truy nhập vào một phần tử của xâu Có thể truy nhập đến từng phần tử của xâu tương tự như truy nhập đến từng phần tử của mảng. Cú pháp sử dụng để truy nhập là tên_xâu[chỉ_số_của_kí_tự_cần_truy_nhập] Ví dụ ta đã có khai báo char que_quan[10], và giả sử xâu que_quan có nội dung là "Ha Noi". Khi đó ta có thể hình dung xâu kí tự que_quan như sau Phần tử thứ Chỉ số của phần tử Tên của phần tử Nội dung lƣu trữ 1 0 que_quan[0] „H‟ 2 1 que_quan[1] „a‟ 3 2 que_quan[2] „ ‟ 4 3 que_quan[3] „N‟ 5 4 que_quan[4] „o‟ 6 5 que_quan[5] „i‟ 7 6 que_quan[6] „\0‟ 8 7 que_quan[7] 9 8 que_quan[8] 10 9 que_quan[9] III.4.2.3. Các hàm xử lý ký tự Lưu ý: để sử dụng các hàm này ta khai báo tệp tiêu đề ctype.h. Hàm toupper() Bài giảng tin học đại cương 148 int toupper(int ch) Hàm toupper() dùng để chuyển một kí tự chữ cái thường (các kí tự 'a', 'b', , 'z') thành kí tự chữ cái hoa tương ứng ('A', 'B', , 'Z'). Hàm tolower() int tolower(int ch) Hàm tolower() dùng để chuyển một kí tự chữ cái hoa ('A', 'B', , 'Z') thành kí tự chữ cái thường tương ứng ('a', 'b', 'z'). Hàm isalpha() int isalpha(int ch) Hàm isalpha() dùng để kiểm tra một kí tự có phải là chữ cái hay không ('a', 'b', , 'z', 'A', 'B', , 'Z'). Hàm trả về giá trị khác không nếu đúng là chữ cái, trả về giá trị 0 nếu ngược lại. Hàm isdigit() int isdigit(int ch) Hàm isdigit() dùng để kiểm tra một kí tự có phải là chữ số hay không ('0', '1', '9'). Hàm trả về giá trị khác không nếu đúng, trả về giá trị 0 nếu ngược lại. Hàm islower() int islower(int ch) Hàm islower() dùng để kiểm tra một kí tự có phải là chữ cái thường hay không ('a', 'b', 'z'). Hàm trả về giá trị khác không nếu đúng, trả về giá trị 0 nếu ngược lại. Hàm isupper() int isupper(int ch) Hàm isupper() dùng để kiểm tra một kí tự có phải là chữ cái hoa hay không ('A', 'B', 'Z'). Hàm trả về giá trị khác không nếu đúng, trả về giá trị 0 nếu ngược lại. Hàm iscntrl() int iscntrl(int ch) Hàm iscntrl() dùng để kiểm tra một kí tự có phải là kí tự điều khiển hay không (là các kí tự không hiển thị được và có mã ASCII từ 0 đến 31). Hàm trả về giá trị khác không nếu đúng, trả về giá trị 0 nếu ngược lại. Hàm isspace() int isspace(int ch) Hàm isspace() dùng để kiểm tra một kí tự có phải là dấu cách (space, mã ASCII là 32), kí tự xuống dòng ('\n', mã ASCII là 10), kí tự về đầu dòng ('\r', mã ASCII là 13), dấu tab ngang ('\t', mã ASCII là 9) hay dấu tab dọc ('\v', mã ASCII là 11) hay không. Hàm trả về giá trị khác không nếu đúng, trả về giá trị 0 nếu ngược lại. Bài giảng tin học đại cương 149 III.4.2.4. Các hàm xử lý xâu a. Vào ra dữ liệu Vào ra dữ liệu trên xâu kí tự tức là nhập dữ liệu cho xâu và hiển thị dữ liệu chứa trong xâu. Để nhập dữ liệu cho xâu ta có thể sử dụng 2 hàm scanf() hoặc gets() scanf(“%s”,tên_xâu); gets(tên_xâu); Để hiển thị nội dung xâu ta có thể dùng 2 hàm printf() hoặc puts() printf(“%s”,tên_xâu); puts(tên_xâu); Các hàm scanf(), gets, printf(), puts() được khai báo trong tệp tiêu đề stdio.h. Ví dụ: Nhập vào một chuỗi và hiển thị trên màn hình chuỗi vừa nhập. #include #include #include int main() { char Ten[12]; printf("Nhap chuoi: ");gets(Ten); printf("Chuoi vua nhap: ");puts(Ten); getch(); return 0; } b. Một số hàm xử lí xâu kí tự khác Hàm strlen() size_t strlen(char* tên_xâu); Hàm trả về độ dài (số kí tự có trong xâu) của xâu kí tự tên_xâu. Hàm strcpy() char* strcpy(char* xâu_đích, char* xâu_nguồn) Hàm này sẽ sao chép nội dung xâu_nguồn và ghi lên xâu_đích. Hàm strcmp() int strcmp(char* xâu_thứ_nhất, char* xâu_thứ_hai); Hàm strcmp trả về - giá trị < 0 nếu xâu_thứ_nhất nhỏ hơn xâu_thứ_hai - giá trị 0 nếu xâu_thứ_nhất bằng xâu_thứ_hai - giá trị > 0 nếu xâu_thứ _nhất lớn hơn xâu_thứ_hai Bài giảng tin học đại cương 150 Hàm strcat() char* strcat(char* xâu_đích, char* xâu_nguồn); Hàm strcat sẽ ghép nối xâu_nguồn vào ngay sau xâu_đích. Kết quả trả về của hàm strcat() là xâu mới ghép nối từ 2 xâu xâu_nguồn và xâu_đích. Hàm strchr() char* strchr(char* str, int ch); Hàm strchr() dùng để tìm kiếm vị trí của kí tự ch trong xâu str. Nếu có kí tự ch trong str thì hàm strchr() trả về con trỏ trỏ tới kí tự ch đầu tiên trong str, ngược lại nó sẽ trả về con trỏ NULL. Hàm strstr() char* strstr(char* str1, char* str2); Hàm strstr() dùng để tìm kiếm vị trí của xâu con str2 trong xâu str1. Nếu str2 là xâu con của str1 thì hàm strstr() trả về con trỏ trỏ tới kí tự đầu tiên của xâu con str2 đầu tiên trong str1, ngược lại nó sẽ trả về con trỏ NULL. Hàm atoi() int atoi(char* str) Hàm atoi() dùng để chuyển một xâu kí tự là biểu diễn của một số nguyên thành số nguyên tương ứng. Nếu chuyển đổi thành công, hàm atoi() trả về giá trị số nguyên chuyển đổi được, ngược lại trả về giá trị 0. Hàm atol() long int atol(char* str) Hàm atol() dùng để chuyển một xâu kí tự là biểu diễn của một số nguyên dài (tương ứng với kiểu dữ liệu long int) thành số nguyên dài tương ứng. Nếu chuyển đổi thành công, hàm atol() trả về giá trị số nguyên chuyển đổi được, ngược lại trả về giá trị 0. Hàm atof() double atof(char* str) Hàm atof() dùng để chuyển một xâu kí tự là biểu diễn của một số thực (ở cả dạng số dấu phẩy tĩnh và động đều được) thành số thực tương ứng. Nếu chuyển đổi thành công, hàm atof() trả về giá trị số thực chuyển đổi được, ngược lại trả về giá trị 0. Các hàm strcpy(), strlen(),strcmp(), strcat(), strchr(), strstr() khai báo trong tệp tiêu đề string.h. Các hàm atoi(), atol(), atof() khai báo trong tệp tiêu đề stdlib.h. Ví dụ minh họa: #include #include #include // Phai co thu vien string.h thi moi // su dung duoc cac ham strcpy, strcmp... void main() Bài giảng tin học đại cương 151 { char str1[10] = “abc”; char str2[10] = “def”; clrscr(); printf(“ str1: %s”,str1); printf(“\n str2: %s”,str2); printf(“\n strcmp(str1,str2) = %d”,strcmp(str1,str2)); printf(“\n strcat(str1,str2) = %s”,strcat(str1,str2)); printf(“\n str1: %s”,str1); printf(“\n str2: %s”,str2); printf(“\n strcpy(str1,str2) = %s”,strcpy(str1,str2)); printf(“\n str1: %s”,str1); printf(“\n str2: %s”,str2); strcpy(str1,”ab”); strcpy(str2,”abc”); printf(“\n strcmp(str1,str2) = %d”,strcmp(str1,str2)); getch(); } Kết quả: str1: abc str2: def strcmp(str1,str2) = -3 strcat(str1,str2) = abcdef str1: abcdef str2: def strcpy(str1,str2) = def str1: def str2: def strcmp(str1,str2) = -3 Cần lưu ý khi sử dụng các hàm strcat(), strcpy(),.. là kích thước bộ nhớ lưu trữ dành cho xâu đích phải đủ để chứa kết quả thu được sau lời gọi các hàm trên. III.5. Cấu trúc (2 tiết LT) III.5.1. Khái niệm cấu trúc Kiểu dữ liệu cấu trúc (struct) là kiểu dữ liệu phức hợp bao gồm nhiều thành phần, mỗi thành phần có thể thuộc những kiểu dữ liệu khác nhau. Các thành phần dữ liệu trong cấu trúc được gọi là các trường dữ liệu (field). Ví dụ: khi cần lưu giữ thông tin về một dạng đối tượng nào đó như đối tượng sinh viên chẳng hạn, ta lưu giữ các thông tin liên quan đến sinh viên như họ tên, tuổi, kết quả học tập Mỗi thông tin thành phần lại có kiểu dữ liệu khác nhau như họ tên có kiểu dữ liệu là xâu kí tự, tuổi có kiểu dữ liệu là số nguyên, kết quả học tập có kiểu dữ liệu là số thực. Bài giảng tin học đại cương 152 III.5.2. Khai báo và sử dụng cấu trúc III.5.2.1. Khai báo kiểu dữ liệu cấu trúc Để khai báo một kiểu dữ liệu cấu trúc ta dùng cú pháp khai báo sau: struct tên_cấu_trúc { ; }; Ví dụ: struct sinh_vien { char ma_so_sinh_vien[10]; char ho_va_ten[30]; float diem_TinDC; } struct point_3D { float x; float y; float z; } ma_so_sinh_vien ho_va_ten diem_tinDC si n h _ vi e n x y z p o in t_ 3 D Khai báo thứ nhất là khai báo của một kiểu dữ liệu cấu trúc có tên là sinh_vien gồm có 3 trường dữ liệu là ma_so_sinh_vien kiểu xâu kí tự, ho_va_ten kiểu xâu kí tự và diem_TinDC kiểu số thực float. Ở khai báo thứ 2 ta đã khai báo một kiểu dữ liệu cấu trúc có tên là point_3D gồm có 3 trường và cả 3 trường này đều có cùng kiểu dữ liệu số thực float. Vì 3 trường này cùng kiểu dữ liệu nên ta có thể sử dụng mảng để thay thế cấu trúc, tuy nhiên việc sử dụng cấu trúc để mô tả dữ liệu điểm 3 chiều sẽ giúp sự mô tả được tự nhiên hơn, “thật” hơn (nghĩa là trong cấu trúc các tọa độ vẫn độc lập với nhau, nhưng nhìn từ bên ngoài thì các toạ độ này lại là một thể thống nhất cung cấp thông tin về vị trí của một điểm. Còn nếu sử dụng mảng thì các tọa độ của một điểm độc lập và rời rạc với nhau, không thấy mối liên hệ. Khi đó ta sẽ phải tự mình ngầm quy ước rằng các phần tử của mảng có chỉ số là 3*k, 3*k+1 và 3*k+2 với k = 0, 1, 2, là tọa độ của một điểm). III.5.2.2. Khai báo biến cấu trúc: Để khai báo biến cấu trúc ta dùng cú pháp khai báo sau struct tên_cấu_trúc tên_biến_cấu_trúc; Ví dụ: struct sinh_vien a, b, c; Câu lệnh trên khai báo 3 biến lần lượt tên là a, b, c có kiểu dữ liệu là cấu trúc sinh_vien. Bài giảng tin học đại cương 153 Tuy nhiên ta cũng có thể kết hợp đồng thời vừa khai báo kiểu dữ liệu cấu trúc vừa khai báo biến cấu trúc bằng cách sử dụng cú pháp khai báo sau: struct [tên_cấu_trúc] { ; } tên_biến_cấu_trúc; Theo cú pháp khai báo trên thì ta thấy phần tên_cấu_trúc có thể có hoặc không. Nếu có tên_cấu_trúc thì sau này ta có thể khai báo bổ sung biến có kiểu dữ liệu là tên_cấu_trúc đó, còn nếu không có tên_cấu_trúc thì cấu trúc khai báo tương ứng không được sử dụng về sau. Ví dụ: struct diem_thi { float diem_Toan; float diem_Ly; float diem_Hoa; } struct thi_sinh { char SBD[10]; // số báo danh char ho_va_ten[30]; struct diem_thi ket_qua; } thi_sinh_1, thi_sinh_2; Qua ví dụ trên ta cũng thấy rằng các cấu trúc có thể lồng nhau, nghĩa là cấu trúc này có thể là trường dữ liệu của cấu trúc khác, và mức độ lồng là không hạn chế. Để tăng thêm sự tiện lợi, ngôn ngữ C còn cho phép khai báo trực tiếp trường dữ liệu là cấu trúc bên trong cấu trúc chứa nó, vì thế ta có thể viết lại ví dụ ở trên như sau struct thi_sinh { char SBD[10]; char ho_va_ten[30]; struct diem_thi { float diem_Toan; float diem_Ly; float diem_Hoa; }ket_qua; } thi_sinh_1, thi_sinh_2; III.5.2.3. Định nghĩa kiểu dữ liệu cấu trúc với typedef Trong các ví dụ trước ta thấy một khai báo biến cấu trúc bắt đầu bằng từ khóa struct, sau đó đến tên cấu trúc rồi mới đến tên biến. Cách khai báo này có phần "rắc rối" hơn so với khai báo biến thông thường và có không ít trường hợp người lập trình quên đặt từ khóa struct ở đầu. Để tránh Bài giảng tin học đại cương 154 điều đó, ngôn ngữ C cho phép ta đặt tên mới cho kiểu dữ liệu cấu trúc bằng câu lệnh typedef có cú pháp khai báo như sau : typedef struct tên_cũ tên_mới; Hoặc ta có thể đặt lại tên cho cấu trúc ngay khi khai báo bằng cú pháp typedef struct [tên_cũ] { ; }danh_sách_các_tên_mới; Sau câu lệnh này ta có thể sử dụng tên_mới thay cho tổ hợp struct tên_cũ khi khai báo biến. Lưu ý: Được phép đặt tên_mới trùng với tên_cũ. Ví dụ: struct point_3D { float x, y, z; } P; struct point_3D M; typedef struct point_3D point_3D; point_3D N; Trong ví dụ trên ta đã đặt lại tên cho cấu trúc struct point_3D thành point_3D và dùng tên mới này làm kiểu dữ liệu cho khai báo của biến N. Các biến P, M được khai báo theo cách chúng ta đã biết. Ví dụ: typedef struct point_2D { float x, y; }point_2D, diem_2_chieu, ten_bat_ki; point_2D X; diem_2_chieu Y; ten_bat_ki Z; Với ví dụ này ta cần chú ý là point_2D, diem_2_chieu và ten_bat_ki không phải là tên biến mà là tên mới của cấu trúc struct point_2D. Các biến X, Y, Z được khai báo với kiểu dữ liệu là các tên mới này. III.5.3. Xử lý dữ liệu cấu trúc III.5.3.1. Truy nhập các trƣờng dữ liệu của cấu trúc Dữ liệu của một biến cấu trúc bao gồm nhiều trường dữ liệu, và các trường dữ liệu này độc lập với nhau. Do đó muốn thay đổi nội dung dữ liệu bên trong một biến cấu trúc ta cần truy nhập tới Bài giảng tin học đại cương 155 từng trường và thực hiện thao tác cần thiết trên từng trường đó. Để truy nhập tới một trường trong cấu trúc ta dùng cú pháp sau: tên_biến_cấu_trúc.tên_trường Dấu chấm “.” sử dụng trong cú pháp trên là toán tử truy nhập thành phần cấu trúc, và nếu như trường được truy nhập lại là một cấu trúc thì ta có thể tiếp tục áp dụng toán tử này để truy nhập tới các trường thành phần nằm ở mức sâu hơn. Giờ đây ta có thể “đối xử” tên_biến_cấu_trúc.tên_trường giống như một biến thông thường có kiểu dữ liệu là kiểu dữ liệu của tên_trường, tức là ta có thể nhập giá trị, hiển thị giá trị của biến cấu trúc, sử dụng giá trị đó trong các biểu thức Ví dụ: // dưới đây là một cấu trúc mô tả một điểm trong không gian 2 chiều. // các trường dữ liệu gồm: tên của điểm và tọa độ của điểm đó. // tọa độ là một cấu trúc gồm 2 trường: hoành độ và tung độ #include #include void main() { struct point_2D { char ten_diem; struct { float x, y; } toa_do; } p; float temp_float; char temp_char; printf(“\n Hay nhap thong tin ve mot diem”); printf(“\n Ten cua diem: “); fflush(stdin); scanf(“%c”,&temp_char); p.ten_diem = temp_char; printf(“\n nhap vao hoanh do cua diem: “); scanf(“%f”,&temp_float); p.toa_do.x = temp_float; // giả sử điểm đang xét nằm trên đường thẳng y = 3x + 2; p.toa_do.y = 3*p.toa_do.x + 2; printf(“\n %c = (%5.2f,%5.2f)”,p.ten_diem, p.toa_do.x, p.toa_do.y); getch(); } Kết quả khi chạy chương trình: Hay nhap thong tin ve mot diem Ten cua diem: A Bài giảng tin học đại cương 156 nhap vao hoanh do cua diem: 5 A = ( 5.00,17.00) Lưu ý: Cũng như việc nhập giá trị cho các phần tử của mảng, việc nhập giá trị cho các trường của cấu trúc (đặc biệt là các trường có kiểu dữ liệu float) nên thực hiện qua biến trung gian để tránh những tình huống có thể làm treo máy hoặc kết quả nhập được không như ý. III.5.3.2. Phép gán giữa các biến cấu trúc Giả sử ta có 2 biến cấu trúc là a và b có cùng kiểu dữ liệu là một cấu trúc nào đó, và giả sử các trường dữ liệu của a đều đã được khởi tạo (tức là giá trị của các trường dữ liệu đó đều đã được xác định). Giờ đây ta cũng muốn giá trị các trường dữ liệu của b có giá trị giống với các trường dữ liệu tương ứng của a. Ta có thể thực hiện điều đó bằng cách gán giá trị từng trường của a cho các trường tương ứng của b. Cách này có vẻ rất “thủ công” và rất bất tiện nếu như trong cấu trúc có nhiều trường dữ liệu. Do vậy C cung cấp cho ta một phương tiện để thực hiện việc này như cách thứ 2, đó là sử dung phép gán các biến cấu trúc. Phép gán cấu trúc có cú pháp tương tự như phép gán thông thường biến_cấu_trúc_1 = biến_cấu_trúc_2; Câu lệnh trên sẽ gán giá trị của các trường trong biến_cấu_trúc_2 cho các trường tương ứng trong biến_cấu_trúc_1. Ví dụ: #include #include #include void main() { struct s { char ho_ten[20]; float diem; }a, b, c; float temp_f; printf("\na.ho_ten: ");fflush(stdin); gets(a.ho_ten); printf("\na.diem = ");scanf("%f",&temp_f); a.diem = temp_f; strcpy(c.ho_ten, a.ho_ten); c.diem = a.diem; b = a; printf("\na: %20s %5.2f", a.ho_ten, a.diem); printf("\nb: %20s %5.2f", b.ho_ten, b.diem); printf("\nc: %20s %5.2f\n", c.ho_ten, c.diem); } Bài giảng tin học đại cương 157 Kết quả thực hiện a.ho_ten: nguyen van minh a.diem = 7.5 a: nguyen van minh 7.50 b: nguyen van minh 7.50 c: nguyen van minh 7.50 Trong chương trình trên ta đã nhập giá trị cho các trường của biến cấu trúc a từ bàn phím, sau đó copy dữ liệu từ biến a sang biến c bằng cách sao chép từng trường, và copy dữ liệu từ biến a sang biến b bằng cách dùng lệnh gán. Kết quả là như nhau và rõ ràng cách thứ 2 ngắn gọn hơn. Lưu ý : Để copy dữ liệu là xâu kí tự ta phải dùng lệnh strcpy(), không được dùng lệnh gán thông thường để copy nội dung xâu kí tự. BUỔI 15. III.6. Hàm (2 tiết LT) III.6.1. Khái niệm hàm III.6.1.1. Khái niệm chƣơng trình con Trong khi lập trình chúng ta thường gặp những đoạn chương trình lặp đi lặp lại nhiều lần ở những chỗ khác nhau. Để tránh rườm rà và tiết kiệm công sức, những đoạn chương trình đó được thay thế bởi các chương trình con tương ứng và khi cần ta chỉ việc gọi những chương trình con đó ra mà không phải viết lại cả đoạn chương trình đó. Lấy ví dụ khi giải các bài toán lượng giác ta thường xuyên cần phải tính giá trị sin của đại lượng lượng giác x nào đó. Như vậy ta nên lập một chương trình con tên là sin và tham số là x để tính giá trị sin(x). Mỗi khi cần tính toán giá trị sin của một đại lượng y nào đó thì ta chỉ cần gọi chương trình con sin đã lập sẵn và truyền đại lượng y làm tham số cho chương trình con sin đó thì ta vẫn thu được kết quả mong muốn mà không phải viết lại cả đoạn chương trình tính giá trị sin(y). Bên cạnh chương trình con sin còn có rất nhiều chương trình con khác được tạo sẵn như cos, exp (dùng để tính lũy thừa cơ số e), pow (tính lũy thừa), sqrt (tính căn bậc 2), ... giúp người lập trình tính toán giá trị của các đại lượng thông dụng. Những chương trình con này nằm trong thư viện các chương trình con mẫu và được trình biên dịch C quản lý, vì vậy chúng còn được gọi là các chương trình con chuẩn. Trình biên dịch Turbo C++ phân loại và đặt các chương trình con chuẩn này trong các đơn vị chương trình khác nhau dưới dạng các tệp tiêu đề như stdio.h, conio.h, math.h, string.h... Ngoài ra còn có một lý do khác cần đến chương trình con. Khi ta giải quyết một bài toán lớn thì chương trình của ta có thể rất lớn và dài, điều này làm cho việc sửa chữa, gỡ rối, hiệu chỉnh chương trình gặp nhiều khó khăn. Nhưng nếu ta chia bài toán lớn, phức tạp ban đầu thành các bài toán con nhỏ hơn và tương đối độc lập với nhau, rồi lập các chương trình con giải quyết từng bài Bài giảng tin học đại cương 158 toán con, cuối cùng ghép các chương trình con đó lại thành một chương trình giải quyết bài toán ban đầu thì sẽ rất tiện lợi cho việc phát triển, kiểm tra và sửa chữa cả chương trình. Việc này tương tự như trong dây chuyền sản xuất công nghiệp khi ta lắp ráp sản phẩm hoàn thiện từ các bán thành phẩm, các module được chế tạo ở những nơi khác nhau. Vì các bán thành phẩm này được chế tạo độc lập nên khi phát hiện lỗi ở module nào ta chỉ việc tìm đến nơi sản xuất ra nó để sửa chữa. Việc chia nhỏ một chương trình thành các chương trình con đảm nhận những công việc nhỏ khác nhau chính là tư tưởng chính cho phương pháp lập trình có cấu trúc (structured programming). Cần lưu ý là có khi một chương trình con chỉ sử dụng đúng một lần nhưng nó vẫn làm cho chương trình trở nên sáng sủa và dễ đọc, dễ hiểu hơn. III.6.1.2. Phân loại chƣơng trình con: Có 2 loại chương trình con là hàm (function) và thủ tục (procedure). Sự khác nhau giữa hàm và thủ tục là ở chỗ hàm sau khi thực hiện xong thì sẽ trả về giá trị, còn thủ tục không trả về giá trị gì cả. Mặc dù vậy hàm và thủ tục là tương đương nhau, tức là có thể xây dựng được thủ tục có chức năng tương đương với một hàm bất kì và có thể xây dựng được hàm có chức năng tương đương với một thủ tục bất kì. Vì thế có những ngôn ngữ lập trình cho phép chương trình con có thể là hàm và thủ tục (Pascal) và có những ngôn ngữ chỉ cho phép chương trình con là hàm mà thôi (như C, Java). Lưu ý là nếu chương trình con là hàm thì nó luôn có giá trị trả về. Nếu thực sự không có giá trị gì để trả về (nghĩa là nó hoạt động giống thủ tục) thì ta phải khai báo hàm đó có kiểu giá trị trả về là “không là kiểu giá trị nào cả” (kiểu void trong C). III.6.2. Khai báo và sử dụng hàm III.6.2.1. Khai báo hàm Cú pháp khai báo một hàm trong C là như sau [] ([,]) Thân hàm Khai báo của một hàm được chia làm 2 phần: - Dòng đầu hàm: [] ([,]) - Thân hàm: là tập hợp các khai báo và câu lệnh đặt trong cặp dấu ngoặc nhọn { Bài giảng tin học đại cương 159 } Trong thân hàm có ít nhất một lệnh return. Ví dụ sau là khai báo và định nghĩa hàm tính giai thừa của một số nguyên dương. Ta quy ước rằng giai thừa của một số âm thì bằng –1, của 0 bằng 0, của một số nguyên dương a là a! = a  (a-1)  1. int giai_thua(int a) { int ket_qua; int i; ket_qua = 1; for(i = 0;i<a;i++) ket_qua = ket_qua * i; if(a < 0) ket_qua = -1; if(a == 0) ket_qua = 0; return ket_qua; }    Dòng đầu hàm Các khai báo Các câu lệnh Các thành phần của dòng đầu hàm Dòng đầu hàm là các thông tin được trao đổi giữa bên trong và bên ngoài hàm. Khi nói tới dòng đầu hàm là ta nói tới tên của hàm, hàm đó cần những thông tin gì từ môi trường để hoạt động (các tham số đầu vào), hàm đó cung cấp những thông tin gì cho môi trường (những tham số đầu ra và giá trị trả về). Dòng đầu hàm phân biệt các hàm với nhau, hay nói cách khác không được có 2 hàm có dòng đầu hàm giống nhau. Kiểu dữ liệu trả về của hàm Thông thường hàm sau khi được thực hiện sẽ trả về một giá trị kết quả tính toán nào đó. Để sử dụng được giá trị đó ta cần phải biết nó thuộc kiểu dữ liệu gì. Kiểu dữ liệu của đối tượng tính toán được hàm trả về được gọi là kiểu dữ liệu trả về của hàm. Trong C, kiểu dữ liệu trả về của hàm có thể là kiểu dữ liệu bất kì (kiểu dữ liệu có sẵn hoặc kiểu dữ liệu do người dùng tự định nghĩa) nhưng không được là kiểu dữ liệu mảng. Nếu kiểu dữ liệu trả về là kiểu void thì hàm không trả về giá trị nào cả. Trường hợp ta không khai báo kiểu dữ liệu trả về thì chương trình dịch của C sẽ ngầm hiểu rằng kiểu dữ liệu trả về của hàm là kiểu int. Bài giảng tin học đại cương 160 Tên hàm Tên hàm là có thể là bất kì một định danh hợp lệ nào. Tuy nhiên tên hàm nên mang nghĩa gợi ý chức năng công việc mà hàm thực hiện. Ví dụ một hàm có chức năng tính và trả về bình phương của một số thực x thì nên có tên là binh_phuong. Trong C, các hàm không được đặt tên trùng nhau. Tham số của hàm Tham số của hàm là các thông tin cần cho hoạt động của hàm và các thông tin, kết quả tính toán được hàm trả lại. Tức là có những tham số chứa dữ liệu vào cung cấp cho hàm, có những tham số chứa dữ liệu ra mà hàm tính toán được. Các tham số sử dụng trong lời khai báo hàm được gọi là tham số hình thức. Nó là tham số giả định của hàm. Khi khai báo tham số hình thức của hàm phải chỉ ra tên của tham số và kiểu dữ liệu của tham số. Các tham số được cung cấp cho hàm trong quá trình thực hiện của hàm được gọi là tham số thực. Kiểu dữ liệu của tham số thực cung cấp cho hàm trong chương trình phải giống kiểu dữ liệu của tham số hình thức tương ứng với tham số thực đó, nếu không sẽ có báo lỗi biên dịch. Một hàm có thể có một, nhiều hoặc không có tham số nào cả. Nếu có nhiều tham số thì chúng phải được phân cách với nhau bằng dấu phẩy. Lưu ý là nếu hàm không có tham số nào cả thì vẫn phải có cặp dấu ngoặc đơn sau tên hàm, ví dụ main(). Lệnh return Trong chương trình, một hàm được thực hiện khi ta gặp lời gọi hàm của hàm đó trong chương trình. Một lời gọi hàm là tên hàm theo sau bởi các tham số thực trong chương trình. Sau khi hàm thực hiện xong, nó sẽ trở về chương trình đã gọi nó. Có 2 cách để từ hàm trở về chương trình đã gọi hàm: - Sau khi thực hiện tất cả các câu lệnh có trong thân hàm. - Khi gặp lệnh return. Cú pháp chung của lệnh return là return biểu_thức; Khi gặp lệnh này, chương trình sẽ tính toán giá trị của biểu_thức, lấy kết quả tính toán được làm giá trị trả về cho lời gọi hàm rồi kết thúc việc thực hiện hàm, trở về chương trình đã gọi nó. Trong lệnh return cũng có thể không có phần biểu_thức, khi đó ta sẽ kết thúc thực hiện hàm mà không trả về giá trị nào cả. Ví dụ và phân tích. #include #include int max(int x, int y, int z) { int max; max = x>y?x:y; max = max>z?max:z; Bài giảng tin học đại cương 161 return max; } void main() { int a,b,c; clrscr(); printf("\n Nhap gia tri cho 3 so nguyen a, b, c: "); scanf("%d %d %d",&a,&b,&c); printf("\n Gia tri cac so vua nhap: "); printf(" a = %-5d b = %-5d c = %-5d"); printf("\n Gia tri lon nhat trong 3 so la %d",max(a,b,c)); getch(); } III.6.2.2. Sử dụng hàm Một hàm sau khi khai báo thì có thể sử dụng. Để sử dụng một hàm (hay còn nói là gọi hàm) ta sử dụng cú pháp sau: ([danh sách các tham số]) Ví dụ: chương trình dưới đây sẽ khai báo và định nghĩa một hàm có tên là Uscln với 2 tham số đều có kiểu unsigned int. Hàm Uscln tìm ước số chung lớn nhất của 2 tham số này theo thuật toán Euclid và trả về ước số chung tìm được. Sau đó ta sẽ gọi hàm Uscln trong hàm main để tìm ước số chung lớn nhất của 2 số nguyên được nhập từ bàn phím. #include #include unsigned int Uscln(unsigned int a, unsigned int b) { unsigned int u; if (a<b) { u = a; a = b; b = u; } do { u = a%b; a = b; b = u; }while (u!=0); return a; } int main() { unsigned int a, b; do { Bài giảng tin học đại cương 162 printf("\n Nhap vao 2 so nguyen duong a va b "); printf("\n a = "); scanf("%d",&a); printf("\n b = "); scanf("%d",&b); if(a*b == 0) { printf("\n Khong hop le"); continue; } printf("\n Uoc chung lon nhat cua %d va %d la: %d", a, b, Uscln(a, b)); }while ((a != 0)||(b != 0)); printf("\n An phim bat ki de ket thuc chuong trinh..."); getch(); return 0; } Kết quả khi thực hiện: Nhap vao 2 so nguyen duong a va b a = 6 b = 9 Uoc chung lon nhat cua 6 va 9 la: 3 Nhap vao 2 so nguyen duong a va b a = 15 b = 26 Uoc chung lon nhat cua 15 va 26 la: 1 Nhap vao 2 so nguyen duong a va b a = 3 b = 0 Khong hop le Nhap vao 2 so nguyen duong a va b a = 0 b = 0 Khong hop le An phim bat ki de ket thuc chuong trinh... Lưu ý:  Nếu có nhiều tham số trong danh sách tham số thì các tham số được phân cách với nhau bằng dấu phẩy  Cho dù hàm có một, nhiều hay không có tham số thì vẫn luôn luôn cần cặp dấu ngoặc đơn đứng sau tên hàm Trong chương trình, khi gặp một lời gọi hàm thì hàm bắt đầu thực hiện bằng cách chuyển các lệnh thi hành đến hàm được gọi. Quá trình diễn ra như sau: - Nếu hàm có tham số, trước tiên các tham số sẽ được gán giá trị thực tương ứng. - Chương trình sẽ thực hiện tiếp các câu lệnh trong thân hàm bắt đầu từ lệnh đầu tiên đến câu lệnh cuối cùng. Bài giảng tin học đại cương 163 - Khi gặp lệnh return hoặc dấu } cuối cùng trong thân hàm, chương trình sẽ thoát khỏi hàm để trở về chương trình gọi nó và thực hiện tiếp tục những câu lệnh của chương trình này. III.6.3. Phạm vi của biến Phạm vi của các biến Một biến sau khi khai báo thì có thể được sử dụng trong chương trình. Tuy nhiên tùy vào vị trí khai báo biến mà phạm vi sử dụng các biến sẽ khác nhau. Nguyên tắc sử dụng biến là biến khai báo trong phạm vi nào thì được sử dụng trong phạm vi đó. Một biến có thể được khai báo trong chương trình chính hoặc trong các chương trình con hoặc thậm chí trong một lệnh khối. Nếu biến được khai báo trong một lệnh khối thì nó chỉ có thể được gọi trong lệnh khối đó thôi, không thể gọi từ bên ngoài lệnh khối được. Một biến được khai báo trong một chương trình con chỉ có thể được sử dụng trong phạm vi chương trình con đó. Một biến được khai báo trong chương trình chính thì có thể được sử dụng trong toàn bộ chương trình, trong tất cả các chương trình con cũng như trong các lệnh khối của chương trình. Lưu ý  Một số ngôn ngữ lập trình như Pascal cho phép khai báo một chương trình con nằm trong một chương trình con khác, nhưng ngôn ngữ C không cho phép khai báo một chương trình con nằm trong một chương trình con khác.  Bên trong một lệnh khối thì có thể có chứa lệnh khối khác. Khi đó biến được khai báo ở lệnh khối bên ngoài có thể được sử dụng ở lệnh khối bên trong.  Việc trùng tên của các biến: Trong cùng một phạm vi ta không được phép khai báo 2 biến có cùng tên nhưng ta có thể khai báo 2 biến trùng tên thuộc 2 phạm vi khác nhau. Nếu có 2 biến trùng tên khai báo ở 2 phạm vi khác nhau thì xảy ra 2 trường hợp: o Hai phạm vi này tách rời nhau: khi đó các biến sẽ có tác dụng ở phạm vi riêng của nó, không ảnh hưởng đến nhau. o Phạm vi này nằm trong phạm vi kia: khi đó nếu chương trình đang ở phạm vi ngoài (tức là đang thực hiện câu lệnh nằm ở phạm vi ngoài) thì biến khai báo ở phạm vi ngoài có tác dụng, còn nếu chương trình đang ở phạm vi trong (đang thực hiện câu lệnh nằm ở phạm vi trong) thì biến khai báo ở phạm vi trong sẽ có tác dụng và nó che lấp biến trùng tên ở bên ngoài. Ví dụ: #include void main() { { int a = 1; printf(“\n a = %d”,a); { int a = 2; Bài giảng tin học đại cương 164 printf(“\n a = %d”,a); } printf(“\n a = %d”,a); } { int a = 3; printf(“\n a = %d”,a); } } Kết quả thực hiện chương trình a = 1 a = 2 a = 1 a = 3 Phân loại biến Theo phạm vi sử dụng, biến chia làm 2 loại: biến cục bộ (biến địa phương – local variable) và biến toàn cục (global variable). Biến địa phƣơng Là các biến được khai báo trong lệnh khối hoặc trong thân chương trình con. Việc khai báo các biến cục bộ phải được đặt trước phần câu lệnh trong lệnh khối hoặc trong chương trình con. Biến toàn cục Là biến được khai báo trong chương trình chính. Vị trí khai báo của biến toàn cục là sau phần khai báo tệp tiêu đề và khai báo hàm nguyên mẫu. Lưu ý: Hàm main() cũng chỉ là một chương trình con, nhưng nó là chương trình con đặc biệt ở chỗ chương trình được bắt đầu thực hiện từ hàm main(). Biến khai báo trong hàm main() không phải là biến toàn cục mà là biến cục bộ của hàm main(). Một số lệnh đặc trưng của C: register, static Chúng ta biết rằng các thanh ghi có tốc độ truy nhập nhanh hơn so với các loại bộ nhớ khác (RAM, bộ nhớ ngoài), do vậy nếu một biến thường xuyên sử dụng trong chương trình được lưu vào trong thanh ghi thì tốc độ thực hiện của chương trình sẽ được tăng lên. Để làm điều này ta đặt từ khóa register trước khai báo của biến đó. Ví dụ register int a; Tuy nhiên có một lưu ý khi khai báo biến register là vì số lượng các thanh ghi có hạn và kích thước của các thanh ghi cũng rất hạn chế (ví dụ trên dòng máy 80x86, các thanh ghi có kích Bài giảng tin học đại cương 165 thước 16 bit = 2 byte) cho nên số lượng biến khai báo register sẽ không nhiều và thường chỉ áp dụng với những biến có kích thước nhỏ như kiểu char, int. Như ta đã biết, một biến cục bộ khi ra khỏi phạm vi của biến đó thì bộ nhớ dành để lưu trữ biến đó sẽ được giải phóng. Tuy nhiên trong một số trường hợp ta cần lưu giá trị của các biến cục bộ này để phục vụ cho những tính toán sau này, khi đó ta hãy khai báo biến với từ khóa static ở đầu. Ví dụ static int a; Từ khóa static giúp chương trình dịch biết được đây là một biến tĩnh, nghĩa là nó sẽ được cấp phát một vùng nhớ thường xuyên từ lúc khai báo và chỉ giải phóng khi chương trình chính kết thúc. Như vậy về thời gian tồn tại biến static rất giống với biến toàn cục, chỉ có một sự khác biệt nhỏ là biến toàn cục thì có thể truy cập ở mọi nơi trong chương trình (miễn là ở đó không có biến địa phương nào cùng tên che lấp nó), còn biến static thì chỉ có thể truy nhập trong phạm vi mà nó được khai báo mà thôi. Hãy xét ví dụ sau: # include # include void fct() { static int count = 1; printf("\n Day la lan goi ham fct lan thu %2d", count++); } void main() { int i; for(i = 0; i < 10; i++) fct(); getch(); } Kết quả khi thực hiện Day la lan goi ham fct lan thu 1 Day la lan goi ham fct lan thu 2 Day la lan goi ham fct lan thu 3 Day la lan goi ham fct lan thu 4 Day la lan goi ham fct lan thu 5 Day la lan goi ham fct lan thu 6 Day la lan goi ham fct lan thu 7 Day la lan goi ham fct lan thu 8 Day la lan goi ham fct lan thu 9 Day la lan goi ham fct lan thu 10 Bài giảng tin học đại cương 166 Bài tập về Lập trình (2 tiết BT)

Các file đính kèm theo tài liệu này:

  • pdfbai_giang_tin_hoc_dai_cuong_ban_dep.pdf