. 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.
124 trang |
Chia sẻ: huyhoang44 | Lượt xem: 757 | Lượt tải: 0
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:
- giao_trinh_pplt_3134_9607.pdf