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