CHƯƠNG 3 : CẤU TRÚC RẼ NHÁNH CÓ ĐIỀU KIỆN
5.2. THAM SỐ DẠNG THAM BIẾN VÀ THAM TRỊ
Mục tiêu:
- Trình bày được cách truyền tham số, tham biến, tham trị ;
- Thực hiện các thao tác an tồn với máy tính.
5.2.1. Tham số dạng tham trị
Mặc nhiên, việc truyền tham số cho hàm trong C là truyền theo giá trị; nghĩa là các giá trị thực (tham số thực) không bị thay đổi giá trị khi truyền cho các tham số hình thức
Ví dụ 1: Giả sử ta muốn in ra nhiều dịng, mỗi dịng 50 ký tự nào đó. Để
đơn giản ta viết một hàm, nhiệm vụ của hàm này là in ra trên một dòng 50 ký tự nào đó. Hàm này có tên là InKT.
#include <stdio.h> #include <conio.h> void InKT(char ch) { int i; for(i=1;i<=50;i++) printf(“%c”,ch); printf(“\n”); } int main() { char c = ‘A’;
InKT(‘*’); /* In ra 50 dau * */ InKT(‘+’); InKT(c);
}
Chú ý:
- Trong hàm InKT ở trên, biến ch gọi là tham số hình thức được truyền bằng giá trị (gọi là tham trị của hàm). Các tham trị của hàm coi như là một biến cục bộ trong hàm và chúng được sử dụng như là dữ liệu đầu vào của hàm.
- Khi chương trình con được gọi để thi hành, tham trị được cấp ô nhớ và nhận giá trị là bản sao giá trị của tham số thực. Do đó, mặc dù tham trị cũng là biến, nhưng việc thay đổi giá trị của chúng khơng có ý nghĩa gì đối với bên ngồi hàm, khơng ảnh hưởng đến chương trình chính, nghĩa là khơng làm ảnh hưởng đến tham số thực tương ứng.
Ví dụ 2: Ta xét chương trình sau đây:
#include <stdio.h> #include <conio.h>
int hoanvi(int a, int b)
{
int t;
t=a; /*Đoạn này hoán vị giá trị của 2 biến a, b*/ a=b;
b=t;
printf("\Ben trong ham a=%d , b=%d",a,b);
return 0; } void main() { int a, b; clrscr();
printf("\n Nhap vao 2 so nguyen a, b:"); scanf("%d%d",&a,&b);
printf("\n Truoc khi goi ham hoan vi a=%d, b=%d", a, b);
hoanvi(a,b);
printf("\n Sau khi goi ham hoan vi a=%d ,b=%d",a,b); getch();
}
Kết quả thực hiện chương trình:
Giải thích:
- Trước khi gọi hàm hốn vị thì a=6, b=5 - Bên trong hàm hốn vị a=5, b=6
- Khi ra khỏi hàm hốn vị thì a=6, b=5
5.2.2. Bài tập thực hành về tham trị
1. Viết chương trình giải phương trình bậc nhất ax+b=0. Hướng dẫn:
- Xây dựng hàm giải phương trình bậc nhất: void giaiptb1(int a, int b) - Truyền tham trị cho hàm giaiptb1(int a, int b)
- Gọi hàm giaiptb1(int a, int b) vừa xây dựng thực hiện trong chương trình chính
2. Viết chương trình tính tổng các số nguyên chẵn từ 1 đến n. Hướng dẫn:
- Xây dựng hàm tính tổng các số nguyên chẵn từ 1 đến n: int tinhtongchan (int n)
- Truyền tham trị cho hàm int tinhtongchan (int n)
- Gọi hàm tinhtongchan (int n) vừa xây dựng thực hiện trong chương trình chính
5.2.3. Tham số dạng tham biến
Trong đoạn chương trình trên, nếu ta muốn sau khi kết thúc chương trình con giá trị của a, b thay đổi thì ta phải đặt tham số hình thức là các con trỏ, còn tham số thực tế là địa chỉ của các biến.
Lúc này mọi sự thay đổi trên vùng nhớ được quản lý bởi con trỏ là các tham số hình thức của hàm thì sẽ ảnh hưởng đến vùng nhớ đang được quản lý bởi tham số thực tế tương ứng (cần để ý rằng vùng nhớ này chính là các biến ta cần thay đổi giá trị). Người ta thường áp dụng cách này đối với các dữ liệu đầu ra của hàm.
Ví dụ: Xét chương trình sau đây:
#include <stdio.h> #include <conio.h>
long hoanvi(long *a, long *b)
/* Khai báo tham số hình thức *a, *b là các con trỏ kiểu long */
{
long t;
t=*a; /*gán nội dung của x cho t*/
*a=*b; /*Gán nội dung của b cho a*/
*b=t; /*Gán nội dung của t cho b*/
printf("\n Ben trong ham a=%ld , b=%ld",*a,*b);
/*In ra nội dung của a, b*/ return 0; } void main() { long a, b; clrscr();
printf("\n Nhap vao 2 so nguyen a, b:"); scanf("%ld%ld",&a,&b);
printf("\n Truoc khi goi ham hoan vi a=%ld, b=%ld", a,b);
hoanvi(&a,&b); /* Phải là địa chỉ của a và b */ printf("\n Sau khi goi ham hoan vi a=%ld, b=%ld", a,b);
getch(); }
Kết quả thực hiện chương trình sau.
Giải thích:
- Nhập vào 2 số 5, 6 (a=5, b=6)
- Trước khi gọi hàm hoanvi thì a=5, b=6
- Trong hàm hoanvi (khi đã hốn vị) thì a=6, b=5 - Khi ra khỏi hàm hốn vị thì a=6, b=6
5.2.4. Bài tập thực hành
1. Viết chương trình giải phương trình bậc nhất ax+b=0 (a#0). Hướng dẫn:
- Xây dựng hàm giải phương trình bậc nhất: float giaiptb1(int a, int b, float *x)
- Truyền tham trị cho hàm giaiptb1(int a, int b, float *x)
- Gọi hàm giaiptb1(int a, int b, float *x) vừa xây dựng thực hiện trong chương trình chính
2. Viết chương trình tính tổng các số ngun chẵn từ 1 đến n. Hướng dẫn:
- Xây dựng hàm tính tổng các số nguyên chẵn từ 1 đến n: int tinhtongchan (int n, int *S)
- Truyền tham biến cho hàm int tinhtongchan (int n, int *S)
- Gọi hàm int tinhtongchan (int n, int *S) vừa xây dựng thực hiện trong chương trình chính
5.3. SỬ DỤNG BIẾN TOÀN CỤC
Mục tiêu: Sử dụng biến cục bộ, toàn cục trong hàm
5.3.1. Sử dụng biến toàn cục 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 #include <stdio.h> #include <conio.h>
// khai bao prototype void oddeven( ); void negative();
//khai bao bien toan cuc int inum; void main(void)
{
printf("Nhap vao 1 so nguyen : "); scanf("%d", &inum);
oddeven(); negative(); getch(); }
// ham kiem tra chan le void oddeven() {
if (inum % 2)
printf("%d la so le.\n", inum); else
printf("%d la so chan.\n", inum); }
//ham kiem tra so am void negative() {
if (inum < 0)
printf("%d la so am.\n", inum); else
printf("%d la so duong.\n", inum); }
Kết quả in ra màn hình
3 la so le. 3 la so duong.
Giải thích chương trình
Chương trình trên gồm 2 hàm oddeven và negative, 2 hàm này bạn thấy khơng có tham số để truyền biến inum vào xử lý nhưng vẫn cho kết quả đúng. Do chương trình sử dụng biến inum tồn cục (dịng.9) nên biến này có ảnh hưởng đến tồn bộ chương trình mỗi khi gọi và sử dụng nó. Xét tình huống sau: Giả sử trong hàm negative ta khai báo biến inum có kiểu int như sau:
void negative() {
int inum; ….
}
Khi đó chương trình sẽ cho kết quả sai! Do các câu lệnh trong hàm negative sử dụng biến inum sẽ sử dụng biến inum khai báo trong hàm negative và lúc này biến inum tồn cục khơng có tác dụng đối với các câu lệnh trong hàm này. Biến inum khai báo trong hàm negative chỉ có ảnh hưởng trong phạm vi hàm và chu trình sống của nó bắt đầu từ lúc gọi hàm đến khi thực hiện xong.
Cẩn thận khi đặt tên biến, xác định rõ phạm vi của biến khi sử dụng để có thể dễ dàng kiểm sốt chương trình.
Ví dụ 6:
#include <stdio.h> #include <conio.h> #define PI 3.14
// khai bao prototype float area();
//khai bao bien toan cuc float frad;
void main(void) {
printf("Nhap vao ban kinh hinh cau : "); scanf("%f", &frad);
printf("Dien tich hinh cau: %10.3f.\n", area());
getch(); }
// ham tinh dien tich hinh cau float area()
{
return (4*PI*frad*frad);
}
Kết quả in ra màn hình
Nhap vao ban kinh hinh cau: 3.2 Dien tich hinh cau: 128.614
5.3.2. Bài tập thực hành
Bài 1: Viết hàm kiểm tra số nguyên n có phải là số ngun tố hay khơng? Bài 2: Viết hàm kiểm tra số nguyên n có phải là số chính phương hay khơng? (25=52, 16=42, 9=32; các số 25, 16, 9 là những số chính phương)
Bài 3: Viết hàm tính n! (n là số nguyên nhập vào từ bàn phím)
Bài 4: Viết thủ tục tìm UCLN và BCNN của hai số nhập vào từ bàn phím Bài 5: Viết hàm, thủ tục tìm số lớn nhất trong hai số. Áp dụng hàm này vào việc tìm số lớn nhất trong ba số nhập vào từ bàn phím
Hướng dẫn
Bài 1:
Thuật tốn:
Viết hàm (function) ktra_ngto(int x): kiểm tra x có phải là số nguyên tố
hay không
Khai báo biến i, ktra Cho i chạy từ 2 đến x Nếu x mod i = 0 thì thốt Nếu x=i thì ktra=1
Ngược lại, ktra=0
Hàm ktra_ngto trả về giá trị ktra Chương trình chính:
Khai báo biến n, kq Nhập số n
Gọi hàm: kq = ktra_ngto(n) Nếu kq = = 1 thì n là số nguyên tố Ngược lại, n không phải là số nguyên tố Bài 2:
Thuật toán:
Viết hàm (function) ktra_chinhphuong(int x): kiểm tra x có phải là số
chính phương hay khơng Khai báo biến i, ktra
Cho i chạy từ 1 đến x (nếu dùng sqrt(x) thì khơng cần lệnh thốt) Nếu i*i = x thì
ktra = 1 Thốt
Nếu ktra= =1 thì x là số chính phương Ngược lại, x khơng phải là số chính phương Chương trình chính:
Khai báo biến n, kq Nhập số n
Gọi hàm: kq = ktra_chinhphuong(n) Bài 3:
Thuật toán:
Viết hàm (function) Giaithua(int n):giai thừa của n Khai báo biến I, GT
Cho i chạy từ 1 đến n GT=1
GT=GT*i
Trả về kết quả tìm được cho hàm Giaithua Chương trình chính:
Khai báo biến n Nhập số n
Gọi hàm: kq = Giaithua(n) Bài 4:
Thuật toán:
Viết hàm (function) USCLN(a:integer, b:integer): integer :tìm ước số chung lớn nhất của hai số a và b
Lấy trị tuyệt đối hai số a và b Chừng nào (a<>0 và b<>0) làm Nếu a>b thì a=a-b
Ngược lại, b=b-a
Nếu a = 0 thì b là USCLN Ngược lại, a là USCLN
Viết hàm (function) BSCNN(a:integer, b:integer):integer tìm bội số chung nhỏ nhất của hai số a và b
BSCNN= (a*b)/USCLN(a,b) Chương trình chính:
Khai báo biến a, b, US, BS Nhập hai số a,b Gọi hàm: US = USCLN(a, b) BS = BSCNN(a, b) Xuất BS, US ra màn hình Bài 5: Thuật tốn:
Viết hàm (function) solonnhat(a:integer, b:integer):integer tìm số lớn nhất trong hai số a và b
Khai báo biến max Gán max = a
Nếu max<b thì max=b
Hàm solonnhat(a,b) trả về giá trị max Chương trình chính:
Khai báo biến a, b, c, max Nhập hai số a, b, c Gọi hàm: max = solonnhat(a, b) max = solonnhat(max, c) Xuất max ra màn hình 5.4. Dùng dẫn hướng #define Mục tiêu: Sử dụng tiền xử lý #define.
Sau đây là một vài ví dụ dùng dẫn hướng #define để định nghĩa hàm đơn giản
#define AREA_CIRCLE (frad) (4*PI*frad*frad) //tinh dien tich hinh cau
#define SUM (x, y) (x + y) //cong 2 so
#define SQR (x) (x*x) //tinh x binh phuong
#define MAX(x, y) (x > y) ? x : y //tim so lon nhat giua x va y #define ERROR (s) printf("%s.\n", s) //in thong bao voi chuoi s
Ví
d ụ 7 : Trong ví dụ 6 xóa từ dịng 20 đến dịng 24, xóa dịng 6, 7;
thêm dòng AREA_CIRCLE (frad) (4*PI+frad*frad) vào sau dòng 5.
Sửa dòng printf("Dien tich hinh cau: %10.3f.\n", area()); thành printf("Dien tich hinh cau: %10.3f.\n", AREA_CIRCLE(frad));
Chạy lại chương trình, quan sát và nhận xét kết quả. Ví dụ 8: 1 2 3 4 5 6 7 8 9 #include <stdio.h> #include <conio.h> #define MAX(x, y) (x > y) ? x : y void main(void) { float a = 4.5, b = 6.1;
printf("So lon nhat la: %5.2f.\n", MAX(a, b)); getch();
}
Kết quả in ra màn hình So lon nhat la: 6.10
Thêm vào dịng 8 giá trị c = 10
Sửa lại dòng 9: MAX(a, b) thành MAX(MAX(a, b), c) Chạy lại chương trình, quan sát và nhận xét kết quả
- 72 -
CHƯƠNG 6: MẢNG VÀ CHUỖI Mã chương/ bài:MH18-06
Ý nghĩa:
Trong C, mảng được dùng để biểu thị một cấu trúc của một dãy nhiều giá trị có cùng một kiểu được xếp thứ tự.
Mục tiêu:
- Trình bày được ý nghĩa, cách khai báo mảng, chuỗi; - Nhập xuất mảng, chuỗi;
- Khởi tạo mảng chuỗi;
- Trình bày một số kỹ thuật thao tác trên mảng, chuỗi; - Vận dụng được mảng làm tham số cho hàm;
- Giải một số bài toán sử dụng kiểu mảng, chuỗi. - Thực hiện các thao tác an toàn với máy tính.
6.1. GIỚI THIỆU KIỂU DỮ LIỆU “KIỂU MẢNG” TRONG C
Mục tiêu: Trình bày được ý nghĩa, cách khai báo mảng
Mảng là một tập hợp các phần tử cố định có cùng một kiểu, gọi là kiểu phần tử. Kiểu phần tử có thể là có các kiểu bất kỳ: ký tự, số, chuỗi ký tự…; cũng có khi ta sử dụng kiểu mảng để làm kiểu phần tử cho một mảng (trong trường hợp này ta gọi là mảng của mảng hay mảng nhiều chiều).
Ta có thể chia mảng làm hai loại: Mảng một chiều và mảng nhiều chiều. Mảng là kiểu dữ liệu được sử dụng rất thường xuyên. Chẳng hạn người ta cần quản lý một danh sách họ và tên của khoảng 100 sinh viên trong một lớp. Nhận thấy rằng mỗi họ và tên để lưu trữ ta cần một biến kiểu chuỗi, như vậy 100 họ và tên thì cần khai báo 100 biến kiểu chuỗi. Nếu khai báo như thế này thì đoạn khai báo cũng như các thao tác trên các họ tên sẽ rất dài dịng và rắc rối. Vì thế, kiểu dữ liệu mảng giúp ích ta trong trường hợp này; chỉ cần khai báo một biến, biến này có thể coi như là tương đương với 100 biến chuỗi ký
- 73 -
tự; đó là một mảng mà các phần tử của nó là chuỗi ký tự. Hay như để lưu trữ các từ khóa của ngơn ngữ lập trình C, ta cũng dùng đến một mảng để lưu trữ chúng.
6.2. MẢNG MỘT CHIỀU
Mục tiêu:
- Trình bày được ý nghĩa, cách khai báo mảng một chiều
- Nhập xuất mảng một chiều
- Khởi tạo mảng một chiều
- Trình bày một số kỹ thuật thao tác trên mảng một chiều;
- Vận dụng được mảng làm tham số cho hàm
- Giải một số bài toán sử dụng kiểu mảng một chiều
- Thực hiện các thao tác an tồn với máy tính.
Nếu xét dưới góc độ tốn học, mảng một chiều giống như một vector. Mỗi phần tử của mảng một chiều có giá trị khơng phải là một mảng khác.
6.2.1. Khai báo
6.2.1.1. Khai báo mảng với số phần tử xác định (khai báo tường minh)
Cú pháp: <Kiểu> <Tên mảng ><[số phần tử]> Ý nghĩa:
- Tên mảng: đây là một cái tên đặt đúng theo quy tắc đặt tên của danh biểu. Tên này cũng mang ý nghĩa là tên biến mảng.
- Số phần tử: là một hằng số nguyên, cho biết số lượng phần tử tối đa trong mảng là bao nhiêu (hay nói khác đi kích thước của mảng là gì). - Kiểu: mỗi phần tử của mảng có dữ liệu thuộc kiểu gì.
- Ở đây, ta khai báo một biến mảng gồm có số phần tử phần tử, phần tử thứ nhất là tên mảng [0], phần tử cuối cùng là tên mảng[số phần tử -1]
Ví dụ:
- 74 -
/* Khai báo biến mảng tên a, phần tử thứ nhất là a[0], phần tử cuối cùng là a[9].*/
Ta có thể coi mảng a là một dãy liên tiếp các phần tử trong bộ nhớ như sau:
Vị trí 0 1 2 3 4 5 6 7 8 9 Tên phần tử
Hình 1: Hình ảnh mảng a trong bộ nhớ
6.2.1.2. Khai báo mảng với số phần tử không xác định (khai báo không tường minh)
Cú pháp: <Kiểu> <Tên mảng> <[]>
Khi khai báo, không cho biết rõ số phần tử của mảng, kiểu khai báo này thường được áp dụng trong các trường hợp: vừa khai báo vừa gán giá trị, khai báo mảng là tham số hình thức của hàm.
Vừa khai báo vừa gán giá trị
Cú pháp:
<Kiểu> <Tên mảng> []= {Các giá trị cách nhau bởi dấu phẩy}
Nếu vừa khai báo vừa gán giá trị thì mặc nhiên C sẽ hiểu số phần tử của mảng là số giá trị mà chúng ta gán cho mảng trong cặp dấu {}. Chúng ta có thể sử dụng hàm sizeof() để lấy số phần tử của mảng như sau:
Số phần tử=sizeof(tên mảng)/ sizeof(kiểu) Khai báo mảng là tham số hình thức của hàm
Trong trường hợp này ta khơng cần chỉ định số phần tử của mảng là bao nhiêu.
6.2.2. Truy xuất từng phần tử của mảng
Mỗi phần tử của mảng được truy xuất thông qua Tên biến mảng theo sau là chỉ số nằm trong cặp dấu ngoặc vuông [ ]. Chẳng hạn a[0] là phần tử đầu tiên của mảng a được khai báo ở trên. Chỉ số của phần tử mảng là một biểu thức mà giá trị là kiểu số nguyên.
Với cách truy xuất theo kiểu này, Tên biến mảng[Chỉ số] có thể coi như là một biến có kiểu dữ liệu là kiểu được chỉ ra trong khai báo biến mảng.
Ví dụ 1:
int a[10];
Trong khai báo này, việc truy xuất các phần tử được chỉ ra trong hình 1. Chẳng hạn phần tử thứ 2 (có vị trí 1) là a[1]…
- 75 -
Ví dụ 2: Vừa khai báo vừa gán trị cho 1 mảng 1 chiều các số nguyên.
In mảng số nguyên này lên màn hình.
Giả sử ta đã biết số phần tử của mảng là n; việc hiển thị 1 giá trị số nguyên lên màn hình ta cần sử dụng hàm printf() với định dạng %d, tổng quát hóa lên nếu muốn hiển thị lên màn hình giá trị của n số nguyên, ta cần gọi hàm printf()