Giáo trình phương pháp lập trình

. Viết chương trình nhập vào mảng một chiều có n số nguyên dương. Hãy cho biết số nào trong mảng có giá trị gần với trung bình cộng của toàn mảng. 19. Nhập vào một mảng có n số nguyên dương khác nhau. Hãy in ra tất cả các phần tử trong mảng có giá trị nhỏ hơn giá trị lớn nhất và lớn hơn giá trị nhỏ nhất của mảng. 20. Viết chương trình nhập ngẫu nhiên một mảng có n số nguyên dương. Nhập vào một số nguyên dương k. Hãy tính trung bình cộng của các phần tử trong mảng có giá trị lớn hơn hay bằng k. 21. Nhập vào một dãy số nguyên dương ngẫu nhiên (random) có n phần tử. Viết chương trình in ra số lớn hơn số nhỏ nhất của dãy và nhỏ hơn hay bằng với mọi số còn lại (nghĩa là tìm số nhỏ thứ hai trong dãy). Nếu n phần tử ñều bằng nhau thì thông báo: không tồn tại số cần tìm.

pdf124 trang | Chia sẻ: huyhoang44 | Lượt xem: 662 | Lượt tải: 0download
Bạn đang xem trước 20 trang tài liệu Giáo trình phương pháp lập trình, để xem tài liệu hoàn chỉnh bạn click vào nút DOWNLOAD ở trên
: Tìm min của 4 giá trị a,b,c,d #include int min(int a, int b); //prototype void main() { int a=40, b=30, c=10, d=20, min4; min4 = min(a,b); min4 = min(min4,c); min4 = min(min4,d) cout << “Min = “ << min4; } int min(int a, int b) { if(a<b) return a; else return b; } 2. Dạng tổng quát của hàm Hàm có dạng tổng quát như sau: returnType functionName(parameterList) { body of the function } Giáo trình PP lập trình TT.Công Nghệ Thông Tin 83/124 returnType: Kiểu dữ liệu của giá trị trả về bởi hàm. Nếu hàm không trả về giá trị thì returnType là void functionName: Tên hàm. parameterList: Danh sách các tham số hình thức ñược ñặt trong cặp dấu (). Là danh sách các tên biến và kiểu dữ liệu tương ứng của chúng phân cách nhau bởi dấu phẩy. Nếu hàm không có tham số thì danh sách này là rỗng. Ví dụ: xem khai báo hàm sau: int max(int a, int b) { if(a<b) return b; else return a; } Trong ví dụ trên, max là tên hàm, hàm cần 2 tham số ñể họat ñộng là 2 biến nguyên, dữ liệu trả về của hàm có kiểu int. Phần mã nằm giữa {} là thân hàm. 3. Các qui tắc về phạm vi của hàm Hàm giống như một hộp ñen, bên trong hàm (thân hàm) là không biết ñối với những lệnh nằm ngoài hàm. Phần thân của hàm chỉ ñược thực thi khi hàm ñược gọi. Do ñó, không có lệnh nào bên ngoài hàm có thể nhảy trực tiếp vào thân hàm. Tóm lại, mã và dữ liệu bên trong một hàm không thể tương tác với mã và dữ liệu nằm trong một hàm khác. Những biến ñược khai báo bên trong hàm (kể cả các tham số hình thức) là những biến cục bộ. Những biến này ñược tạo ra khi hàm ñược gọi và biến mất sau khi hàm thực thi xong. 4. Tham số hình thức và tham số thực Khi hàm cần nhận ñối số (arguments) ñể thực thi thì khi khai báo hàm cần khai báo danh sách các tham số ñể nhận giá trị từ chương trình gọi. Các tham số này ñược gọi là tham số hình thức. Khi gọi hàm, ta cung cấp các giá trị thật, các giá trị này sẽ ñược sao chép vào các tham số hình thức và khi ñó ta gọi chúng là tham số thực. Giáo trình PP lập trình TT.Công Nghệ Thông Tin 84/124 Ví dụ: Xem xét ñịnh nghĩa hàm sau int min(int a, int b) { if(a<b) return a; else return b; } Trong ñịnh nghĩa hàm min ở trên thì a và b là 2 tham số hình thức. Khi gọi hàm như câu lệnh sau: minAB = min(15,7); thì 15 ñược sao chép vào biến a, 7 ñược sao chép vào biến b. 15 và 7 ñược gọi là ñối số. Khi a và b nhận giá trị do lời gọi hàm thì chúng ñược gọi là tham số thực. Có hai cách truyền ñối số vào tham số hình thức: Truyền tham trị và truyền tham biến. 4.1. Truyền tham trị (call by value) Cách này sao chép giá trị của ñối số vào tham số hình thức của hàm. Trong trường hợp này, những thay ñổi của tham số không ảnh hưởng ñến ñối số. Như vậy, nếu không muốn hàm làm thay ñổi giá trị của ñối số truyền vào thì ta khai báo tham số của hàm là biến thông thường (nghĩa là không phải biến con trỏ). int min(int a, int b) { if(a<b) return a; else return b; } minAB = min(15,7); sao chép ñối số (arguments) Tham số hình thức Giáo trình PP lập trình TT.Công Nghệ Thông Tin 85/124 Ví dụ: Khảo sát chương trình sau #include void doubleNum(int a); //prototype void main() { int a=40; doubleNum(a); cout << “Inside main function:” << endl; cout << “a = “ << a << endl; } void doubleNum(int a) { a = a*2; cout << “Inside doubleNum function. a = “ << a; } Khi ñối số a của hàm main ñược truyền vào tham số a của hàm doubleNum thì cách truyền này là truyền tham trị, nghĩa là giá trị của a là 40 ñược truyền vào tham số a. Trong thân hàm doubleNum, tham số a ñược nhân ñôi và có giá trị là 80. Khi hàm doubleNum kết thúc, biến a trong hàm main vẫn không thay ñổi. 4.2. Truyền tham chiếu (call by reference) Trong cách này, ñịa chỉ của ñối số ñược sao chép vào tham số hình thức. Do ñó, những thay ñổi làm ñối với tham số sẽ có tác dụng trên ñối số. Ví dụ 1: Xem xét ví dụ trên ñược viết lại như sau: #include void doubleNum(int *b); //prototype void main() { int a=40; doubleNum(&a); cout << “Inside main function:” << endl; cout << “a = “ << a << endl; } void doubleNum(int *b) { *b = *b + *b; cout << “Inside doubleNum function. a = “ << *b; } Giáo trình PP lập trình TT.Công Nghệ Thông Tin 86/124 Trong chương trình trên, khi gọi hàm doubleNum ta truyền ñịa chỉ của ñối số a vào biến con trỏ b của hàm. Như vậy, con trỏ b sẽ chứa ñịa chỉ của biến a của hàm main. Trong thân hàm doubleNum, lệnh làm gấp ñôi giá trị của vùng nhớ do con trỏ b trỏ ñến thực chất ñã làm tăng gấp ñôi giá trị của biến a trong hàm main. Ví dụ 2: Hoán ñổi giá trị 2 biến dùng con trỏ (truyền tham chiếu) #include void swap(int *a, int *b); //prototype void main() { int a = 20, b = 40; int *pa, *pb; pa = &a; pb = &b; swap(pa,pb); cout << “After call swap, Values:” << endl; cout << “a = “ << a << endl; cout << “b = “ << b << endl; } void swap(int *a, int *b) { int temp; temp = *a; *a = *b; *b = temp; } 5. Truyền mảng vào hàm Khi một mảng ñược dùng như một ñối số ñể truyền cho hàm, ñịa chỉ của mảng ñược truyền ñến hàm vào tham số hình thức. Như vậy, truyền mảng vào hàm mặc ñịnh là truyền tham chiếu. Do ñó, những thay ñổi ñến giá trị của các phần tử mảng trong thân hàm sẽ ảnh hưởng ñến mảng gốc. Giáo trình PP lập trình TT.Công Nghệ Thông Tin 87/124 Ví dụ: Viết chương trình thay ñổi giá trị các phần tử mảng theo yêu cầu: nếu giá trị >=0 thì thay bằng 1, ngược lại thay bằng 0. #include void change(int a[], int elements); //prototype void main() { int arr[] = {5, -5, -3, 3, 7, -7}; change(arr,6); cout << “After call change, value of array:\n”; for(int i=0 ; i<6 ; i++) cout << “arr[“ <<i<< “] = “ << arr[i] << endl; } void change(int a[], int elements) { for(int i=0 ; i<elements ; i++) if(a[i] < 0) a[i]=0; else a[i] = 1; } Như vậy, sau khi gọi hàm change, giá trị các phần tử trong mảng bị thay ñổi thành 0 hay 1. 6. ðối số của hàm main Hàm main là ñiểm bắt ñầu của mọi chương trình C/C++. Thỉnh thoảng, ta cần truyền thông tin vào hàm main khi nó thực thi. Những thông tin này ta gọi là ñối số dòng lệnh (command line arguments). Hàm main có 2 tham số là argv và argc dùng ñể nhận các ñối số dòng lệnh. Tham số argc là một biến nguyên giữ số ñối số có trong dòng lệnh. Tham số argv là một mảng con trỏ char. Mỗi phần tử của mảng này trỏ ñến một ñối số dòng lệnh. Tất cả ñối số dòng lệnh là chuổi (string). Giáo trình PP lập trình TT.Công Nghệ Thông Tin 88/124 Khi hàm main có nhận ñối số dòng lệnh, nó ñược khai báo như sau: int main(int argc, char *argv[]) Xem xét chương trình sau: #include int main(int argc, char *argv[]) { if(argc!=2) { count << “Hello, “ << argv[1]; exit(1); } return 0; } Giả sử sau khi biên dịch chương trình trên, ta ñược tập tin thực thi là greeting.exe Tại dấu nhắc hệ ñiều hành DOS, ta nhập lệnh sau: greeting Mr.IT Thì trên màn hình xuất hiện: Hello, Mr.IT Lệnh geeting Mr.IT gồm 2 ñối số dòng lệnh là getting và Mr.IT, các ñối số này ñược trỏ ñến bởi 2 con trỏ là argv[0] và argv[1]. Do ñó, khi thực thi chương trình trên, chuổi Mr.IT sẽ ñược in ra. Khi một chương trình không yêu cầu cung cấp ñối số dòng lệnh, thông thường nó ñược khai báo là main() mà không có tham số. ðây là mẫu ñược dùng cho hầu hết các ví dụ trong tài liệu này. 7. Lệnh return Lệnh return có 2 cách dùng quan trọng. Thứ nhất, nó kết thúc ngay lập tức hàm chứa nó khiến sự thực thi chương trình ñược trả về cho chương trình gọi hàm. Thứ hai, nó dùng ñể trả về một giá trị cho chương trình gọi. Giáo trình PP lập trình TT.Công Nghệ Thông Tin 89/124 7.1. Cách dùng thứ nhất (kết thúc hàm) Có hai cách thức ñể hàm kết thúc sự thực thi của nó và trả ñiều khiển về chương trình gọi nó. Một là (thông thường) là khi lệnh cuối cùng có trong hàm ñược thực thi. Ví dụ: Xem xét chương trình sau: #include void printMessage(); void main() { cout << “Calling printMessage: “ <<endl; printMessage(); cout << “Calling printMessage again: “ << endl; printMessage(); } void printMessage() { cout << “This message comes from the printMessage function.” << endl; } Trong ví dụ trên, vì hàm main khai báo kiểu trả về là void nên sau khi thực thi tất cả các lệnh có trong hàm main, chương trình kết thúc và trả quyền ñiều khiển về cho hệ ñiều hành. Trong thân hàm main, khi gọi hàm printMessage, ñiều khiển ñược trao cho hàm printMessage, các lệnh trong thân hàm printMessage thực thi và rồi ñiều khiển ñược trả về cho chương trình gọi (trong trường hợp này là hàm main). Lưu ý là cả hai hàm trên ñều không dùng lệnh return. Hai là khi hàm thực hiện câu lệnh return. Ví dụ: In các phần tử của mảng ñến khi gặp phần tử có giá trị âm #include void main() { int a[] = {3,2,1,0,-1,-2,-3]; for(int i=0 ; i<7 ; i++) { if(a[i] < 0) return; count << setw(5) << a[i]; } } Giáo trình PP lập trình TT.Công Nghệ Thông Tin 90/124 Thực hiện chương trình trên, khi duyệt ñến phần tử a[4] có giá trị là -1 nên biểu thức ñiều kiện của câu lệnh if là true nên lệnh return ñược thực thi và chương trình kết thúc. 7.2. Cách dùng thứ hai (trả về một giá trị) Xem xét ví dụ sau: Tính tổng các phần tử có trong mảng #include int total(int a[], int size); void main() { int a1[] = {1,2,3]; int a2[] = {1,2,3,4,5,6]; int total1, total2; total1 = total(a1,3); total2 = total(a2,6); cout << “Tong mang 1 la = “ << total1 << endl; cout << “Tong mang 2 la = “ << total2 << endl; } int total(int a[], int size) { int sum=0; for(int i=0 ; i<size ; i++) sum += a[i]; return sum; } Như vậy, mỗi khi hàm total ñược gọi, mảng tương ứng ñược tính tổng và lệnh return trả về giá trị này cho chương trình gọi. 8. ðệ qui Một hàm có thể gọi ñến chính nó. Một hàm ñược gọi là ñệ qui nếu một lệnh trong thân hàm gọi ñến chính hàm ñó. Ví dụ, xem xét chương trình tính giai thừa của n. n! = 1*2*..*n Giáo trình PP lập trình TT.Công Nghệ Thông Tin 91/124 #include int giaiThua(int n); void main() { int gt4, gt7; gt4 = giaiThua(4); gt7 = giaiThua(7); cout << “4! =“ << gt4 << endl; cout << “7! =“ << gt7 << endl; } int giaiThua(int n) { int gt; if(n==1) return(1); gt = giaiThua(n-1)*n; // goi de qui return gt; } 9. Nguyên mẫu hàm (function prototypes) Trong C/C++, tất cả các hàm phải ñược khai báo trước khi chúng ñược sử dụng. Việc này thực hiện bằng cách khai báo nguyên mẫu của hàm. Nguyên mẫu hàm cho phép C/C++ cung cấp chức năng kiểm tra sự hợp lệ của tham số khi ñịnh nghĩa cũng như khi gọi hàm. Khi biên dịch, trình biên dịch sẽ dựa vào nguyên mẫu hàm ñể kiểm tra xem có sự không hợp lệ nào của các ñối số khi gọi hàm và kiểu của các tham số hình thức trong ñịnh nghĩa hàm. Nó cũng kiểm tra xem số ñối số cung cấp khi gọi hàm có phù hợp với số tham số hình thức của hàm. Dạng tổng quát của một nguyên mẫu hàm: type functionName(type parameter1, type parameter2, ...); type: kiểu dữ liệu trả về bởi hàm functionName: tên hàm parameter1, parameter2,... : danh sách các tham số hình thức và kiểu của chúng. Lưu ý: Khai báo nguyên mẫu hàm phải có dấu chấm phẩy ở cuối. Nhưng khi ñịnh nghĩa hàm thì không có. Giáo trình PP lập trình TT.Công Nghệ Thông Tin 92/124 10. Cấu trúc của một chương trình viết dưới dạng hàm - Phần khai báo các thư viện - Phần khai báo các hằng toàn cục (nếu có) - Phần khai báo các biến toàn cục (nếu có) - Phần khai báo các nguyên mẫu hàm (prototype) - Phần hàm main (sẽ gọi các hàm thực hiện) - Phần ñịnh nghĩa các hàm ñã ñược khai báo prototype Ví dụ : Viết chương trình nhập vào 2 số nguyên a,b và xuất ra màn hình số lớn nhất trong 2 số (sử dụng hàm) #include // Khai báo thư viện iostream.h #include // Khai báo thư viện conio.h int max(int x, int y);// khai báo nguyên mẫu hàm max (prototype) void main()//hàm main (sẽ gọi các hàm thực hiện) { int a, b;// khai báo biến cout<<” Nhap vao 2 so a, b "; cin>>a>>b; cout<<”so lon nhat la:”<< max(a,b); getch(); return; } int max(int x, int y)// ðịnh nghĩa hàm max(a,b) ñã ñược khai báo prototype { return (x>y) ? x:y; } Giáo trình PP lập trình TT.Công Nghệ Thông Tin 93/124 BÀI TẬP CHƯƠNG 6 Viết lại tất cả bài tập chương 3 và 4 dưới dạng hàm. Giáo trình PP lập trình TT.Công Nghệ Thông Tin 94/124 Chương 7 CHUỖI KÝ TỰ (Strings) 1. Chuổi Trong C/C++, một chuổi là một mảng ký tự với ký tự null ở cuối chuổi. Ký tự null (‘\0’) là ký tự dùng ñể kết thúc chuổi. Như vậy, một chuổi bao gồm các ký tự tạo nên chuổi và theo sau là ký tự null. Khi khai báo một mảng ký tự dùng ñể chứa chuổi, ta cần khai báo nó dài hơn 1 byte ñể chứa ký tự null. Ví dụ: ñể khai báo một mảng str ñể chứa chuổi có ñộ dài 10 ký tự, ta phải khai báo như sau: char str[11]; Hằng chuổi là chuổi ñược bao quanh bởi cặp dấu nháy ñôi. Ví dụ: "Hello" là một hằng chuổi. Ta không cần thêm ký tự null vào sau chuổi vì trình biên dịch sẽ làm ñiều này tự ñộng. 2. Khai báo và khởi tạo chuổi Có 2 cách khai báo và khởi tạo chuổi. Giả sử khai báo và khởi tạo chuổi “Hello”. Cách 1: Dùng mảng một chiều char str[] = {‘H’,’e’,’l’,’l’,’o’,’\0’}; Lưu ý: trong trường hợp này, ta phái thêm ký tự null vào cuối. hoặc char str[] = “Hello”; Lưu ý không cung cấp ký tự null. Chuổi trên ñược lưu trữ trong bộ nhớ như sau: ‘H’ ‘e’ ‘l’ ‘l’ ‘o’ ‘\0’ str[0] str[1] str[2] str[3] str[4] str[5] ký tự null Giáo trình PP lập trình TT.Công Nghệ Thông Tin 95/124 Cách 2: Dùng con trỏ char *str = “Hello”; 3. Nhập chuổi ðể nhập dữ liệu cho biến chuổi, ta dùng hàm gets() của thư viện stdio.h. Hàm này có cú pháp sau: char *gets(char *s); Hàm gets() ñọc các ký tự từ bàn phím (stdin) vào trong mảng trỏ ñến bởi s cho ñến khi nhấn Enter. Ký tự null sẽ ñược ñặt sau sau ký tự cuối cùng của chuổi nhập vào trong mảng. Hoặc ta có thể dùng cin (Console INput). Cú pháp như sau: cin >> s; 4. Xuất chuổi ðể xuất chuổi ra màn hình, ta dùng hàm puts() của thư viện stdio.h. Hàm này có cú pháp sau: int puts(const char *s); Hoặc ta có thể dùng cout (Console OUTput). Cú pháp như sau: cout << s; 5. Một số hàm thư viện thao tác trên chuổi ðể sử dụng các hàm này, ta phải khai báo dòng lệnh sau: #include strcpy(s1, s2) Sao chép chuổi s2 vào s1 strcat(s1, s2) Nối chuổi s2 vào cuối chuổi s1 strlen(s1) Trả về ñộ dài của chuổi strcmp(s1, s2) Trả về 0 nếu s1 và s2 giống nhau, giá trị nhỏ hơn 0 nếu s1<s2 và giá trị lớn hơn 0 nếu s1>s2 Giáo trình PP lập trình TT.Công Nghệ Thông Tin 96/124 strchr(s1, ch) Trả về con trỏ ñến vị trí xuất hiện ñầu tiên của ký tự ch trong chuổi s1 strstr(s1, s2) Trả về con trỏ ñến vị trí xuất hiện ñầu tiên của chuổi s2 trong s1 6. Một số ví dụ về chuổi Ví dụ 1: #include #include #include void main() { char s1[80], s2[80]; cout << “Input the first string: “; gets(s1); cout << “Input the second string: “; gets(s2); cout << "Length of s1= “ << strlen(s1); cout << "Length of s2= “ << strlen(s2); if(!strcmp(s1, s2)) cout << "These strings are equal\n"; strcat(s1, s2); cout << “s1 + s2: “ << s1 << endl;; strcpy(s1, "This is a test.\n"); cout << s1; if(strchr("hello", 'e')) cout << "e is in hello\n"; if(strstr("hi there", "hi")) cout << "found hi"; } Giáo trình PP lập trình TT.Công Nghệ Thông Tin 97/124 Ví dụ 2: Nhập một chuổi str, nhập một ký tự ch. Cho biết ch xuất hiện bao nhiêu lần trong chuổi str. #include #include #include void main() { char str[80], ch; int num=0; cout << “Input str: “; gets(str); cout << “Input ch: “; cin >> ch; for(int i=0 ; i<strlen(str) ; i++) if(str[i] == ch) num++; cout << ch << “ is appeared “ << num << “ times.”; } 7. Mảng các chuổi ðể tạo một mảng các chuổi, dùng một mảng ký tự hai chiều. Kích thước của chỉ mục thứ nhất là số chuổi và kích thước của chỉ mục thứ hai xác ñịnh chiều dài lớn nhất của mỗi chuổi. ðọan mã dưới ñây khai báo một mảng của 5 chuổi, mỗi chuổi có chiều dài tối ña là 79 ký tự. char str[5][80]; ðể nhập dữ liệu cho chuổi thứ nhất từ bàn phím, ta dùng lệnh: gets(str[0]); cin >> str[0] //Tuong duong voi lenh tren ðể xuất chuổi thứ hai ra màn hình, ta dùng lệnh: puts(str[1]); cout << str[1]; //Tuong duong voi lenh tren Giáo trình PP lập trình TT.Công Nghệ Thông Tin 98/124 Khai báo và khởi tạo mảng các chuổi char arrayList[][length] = { constantString_1, constantString_2, ... constantString_n}; arrayList: Tên của mảng chuổi constantString_1, ..., constantString_n : Các hằng chuổi Ví dụ: ðể khai báo một mảng danh sách các ngôn ngữ lập trình thông dụng, ta khai báo như sau: char listOfPL[][10] = {“Pascal”, “C/C++”, “CSharp”, “Java”, “VB”}; Câu lệnh trên sẽ khai báo mảng listOfPL gồm 5 chuổi. Mảng chuổi trên ñược lưu trữ trong bộ nhớ như sau: P a s c a l ‘\0’ C / C + + ‘\0’ C S h a r p ‘\0’ J a v a ‘\0’ V B ‘\0’ Lưu ý vị trí của các ký tự null Ví dụ: Nhập tên của 5 người dùng mảng char hai chiều, in chúng ra màn hình. #include #include #include Giáo trình PP lập trình TT.Công Nghệ Thông Tin 99/124 void main() { char name[5][20]; for(int i=0 ; i<5 ; i++) { cout << “Input name “ << i+1 <<”: “; cin >> name[i]; } cout << “List of names: “; for(int i=0 ; i<5 ; i++) cout << name[i] << “, “; } 8. Mảng con trỏ ñến các chuổi Ngoài cách dùng mảng ký tự hai chiều ñể lưu trữ mảng các chuổi, ta có thể dùng mảng của các con trỏ. Mỗi con trỏ sẽ chứa ñịa chỉ của chuổi. Cũng ví dụ như phần trên, ta dùng mảng con trỏ char *listOfPL[] = {“Pascal”, “C/C++”, “CSharp”, “Java”, “VB”}; Mảng con trỏ trên có thể ñược lưu trữ trong bộ nhớ như sau: Giá trị 120 145 189 210 272 ðịa chỉ bộ nhớ 65514 65516 65518 65520 65522 listOfPL[0] listOfPL[1] listOfPL[2] listOfPL[3] listOfPL[4] P a s c a l ‘\0’ 120 C / C + + ‘\0’ 145 J a v v ‘\0’ 210 C S h a r p ‘\0’ 189 V B ‘\0’ 172 Giáo trình PP lập trình TT.Công Nghệ Thông Tin 100/124 Ví dụ: Nhập tên của 5 người dùng mảng con trỏ, in chúng ra màn hình. #include #include #include #include void main() { char *name[5]; for(int i=0 ; i<5 ; i++) name[i] = (char *)malloc(20); for(int i=0 ; i<5 ; i++) { cout << "Input name " << i+1 <<": "; gets(name[i]); } cout << "List of names: "; for(int i=0 ; i<5 ; i++) cout << name[i] << ", "; } Giáo trình PP lập trình TT.Công Nghệ Thông Tin 101/124 BÀI TẬP CHƯƠNG 7 1. Viết chương trình nhập một chuỗi ký tự từ bàn phím, xuất ra màn hình mã ASCII của từng ký tự vừa nhập vào (gợi ý mỗi ký tự trên một dòng). 2. Viết chương trình nhập một chuỗi ký tự từ bàn phím, xuất ra màn hình ñảo ngược của chuỗi ñó. Ví dụ ñảo của “abcd egh” là “hge dcba”. 3. Viết chương trình nhập một chuỗi ký tự và kiểm tra xem chuỗi ñó có ñối xứng không. Ví dụ : ABCDEDCBA là ñối xứng. 4. Nhập vào một chuỗi ký tự bất kỳ, hãy ñếm số lần xuất hiện của mỗi loại ký tự. 5. Viết chương trình nhập vào một chuỗi ký tự. a) In ra màn hình từ bên trái nhất và phần còn lại của chuỗi. Ví dụ: “Nguyễn Văn Minh” in ra thành: Nguyễn Văn Minh b) In ra màn hình từ bên phải nhất và phần còn lại của chuỗi. Ví dụ: “Nguyễn Văn Minh” in ra thành: Minh Nguyễn Văn 6. Viết chương trình nhập vào một chuỗi rồi xuất chuỗi ñó ra màn hình dưới dạng mỗi từ một dòng. Ví dụ: “Nguyễn Văn Minh” In ra : Nguyễn Văn Minh 7. Viết chương trình nhập vào một chuỗi, in ra ñảo ngược của chuỗi ñó theo từng từ. Ví dụ : “Nguyễn Văn Minh” ñảo thành “Minh Văn Nguyễn” 8. Viết chương trình ñổi số tiền từ số thành chữ. Giáo trình PP lập trình TT.Công Nghệ Thông Tin 102/124 9. Viết chương trình nhập vào họ và tên của một người, cắt bỏ các khoảng trống không cần thiết (nếu có), tách tên ra khỏi họ và tên, in tên lên màn hình. Chú ý ñến trường hợp cả họ và tên chỉ có một từ. 10. Viết chương trình nhập vào họ và tên của một người, cắt bỏ các khoảng trắng bên phải, trái và các khoảng trắng không có nghĩa trong . In ra màn hình toàn bộ họ tên người ñó dưới dạng chữ hoa, chữ thường. 11. Viết chương trình nhập vào một danh sách họ và tên của n người theo kiểu chữ thường, ñổi các chữ cái ñầu của họ, tên và chữ lót của mỗi người thành chữ hoa. In kết quả lên màn hình. 12. Viết chương trình nhập vào một danh sách họ và tên của n người, tách tên từng người ra khỏi họ và tên rồi sắp xếp danh sách tên theo thứ tự từ ñiển. In danh sách họ và tên sau khi ñã sắp xếp. Giáo trình PP lập trình TT.Công Nghệ Thông Tin 103/124 Chương 8 STRUCTURES – ENUM - typedef Ngôn ngữ C/C++ ñưa ra 5 cách ñể tạo nên một kiểu dữ liệu tùy biến (custom data types). 1. Kiểu cấu trúc (structure): Là một nhóm của các biến ñược ñịnh nghĩa dưới một tên. Kiểu này còn gọi là kiểu dữ liệu phức hợp (compound data types). 2. bit-field: là một biến thể của kiểu structure và cho phép dễ dàng truy cập ñến từng bit riêng rẽ. 3. Union: cho phép cùng một mẫu bộ nhớ ñược ñịnh nghĩa như hai hay nhiều kiểu biến khác nhau. 4. Enumeration: là danh sách của của các tên hằng nguyên. 5. Từ khóa typedef: ñịnh nghĩa một tên khác cho một kiểu dữ liệu ñã có. Trong phần này chỉ thảo luận structures, enumerations, và typedef. 1. Structures Một cấu trúc là một tập các biến ñược tham chiếu thông qua một tên chung. Một khai báo cấu trúc hình thành một khuôn mẫu (template) mà có thể dùng ñể tạo nên các biến cấu trúc có cùng kiểu. Những biến mà tạo nên cấu trúc ñược gọi là các thành viên (members). Nói chung, tất cả các thành viên của một cấu trúc về mặt logic là có liên quan với nhau. Ví dụ sau ñây khai báo một cấu trúc address gồm các thông tin về một ñịa chỉ. Từ khóa struct dùng ñể khai báo một cấu trúc. Xem xét khai báo sau: struct addr { char name[30]; char street[40]; char city[20]; char state[3]; Giáo trình PP lập trình TT.Công Nghệ Thông Tin 104/124 unsigned long int zip; }; Tại thời ñiểm này, ta mới chỉ có khai báo một cấu trúc. ðể khai báo một hoặc nhiều biến có kiểu address, ta dùng tên cấu trúc như bất kỳ kiểu dữ liệu nào. Ví dụ, ñể khai báo 2 biến kiểu address, ta khai báo như sau: address addr1, addr2; Khi một biến cấu trúc ñược khai báo, trình biên dịch tự ñộng cấp phát ñủ bộ nhớ cho tất cả thành viên của cấu trúc. 1.1. Dạng tổng quát của một khai báo cấu trúc struct structureName { type member_1; type member_2; ... type member_n; .. . } varNames; structureName: Tên của cấu trúc type: Kiểu dữ liệu của thành viên tương ứng member_1, member_2, ..., member_n: Tên các biến thành viên của cấu trúc varNames: Tên các biến cấu trúc phân cách nhau bằng dấu phẩy. 1.2. Truy cập các thành viên của biến cấu trúc Toán tử dấu chấm (dot operator) dùng ñể truy cập (access) các thành viên của một biến cấu trúc. Dạng tổng quát ñể truy cập một thành viên của một biến cấu trúc là: structureName.memberName Giáo trình PP lập trình TT.Công Nghệ Thông Tin 105/124 Ví dụ: Xem xét khai báo cấu trúc sau struct coordXY { int x; int y; } diemA, diemB; ðể gán giá trị tọa ñộ cho diemA, ta dùng các lệnh sau: diemA.x = 100; diemA.y = 200; ðể in tọa ñộ ñiểm A, ta dùng lệnh sau: cout < “A(“ << diemA.x << “,” << diemA.y << “)”; 1.3. Lệnh gán cấu trúc Nội dung trong 1 biến cấu trúc có thể gán cho một biến cấu trúc khác có cùng kiểu dùng một câu lệnh gán. Ví dụ, ñể gán nội dung biến cấu trúc pointA cho biến pointB, ta thực hiện lệnh sau: pointB = pointA; Sau câu lệnh này, biến pointB có cùng nội dung như biến pointA. Tuy nhiên, ta cũng có thể sao chép từng thành viên như sau: pointB.x = pointA.x; pointB.y = pointA.y; Ví dụ: Khai báo, nhập và xuất cấu trúc book gồm các thông tin title, author, pages, price. #include #include void main() { struct book { char title[30]; char author[20]; int pages; float price; Giáo trình PP lập trình TT.Công Nghệ Thông Tin 106/124 }; book b; cout << “Input book information:” << endl; cout << “Title: “; gets(b.title); cout << “Author: “; gets(b.author); cout > b.pages; cout > b.price; cout << “Information of this book is:” << endl; cout << “Title: “ << b.title << endl; cout << “Author: “ << b.author << endl; cout << “Pages: “ << b.pages << endl; cout << “Price: “ << b.price << endl; } 1.4. Mảng các cấu trúc ðể khai báo một mảng các cấu trúc, ñầu tiên ta khai báo cấu trúc, sau ñó khai báo một mảng của cấu trúc ñó. Ví dụ, ñể khai báo mảng points có 100 phần tử, ta khai báo như sau: coordXY points[100]; ðể truy cập (access) ñến từng thành viên của từng phần tử của mảng, ta dùng chỉ mục của phần tử và toán tử thành viên (.). Ví dụ, ñể gán tọa ñộ x,y cho phần tử thứ 10, ta dùng các lệnh: points[9].x = 100; points[9].y = 200; 1.5. Truyền các cấu trúc vào hàm a. Truyền các thành viên của biến cấu trúc vào hàm Khi ta truyền một thành viên của một cấu trúc vào một hàm, ta thật sự truyền giá trị của thành viên ñó cho tham số hình thức của hàm Giáo trình PP lập trình TT.Công Nghệ Thông Tin 107/124 (truyền tham trị). Nếu muốn truyền ñịa chỉ của thành viên cho hàm (truyền tham chiếu) ta ñặt phía trước dấu &. Ví dụ: Giả sử ta có hàm int distanceAB(int x1, int y1, int x2, int y2) ñể tính khoảng cách giữa 2 ñiểm. ðể tính khoảng cách giữa 2 ñiểm nào ñó, ta truyền tọa ñộ x,y của 2 ñiểm tương ứng. Ta dùng lệnh sau: length1 = distance(pointA.x, pointA.y, pointB.x, pointB.y); Lệnh trên gọi hàm distance và truyền tọa ñộ x,y của hai ñiểm A,B. Kết quả thực hiện hàm trả về gán cho biến length. ðể truyền ñịa chỉ của thành viên của cấu trúc vào hàm dùng toán tử & ñặt phía trước tên biến cấu trúc chứ không ñặt trước tên của thành viên của biến cấu trúc. Ví dụ: Ta có hàm void move(int *x, int *y, int delta_x, int delta_y); dùng ñể thay ñổi tọa ñộ x,y của một ñiểm; delta_x lượng di chuyển theo chiều ngang; delta_y lượng di chuyển theo chiều dọc. Vậy, ñể thay ñổi tọa ñộ biến cấu trúc pointA, ta dùng lệnh sau: move(&pointA.x, &pointA.y, 10, 20); b. Truyền toàn bộ biến cấu trúc ñến hàm Khi một cấu trúc ñược dùng như một ñối số của một hàm, tòan bộ cấu trúc ñược truyền dùng cách truyền tham trị. Với cách này, hàm không thể làm thay ñổi nội dung của ñối số. Tuy nhiên, nếu muốn hàm có thể làm thay ñổi nội dung của ñối số, ta truyền tham chiếu (thêm dấu & vào trước ñối số). Ví dụ ta có hàm distance2 có khai báo nguyên mẫu như sau: int distance2(point p1, point p2); ðể gọi hàm trên tính khỏang cách của 2 ñiểm pointA và pointB, ta dùng lệnh sau: length2 = distance2(pointA, pointB); Giáo trình PP lập trình TT.Công Nghệ Thông Tin 108/124 Trong trường hợp này, ta sao chép nội dung của 2 biến cấu trúc pointA, pointB vào 2 tham số hình thức p1 và p2 của hàm distance2. 1.6. Con trỏ ñến cấu trúc C/C++ cho phép các con trỏ ñến các cấu trúc như ñến bất kỳ kiểu dữ liệu nào của biến. Khai báo một con trỏ cấu trúc Cú pháp khai báo con trỏ cấu trúc giống như các lọai con trỏ khác. Dạng tổng quát ñể khai báo con trỏ cấu trúc: structureName *structurePointers; 1.7. Sử dụng con trỏ cấu trúc ðể tham chiếu ñến thành viên của một cấu trúc ñược trỏ ñến bởi một con trỏ, ta dùng toán tử -> (toán tử tham chiếu gồm một dấu trừ và một dấu lớn hơn). Xem xét ví dụ sau: points *p; //khai báo con trỏ p có kiểu cấu trúc points p = &pointA; //gán ñịa chỉ của biến cấu trúc pointA cho con trỏ p p->x = 100; //gán giá trị 100 cho thành viên x của biến cấu trúc pointA Lưu ý: ðể truy cập ñến thành viên của một cấu trúc, nếu dùng biến cấu trúc thì dùng toán tử chấm (dot operator), nếu dùng biến con trỏ thì dùng toán tử -> (arrow operator). Khi con trỏ cấu trúc ñược truyền vào một hàm thì hàm có thể thay ñổi nội dung của biến cấu trúc ñó vì cách truyền này là truyền tham chiếu. Giáo trình PP lập trình TT.Công Nghệ Thông Tin 109/124 2. Kiểu liệt kê (Enumerations, enum) Một enum là một tập của các tên hằng nguyên mà xác ñịnh tất cả các giá trị hợp lệ mà một biến của kiểu ñó có thể có. Ví dụ, ta có một enum là danh sách giá trị tiền tệ sau: one$, two$, five$, ten$, twenty$, fifty$, hundred$ Dạng tổng quát ñể khai báo một enum là enum enumName {enumList} enumVars; enum: từ khóa ñể khai báo enum enumName: Tên của enum enumList: Danh sách các tên hằng nguyên phân cách nhau bởi dấu phẩy enumVars: Tên các biến kiểu enum. Ví dụ, khai báo enum trên enum money {one$,two$,five$,ten$,twenty$,fifty$,hundred$} m1, m2; Khai báo hai biến m1, m2 có kiểu money Khảo sát các lệnh sau: m1 = one$; m2 = ten$; if(m1 == m2) cout << "They are same."; if(m1 == one$) cout << "m1 is one dollar."; if(m2 != five$) cout << "m2 is not five-dollar."; ðiểm quan trọng ñể hiểu về enum là mỗi một tên trong danh sách enum tượng trưng cho một giá trị nguyên. Giá trị của tên thứ nhất trong enum là 0, kế tiếp là 1, ... Trong khai báo trên giá trị của các tên lần lượt là: one$ 0 two$ 1 five$ 2 Giáo trình PP lập trình TT.Công Nghệ Thông Tin 110/124 ten$ 3 twenty$ 4 fifty$ 5 hundred$ 6 Ta có thể gán giá trị khác cho mỗi tên hằng nguyên như trong câu lệnh sau: enum money {one$=1, two$=2, five$=5, ten$=10, twenty$=20, fifty$=50, hundred$=100}; Lệnh này sẽ gán mỗi tên hằng nguyên một giá trị ñứng sau dấu bằng. one$ 1 two$ 2 five$ 5 ten$ 10 twenty$ 20 fifty$ 50 hundred$ 100 3. typedef Từ khóa typedef dùng ñể ñịnh nghĩa một tên mới cho một kiểu dữ liệu ñã có. Dạng tổng quát của dùng typedef là typedef existingType newType; existingType: là bất kỳ kiểu dữ liệu nào ñã tồn tại newType: tên mới của kiểu dữ liệu Ví dụ: ñể tạo một tên mới cho kiểu dữ liệu nguyên typedef int int2bytes; //Kiểu int có thêm một tên mới là int2bytes typedef long int4bytes; //Kiểu long có thêm một tên mới là int4bytes Sau khi các lệnh trên thực hiện thì lệnh long n1, n2; //Khai báo 2 biến long tương ñương int4bytes n1, n2; Giáo trình PP lập trình TT.Công Nghệ Thông Tin 111/124 BÀI TẬP CHƯƠNG 8 1. Cho cấu trúc NHANVIEN như sau: - MaNV: kiểu số nguyên có giá trị trong khoảng 065535 - Họtên: kiểu chuỗi. - ðịachỉ: kiểu chuỗi. - CBQL: có giá trị 1 nếu nhân viên này là cán bộ quản lý. Yêu cầu chương trình thực hiện: (a) Viết hàm nhập vào thông tin của một nhân viên. (b) Viết hàm xuất thông tin của một nhân viên. (c) Viết hàm main có yêu cầu nhâp vào n nhân viên với n ñược nhập từ bàn phím. In ra họ tên của các nhân viên là cán bộ quản lý. 2. Cho cấu trúc NHANVIEN như bài 1: Nhập viết hàm Main có yêu cầu nhâp vào n nhân viên với n ñược nhập từ bàn phím. Xóa các nhân viên không là cán bộ quản lý ra khỏi danh sách. 3. Cho cấu trúc NHANVIEN như bài 1 Viết hàm main có yêu cầu nhâp vào n nhân viên với n ñược nhập từ bàn. Nhập thêm thông tin của một nhân viên và nhập một số nguyên k. Thực hiện việc chèn nhân viên mới vào danh sách tại vị trí k. Giáo trình PP lập trình TT.Công Nghệ Thông Tin 112/124 Chương 10 TẬP TIN (Files) C/C++ hổ trợ 2 hệ thống nhập xuất. Một hệ thống thừa kế từ ngôn ngữ C và một hệ thống nhập xuất hướng ñối tượng của C++. Trong chương này, ta chỉ khảo sát hệ thống thứ nhất. 1. Streams và Files Hệ thống nhập xuất của C cung cấp một giao diện (interface) nhất quán cho lập trình viên mà ñộc lập với thiết bị thật sự mà chương trình tương tác. Nghĩa rằng hệ thống nhập xuất của C cung cấp một mức ñộ trừu tượng giữa lập trình viên và thiết bị nhập xuất. Sự trừu tượng này ñược gọi là stream và thiết bị thật sự ñược gọi là file. 2. Streams (dòng nhập xuất) Hệ thống file của C ñược thiết kế ñể làm việc với nhiều loại thiết bị khác nhau như terminals (thiết bị ñầu cuối), các loại ổ ñĩa, băng từ, ... Mặc dầu mỗi thiết bị là rất khác nhau, hệ thống file chuyển ñổi mỗi loại thành một thiết bị logic gọi là stream. Tất cả stream có cùng hành vi. Bởi vì stream thì ñộc lập với thiết bị nên cùng một hoạt ñộng trên stream như viết vào một tập tin trên ñĩa cũng có thể dùng ñể viết ñến loại thiết bị khác như console (màn hình). Có hai loại stream: văn bản (text) và nhị phân (binary). 2.1. Text Streams Một text stream là một chuổi các ký tự. Trong một text stream, một số ký tự có thể bị chuyển ñổi (ñược hiểu như là một ký tự khác) tùy thuộc môi trường. Ví dụ, ký tự newline ('\n') có thể bị ñổi thành cặp ký tự carriage return/linefeed (ký tự xuống dòng và về ñầu dòng). Vì vậy, không có sự quan hệ một-một giữa các ký tự ñược viết (hay ñọc) và những ký tự trên các thiết bị ngoài. Do ñó, bởi vì có khả năng xảy ra sự chuyển ñổi, nên số số ký tự ñược viết (hay ñọc) có thể khác số số ký tự trên thiết bị ngoài. 2.2. Binary Streams Giáo trình PP lập trình TT.Công Nghệ Thông Tin 113/124 Một binary stream là một chuổi bytes mà có sự tương ứng một-một với chuổi bytes trên thiết bị ngoài. Nghĩa là không có sự chuyển ñổi xảy ra. Do ñó, số bytes ñược viết (hay ñọc) thì bằng với số bytes trên thiết bị ngoài. 3. Files Một file có thể là một tập tin trên ñĩa, một terminal, hay máy in. ðể tạo kết nối (associate) giữa một stream với một file ta dùng hoạt ñộng mở (open). Một khi một file ñược mở, thông tin có thể ñược trao ñổi giữa nó và chương trình. Không phải tất cả file ñều có cùng khả năng như nhau. Ví dụ, một tập tin trên ñĩa (file) có thể hỗ trợ truy xuất ngẫu nhiên trong khi ñó máy in (cũng là file) thì không thể. Việc này ñưa ñến một kết luận là: "Tất cả stream là như nhau nhưng file thì không". ðể ngắt kết nối giữa một stream với một file ta dùng hoạt ñộng ñóng (close). Nếu ñóng một file ñang mở cho xuất (output) thì nội dung (nếu có) của stream tương ứng ñược viết ra thiết bị ngoài. Qúa trình này ñược gọi là flushing và ñảm bảo là không có thông tin bị ñể lại trong vùng ñệm (buffer). Tất cả file ñược tự ñộng ñóng khi chương trình mở chúng kết thúc bình thường. Files không ñược ñóng khi chương trình mở chúng bị kết thúc bất thường như bị treo (halt) hay khi chương trình thực hiện hàm abort(). Mỗi stream liên ñới với một file có một cấu trúc kiểu FILE. 3.1. Cơ bản về hệ thống file C/C++ có nhiều hàm liên quan nhau hoạt ñộng trên file. Những hàm này yêu cầu tập tin header stdio.h. Sau ñây là danh sách các hàm: Tên hàm Chức năng fopen( ) Mở một file fclose( ) ðóng một file. putc( ) Viết một ký tự ñến một file. fputc( ) Giống như putc() . getc( ) ðọc một ký tự từ một file. Giáo trình PP lập trình TT.Công Nghệ Thông Tin 114/124 fgetc( ) Giống như getc() . fgets( ) ðọc một chuổi từ một file. fputs( ) Viết một chuổi ñến một file. fseek( ) Tìm một byte trong một file. ftell( ) Trả về vị trí hiện hành của của file indicator. feof( ) Trả về true nếu duyệt ñến cuối file (end-of-file). ferror( ) Trả về true nếu một lỗi xảy ra. rewind( ) ðưa indicator về ñầu. remove( ) Xóa một file. fflush( ) Xả hết vùng ñệm của file. Tập tin header stdio.h cung cấp các nguyên mẫu cho các hàm nhập xuất file. Ngoài ra còn có các macro như NULL, EOF, FOPEN_MAX, SEEK_SET, SEEK_CUR và SEEK_END. Macro NULL ñịnh nghĩa một con trỏ null. Macro EOF ñược ñịnh nghĩa là -1, là giá trị trả về khi hàm ñọc file ñến vị trí cuối của file. FOPEN_MAX ñịnh nghĩa một giá trị nguyên chỉ ra số file có thể mở ñồng thời. Các macro còn lại hoạt ñộng với hàm fseek() ñể thi hành hoạt ñộng truy cập file ngẫu nhiên. 3.2. Con trỏ file (File pointer) Một con trỏ file là một cấu trúc kiểu FILE. Nó trỏ ñến thông tin mà ñịnh nghĩa nhiều thứ về file như tên file, trạng thái, và vị trí hiện hành của file. Con trỏ file ñược dùng bởi stream tương ứng ñể thực hiện các hoạt ñộng nhập xuất trên file. ðể ñọc hay viết file, chương trình phải dùng con trỏ file. ðể khai báo một biến con trỏ file, dùng lệnh: FILE *fp; 3.3. Mở file Hàm fopen() mở một stream ñể dùng và liên kết một file với stream ñó. Hàm trả về một con trỏ file liên ñới với tập tin ñược mở. Hàm fopen() có nguyên mẫu sau: FILE *fopen(const char *filename, const char *mode); filename: Là một hằng chuổi chứa tên (và ñường dẫn) của file mode: Là một hằng chuổi cho biết mở file theo mode nào. Giáo trình PP lập trình TT.Công Nghệ Thông Tin 115/124 Các mode ñể mở tập tin Dưới ñây là danh sách các mode ñể mở một tập tin. "r" Nếu tập tin ñược mở thành công, hàm fopen() nạp nó vào trong bộ nhớ và trả về một con trỏ trỏ ñến ký tự ñầu tiên của tập tin. Nếu không thể mở tập tin, hàm fopen() trả về NULL Các hoạt ñộng có thể làm trên tập tin: ñọc (read) "w" Nếu tập tin tồn tại, nội dung của nó sẽ bị viết ñè. Nếu tập tin không tồn tại, một tập tin mới ñược tạo. Trả về NULL nếu không thể mở tập tin. Các hoạt ñộng có thể làm trên tập tin: viết (write) "a" Nếu tập tin ñược mở thành công, hàm fopen() nạp nó vào trong bộ nhớ và trả về một con trỏ trỏ ñến ký tự cuối cùng của tập tin. Nếu tập tin không tồn tại, một tập tin mới ñược tạo. Trả về NULL nếu không thể mở tập tin. Các hoạt ñộng có thể làm trên tập tin: thêm (append) nội dung mới vào cuối tập tin. "r+" Nếu tập tin ñược mở thành công, hàm fopen() nạp nó vào trong bộ nhớ. Trả về NULL nếu không thể mở tập tin. Các hoạt ñộng có thể làm trên tập tin: viết nội dung mới vào tập tin, ñọc nội dung tập tin, và sửa ñổi nội dung của tập tin. "a+" Nếu tập tin ñược mở thành công, hàm fopen() nạp nó vào trong bộ nhớ và trả về một con trỏ trỏ ñến ký tự ñầu tiên của tập tin. Nếu tập tin không tồn tại, một tập tin mới ñược tạo. Trả về NULL nếu không thể mở tập tin. Các hoạt ñộng có thể làm trên tập tin: ñọc nội dung tập tin, viết thêm nội dung mới vào cuối tập tin. Không thể sửa ñổi nội dung ñang có trong tập tin. Ví dụ sau minh họa mở một file test.txt ñể viết FILE *fp; fp = fopen("test.txt", "w"); ðoạn mã trên là ñúng nhưng thực tế ñoạn mã trên ñược viết như sau: Giáo trình PP lập trình TT.Công Nghệ Thông Tin 116/124 FILE *fp; if((fp = fopen("test.txt","w")) == NULL) { cout << "Cannot open file"; exit(1); } Phương pháp này sẽ dò tìm bất kỳ lỗi nào khi mở file ñể viết như file ñược bảo vệ chống ghi hay ñĩa bị ñầy không thể tạo thêm file. Nói chung, ta phải luôn kiểm tra xem việc mở file có thành công hay không trước khi làm bất kỳ hành ñộng nào trên file. Một file có thể mở ở mode text hay binary. Mặc ñịnh là mở file ở text mode. Nếu muốn mở file ở binary mode thì thêm ký tự b vào chuổi mode ở trên như "wb", "rb", ... Số file ñược phép mở ñồng thời ñược xác ñịnh bởi macro FOPEN_MAX. Giá trị này thường nhỏ nhất là 8. 3.4. ðóng file Hàm fclose() ñóng stream ñược mở bởi hàm fopen(). Khi hàm ñược gọi, nó sẽ viết bất kỳ dữ liệu nào vẫn còn trong buffer ñến file rồi ñóng file. Hàm fclose() có nguyên mẫu sau: int fclose(FILE *fp); fp: là con trỏ file trả về bởi hàm fopen(). Nếu ñóng file thành công, hàm trả về giá trị zero. Nếu một lỗi xảy ra khi ñóng file, hàm trả về EOF. 3.5. Viết một ký tự ñến một file Có hai hàm xuất ký tự ñến file là putc() và fputc(). Hai hàm này là tương ñương nhau. Hàm putc() viết một ký tự ñến một file ñã ñược mở bởi hàm fopen(). Nguyên mẫu của hàm như sau: int putc(int ch, FILE *fp); fp là con trỏ file trả về bởi hàm fopen() và ch là ký tự ñược viết ñến file. Nếu hoạt ñộng putc() thành công, nó trả về ký tự ñược viết vào file. Ngược lại, nó trả về EOF. Giáo trình PP lập trình TT.Công Nghệ Thông Tin 117/124 3.6. ðọc một ký tự từ một file Có hai hàm tương ñương ñể ñọc một ký tự từ file là getc() và fgetc(). Hàm getc() ñọc mỗi lần một ký tự từ file ñược mở bởi hàm fopen() ở chế ñộ ñọc (read). Nguyên mẫu của fopen là: int getc(FILE *fp); fp là con trỏ file kiểu FILE trả về bởi hàm fopen(). Hàm getc() trả về một số nguyên là giá trị của ký tự ñược ñọc. Hàm getc() trả về EOF nếu một lỗi xảy ra. 3.7. Ví dụ minh họa fopen(), getc(), putc(), và fclose() Ví dụ 1: Minh họa việc ñọc từ bàn phím và ghi chúng vào một tập tin cho ñến khi người dùng nhấn nhập ký tự $. #include #include void main() { FILE *fp; char ch; if((fp=fopen(“test.txt”, "w")==NULL) { cout << "Cannot open file.\n"; exit(1); } do { ch = getchar(); putc(ch, fp); }while (ch != '$'); fclose(fp); } Ví dụ 2: Minh họa việc ñọc từ một file văn bản và xuất chúng ra màn hình. #include #include void main() { FILE *fp; Giáo trình PP lập trình TT.Công Nghệ Thông Tin 118/124 char ch; if((fp=”test.txt”, "r")==NULL) { cout << "Cannot open file.\n"; exit(1); } ch = getc(fp); // read one character while (ch!=EOF) { putchar(ch); // print on screen ch = getc(fp); } fclose(fp); } 3.8. ðọc và viết chuổi trên file C/C++ hỗ trợ hai hàm fgets() và fputs() ñể ñọc và viết chuổi ký tự trên file. Những hàm này tương tự như getc() và putc() nhưng thay vì ñọc hay viết từng ký tự, chúng ñọc hay viết một chuổi. Nguyên mẫu của các hàm trên như sau: int fputs(const char *str, FILE *fp); char *fgets(char *str, int length, FILE *fp); Hàm fputs() viết một chuổi trỏ ñến bởi str ñến stream trỏ ñến bởi con trỏ file fp. Hàm trả về EOF nếu một lỗi xảy ra. Hàm fgets() ñọc một chuổi từ stream tương ứng cho ñến khi gặp ký tự newline hay ñã ñọc ñược length-1 ký tự. Hàm trả về str nếu ñọc thành công và một con trỏ null nếu không. Chương trình sau minh họa hàm fputs(). Nó ñọc các chuổi từ bàn phím và viết chúng ñến file tên teststr.txt. ðể kết thúc chương trình, nhập một dòng trống #include #include #include void main(void) { char str[80]; FILE *fp; Giáo trình PP lập trình TT.Công Nghệ Thông Tin 119/124 if((fp = fopen("teststr.txt", "w"))==NULL) { cout << "Cannot open file.\n"; exit(1); } do { cout << "Enter a string (CR to quit):\n"; gets(str); strcat(str, "\n"); /* add a newline */ fputs(str, fp); } while(*str!='\n'); } 3.9. Hàm fread() và fwrite() ðể ñọc và viết các kiểu dữ liệu có kích thước lớn hơn 1 byte, C/C++ cung cấp hai hàm fread() và fwrite(). Những hàm này cho phép ñọc và viết một khối của bất kỳ dữ liệu nào. Nguyên mẫu của các hàm này như sau: size_t fread(void *buffer, size_t numbytes, size_t count, FILE *fp); size_t fwrite(const void *buffer, size_t numbytes, size_t count, FILE *fp); ðối với fread(), buffer là một con trỏ ñến một vùng bộ nhớ mà sẽ nhận dữ liệu ñọc từ file. ðối với fwrite(), buffer là một con trỏ ñến thông tin mà sẽ viết ñến file. Giá trị của count cho biết bao nhiêu phần tử ñược ñọc hay viết với mỗi phần tử có ñộ dài num_bytes. fp là con trỏ ñến file ñã ñược mở bởi fopen(). /* Write some non-character data to a disk file and read it back. */ #include #include void main(void) { FILE *fp; double d = 12.23; int i = 101; long l = 123023L; if((fp=fopen("testNchar.txt", "wb+"))==NULL) { cout << "Cannot open file.\n"; exit(1); } Giáo trình PP lập trình TT.Công Nghệ Thông Tin 120/124 fwrite(&d, sizeof(double), 1, fp); fwrite(&i, sizeof(int), 1, fp); fwrite(&l, sizeof(long), 1, fp); rewind(fp); fread(&d, sizeof(double), 1, fp); fread(&i, sizeof(int), 1, fp); fread(&l, sizeof(long), 1, fp); cout << “double= ” << d << endl; cout << “integer= ” << i << endl; cout << “long= ” << l; fclose(fp); } Một trong những ứng dụng hữu dụng của fread() và fwrite() là ñọc và viết kiểu dữ liệu người dùng ñịnh nghĩa như các cấu trúc. Ví dụ 1: Nhập các mẫu tin nhân viên vào tập tin employee.dat #include void main() { FILE *fp; struct employee { char name[30]; int age; }; struct employee e; char more = 'Y'; fp = fopen("employee.dat", "wb"); if(fp == NULL) { cout << "Cannot open file"); exit(1); } while(more == 'Y'|| more == ‘y’) { cout << "\nEnter name and age: "; cin >> e.name >> e.age; fwrite(&e, sizeof(e), 1, fp); cout << "Input more (y/n)?: "; fflush(stdin); more = getche(); } fclose(fp); Giáo trình PP lập trình TT.Công Nghệ Thông Tin 121/124 } Ví dụ 2: Xuất các mẫu tin nhân viên có trong tập tin employee.dat #include void main() { FILE *fp; struct employee { char name[30]; int age; }; struct employee e; fp = fopen("employee.dat", "rb"); if(fp == NULL) { cout << "Cannot open file"); exit(1); } while(fread(&e, sizeof(e), 1, fp) == 1) { cout << "\nName= " << e.name; cout << "\tAge= " << e.age; } fclose(fp); } Hàm rewind() Hàm rewind() di chuyển indicator ñến ñiểm bắt ñầu của file. Hàm có prototype như sau: void rewind(FILE *fp); Hàm ferror() Hàm ferror() cho biết một hoạt ñộng trên file ñã gây ra lỗi. Nguyên mẫu của hàm như sau: int ferror(FILE *fp); Hàm trả về true nếu một lỗi ñã xảy ra với hoạt ñộng trên file trước khi gọi hàm ferror(), ngược lại trả về false. Xóa file Hàm remove() dùng ñể xóa tập tin. Nguyên mẫu như sau: Giáo trình PP lập trình TT.Công Nghệ Thông Tin 122/124 int remove(const char *filename); Hàm trả về zero nếu xóa thành công, ngược lại trả về nonzero Flushing a stream Hàm fflush() dùng ñể xuất tất cả nội dung còn lại trong buffer của stream. Hàm có nguyên mẫu sau: int fflush(FILE *fp); Hàm viết nội dung còn trong buffer ñến file liên ñới với fp. Nếu ta gọi hàm fflush() không có ñối số thì tất cả flush tất cả file ñang mở. Hàm trả về 0 nếu thành công, ngược lại trả về EOF Truy xuất file ngẫu nhiên ðể ñọc hay viết từ hay ñến một vị trí bất kỳ trong file ta cần sự giúp ñỡ của hàm fseek(). Hàm này dùng ñể di chuyển chỉ báo file. Hàm có nguyên mẫu sau: int fseek(FILE *fp, long numbytes, int origin); fp là con trỏ trả về bởi hàm fopen() origin là một trong các giá trị sau: SEEK_SET (từ ñầu file), SEEK_CUR (từ vị trí hiện hành), và SEEK_END (từ cuối file). numbytes: số byte mà indicator di chuyển tùy thuộc origin cung cấp. Các stream chuẩn Khi một chương trình thực thi, ba stream ñược mở tự ñộng. ðó là stdin (standard input), stdout (standard output), và stderr (standard error). stdin dùng ñể ñọc từ bàn phím, stdout và stderr dùng ñể viết ñến màn hình. Bởi vì standard streams là các con trỏ file nên có thể dùng các hàm nhập xuất trên chúng. Giáo trình PP lập trình TT.Công Nghệ Thông Tin 123/124 BÀI TẬP CHƯƠNG 10 Viết hàm tạo một tập chứa 10000 số nguyên ngẫu nhiên khác nhau ñôi một trong phạm vi từ 1 ñến 32767 ñặt tên là “SONGUYEN.INP” 1) Viết hàm ñọc tập “SONGUYEN.INP”, sau ñó sắp xếp theo thứ tự tăng dần và lưu kết quả vào tập “SONGUYEN.OUT” 2) Viết hàm tạo tập văn bản có tên là “INPUT.TXT” có cấu trúc như sau - Dòng ñầu tiên ghi N (N là số nguyên dương nhập từ bàn phím) - Trong các dòng tiếp theo ghi N số nguyên ngẫu nhiên trong phạm vi từ 0 ñến 100, mỗi dòng 10 số (các số cách nhau ít nhất một dấu cách) - Hãy ñọc dữ liệu của file “INPUT.TXT” và lưu vào mảng một chiều A Hãy thực hiện các công việc sau : a) Tìm giá trị lớn lớn nhất của mảng A b) ðếm số lượng số chẳn, số lượng số lẽ của mảng A c) Hãy sắp xếp các phần tử theo thứ tự tăng dần Hãy ghi các kết quả trên vào filetext có tên là “OUTPUT.TXT” theo mẫu sau: 4) Viết hàm tạo tập văn bản có tên là “INPUT.TXT” có cấu trúc như sau - Dòng ñầu tiên ghi hai số M và N (M,N là các số nguyên dương nhập từ bàn phím) OUTPUT.TXT Cau a: 99 cau b: 9 9 cau c: 1 4 4 19 20 29 34 38 39 40 43 58 65 78 87 89 98 99 INPUT.TXT 18 87 39 78 19 89 4 40 98 29 65 20 43 1 99 38 34 58 4 Giáo trình PP lập trình TT.Công Nghệ Thông Tin 124/124 - Trong M dòng tiếp theo mỗi dòng ghi N số nguyên ngẫu nhiên trong phạm vi từ 0 ñến 50 (các số cách nhau ít nhất một dấu cách) Hãy ñọc dữ liệu của tập “INPUT.TXT” và lưu vào mảng hai chiều A. Hãy thực hiện các công việc sau: a) Tìm giá trị lớn lớn nhất của mảng A b) ðếm số lượng số chẳn, số lượng số lẽ của mảng A c) Hãy tính tổng các phần tử trên mỗi dòng của mảng A Hãy ghi các kết quả trên vào tập tin văn bản có tên là “OUTPUT.TXT” theo như mẫu trong ví dụ sau INPUT.TXT OUTPUT.TXT 5) Viết hàm ñọc một file “SONGUYEN.INP” ñược tạo ở bài tập 1. Sau ñó ghi các số chẵn vào file SOCHAN.OUT và các số lẻ vào file SOLE.OUT. 6) Viết một hàm gộp nội dung của hai tập tin có sẵn vào một tập tin thứ ba. Tên các tập tin ñược nhập vào từ bàn phím. 7) Viết một hàm tìm kiếm trên tập nhị phân có cấu trúc employee gồm tên và tuổi. Hiển thị kết quả là tất cả các thông tin về các nhân viên ñược tìm thấy khi cho biết : a) Tên nhân viên b) Có tuổi cao hơn một giá trị X nào ñó. 6 6 41 17 33 23 12 1 44 24 23 49 5 24 33 20 17 25 33 19 0 48 45 48 41 32 10 24 36 19 19 24 30 4 23 26 27 36 Cau a: 49 Cau b: 17 19 Cau c: 127 169 147 214 132 146

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

  • pdfgiao_trinh_pplt_3134_9607.pdf
Tài liệu liên quan