CH¯¡NG 3 HÀM
3.2. Hàm tron gC
Xây dựng mát hàm bao gồm: khai báo kiáu hàm, đặt tên hàm, khai báo các đối và đ°a ra câu lánh cÅn thiết đá thực hián yêu cÅu đß ra cho hàm. Mát hàm đ°ÿc viết theo mẫu sau:
type_f tên hàm (khai báo các đối) {
Khai báo các biến cāc bá
Các câu lánh của hàm
[return[biáu thức]; ] }
Dịng tiêu đß:
Trong dịng đÅu tiên của hàm chứa các thơng tin vß: kiáu hàm (type_f), tên hàm, kiáu và tên mßi đối.
Ví dā: float max3s(float a, float b, float c)
Khai báo các đối có d¿ng:
Kiểu đối 1 tên đối 1, kiểu đối 2 tên đối 2, ..., kiểu đối n tên đối n
Thân hàm:
Sau dịng tiêu đß là thân hàm. Thân hàm là nái dung chính của hàm bắt đÅu và kết thúc bằng các dÃu { }. Trong thân hàm chứa các câu lánh cÅn thiết đá thực hián mát yêu cÅu nào đó đã đß ra cho hàm.
Thân hàm có thá sÿ dāng mát câu lánh return, có thá dùng nhißu câu lánh return ỗ cỏc chị khỏc nhau, và cũng có thá khơng sÿ dāng câu lánh này.
D¿ng tổng quát của nó là: return [biáu thức];
Giá trß của biáu thức trong câu lánh return sẽ đ°ÿc gán cho hàm.
Ví dā: Xét bài tốn: Tìm giá trß lãn nhÃt của ba số mà giá trß của chúng đ°ÿc đ°a vào
bàn phím. Xây dựng ch°¢ng trình và tổ chức thành hai hàm: Hàm main() và hàm max3s. Nhiám vā của hàm max3s là tính giá trß lãn nhÃt của ba số đác vào, giÁ sÿ là a, b, c. Nhiám vā của hàm main() là đác ba giá trß vào từ bàn phím, rồi dùng hàm max3s đá tính nh° trên, rồi đ°a kết quÁ ra màn hình.
Ch°¢ng trình đ°ÿc viết nh° sau:
float max3s(float a, float b, float c); // Nguyên mẫu hàm main() { float x, y, z; printf("\n Vao ba so x, y, z: "); scanf("%f%f%f", &x&y&z);
printf("\n Max cua ba so x=%8.2f y=%8.2f z=%8.2f la: %8.2f", x, y, z, max3s(x, y, z));
} // Kết thúc hàm main float max3s(float a, float b, float c) { float max; max=a; if (max<b) max=b; if (max<c) max=c; return max; } // Kết thúc hàm max3s
Quy tắc ho¿t đßng của hàm:
Mát cách tổng quát låi gái hàm có d¿ng sau: tên hàm ([Danh sách các tham số thực])
Số các tham số thực tế thay vào trong danh sách các đối phÁi bằng số tham số hình thức và lÅn l°ÿt chúng có kiáu t°¢ng ứng vãi nhau.
Khi gặp mát låi gái hàm thì nó sẽ bắt đÅu đ°ÿc thực hián. Nói cách khác, khi máy gặp låi gái hàm ỗ mỏt vò trớ no ú trong chÂng trỡnh, máy sẽ t¿m dåi chß đó và chun đến hàm tÂng ng. Quỏ trỡnh ú diòn ra theo trình tự sau:
CÃp phát bá nhã cho các biến cāc bá.
Gán giá trß của các tham số thực cho các đối t°¢ng ứng.
Thực hián các câu lánh trong thân hàm.
Khi gặp câu lánh return hoặc dÃu } cuối cùng của thân hàm thì máy sẽ xố các đối, biến cāc bá và ra khỏi hàm.
Nu trỗ vò từ mát câu lánh return có chứa biáu thức thì giá trß của biáu thức đ°ÿc gán cho hàm. Giá trß của hàm sẽ đ°ÿc sÿ dāng trong các biáu thức chứa nó.
3.2. Tham sá của hàm.
Các tham số đ°ÿc khai báo khi xây dựng hàm đ°ÿc gái là các tham số hình thức. Các
tham số này ch°a có giá trß khi chúng ta xây dựng hàm. Ví dā: int sum(int x, int y) {return x+y; }
Hai tham số x, y là các tham số hình thức của hàm sum.
Các tham số thực sự là các tham số đ°ÿc truyßn vào khi hàm đ°ÿc gái. Chúng là các giá trß cā thá của tham số hình thức khi hàm thực hián.
int a=10, b=20, c; c=sum(a, b);
Trong hàm sum ta truyßn vào 2 giá trß 10 (a) và 20 (b). Hai giá trß này là các tham số thực sự của hàm sum.
Quy tắc ràng buác của hai lo¿i tham số:
- Số l°ÿng tham số hình thức và thực sự phÁi bằng nhau.
- Kiáu của tham số hình thức và thc s phi tÂng Âng nhau.
Truyòn tham sỏ:
Truyũn theo tham trò: khi chÂng trỡnh con c chy, mát bản sao của tham số thực sự đ°ÿc gán cho tham số hình thức (sao chép giá trß). Nghĩa là mái sÿa đổi của ch°¢ng trình con đối vãi tham số hình thức khơng gõy nh hỗng tói bin c trun vo chÂng trỡnh con theo kiáu truyßn tham trß.
Các tham số đ°ÿc truyßn bằng giá trß đ°ÿc gái là tham trá. Do chỉ có giá trß đ°ÿc trun vo chÂng trỡnh con, tham số thực sự không nhÃt thiết phÁi là mát biến thơng th°ång mà có thá là hằng giá trß, hằng biến, biáu thức trÁ vß giá trß...
Trun theo tham bin: trong c ch trun tham số bằng biến (gái tắt là truyền biến),
khi ch°¢ng trình con đ°ÿc ch¿y, tham s hỡnh thc trỗ thnh mỏt tham chiu tham số thực sự. Nghĩa là mái sÿa đổi của ch°¢ng trình con đối vãi tham số hình thức sẽ có tác dāng vãi tham số thực sự. Đây đ°ÿc gái là hiệu ứng phụ của chương trình con.
Các tham số đ°ÿc trun bằng biến đ°ÿc gái là tham biÁn. Ngc li vói c ch trun bằng giá trß, c ch trun bng bin địi hỏi tham số thực sự phÁi là mát biến.
Ví dā:
void fct(itn a, int *b) {a=20; *b=30; printf(<\nTrong ham fct: a= %d, b=%d=, a, *b); } int main()
{
int x=2, y=3; fct(x, &y);
printf(<\nKet qua: x= %d, y=%d=, x, y);
}
Vãi a là tham trß, b là tham biến của hàm fct(..); Tr°ãc khi gái hàm fct(&.) x=2, y=3
Bên trong hàm fct(&) a=20, b=30 //x là tham số thực sự truyßn vào cho a, y &. cho b Sau khi ra khỏ hàm fct(&) x=2, y=30
Câu hỏi ôn tập: Khi nào thì dùng tham biến, khi nào thì dùng tham trị?
3.3. BiÁn tồn cāc, biÁn đáa ph°¢ng
Biến khai báo bên ngoài mái hàm đ°ÿc gái là biến toàn cāc. Biến này có tác dāng trong tồn bá ch°¢ng trình ká cÁ trong các hàm.
Biến đ°ÿc khai báo bên trong mßi hàm đ°ÿc gái là biến cāc bá của hàm đó. Các biến cāc bá chỉ có tác dāng trong bÁn thân hàm chúng đ°ÿc khai báo. Các tham số hình thức cũng chính là biến cāc bá.
Do đối và biến cāc bá đßu có ph¿m vi ho¿t đáng trong cùng mát hàm nên đối và biến cāc bá cÅn có tên khác nhau.
Đối và biến cāc bá đßu là các biến tự đáng. Chúng đ°ÿc cÃp phát bá nhã khi hàm đ°ÿc xét đến và bß xố khi ra khỏi hàm nên ta khơng thá mang giá trß của đối ra khỏi hàm.
Đối và biến cāc bá có thá trùng tên vãi các đ¿i l°ÿng ngồi hàm mà không gây ra nhÅm lẫn nào.
Khi mát hàm đ°ÿc gái tãi, viác đÅu tiên là giá trß của các tham số thực đ°ÿc gán cho các đối (trong ví dā trên hàm max3s, các tham số thực là x, y, z, các đối t°¢ng ứng là a, b, c). Nh° vậy các đối chính là các bÁn sao của các tham số thực. Hàm chỉ làm viác trên các đối.
Các đối có thá bß biến đổi trong thân hàm, cịn các tham số thực thì khơng bß thay đổi.
Chú ý:
- Khi hàm khai báo khơng cú kiỏu ỗ tróc nú thỡ nú c mc ịnh là kiáu int.
- Không nhÃt thiết phÁi khai báo ngun mẫu hàm. Nh°ng nói chung nên có vì nó cho phộp chÂng trỡnh biờn dịch phỏt hiỏn lịi khi gái hàm hay tự đáng viác chuyán d¿ng.
- Nguyên mẫu của hàm thực chÃt là dòng đÅu tiên của hàm thêm vào dÃu ; . Tuy nhiên
trong nguyên mẫu có thá bỏ tên các đối.
- Hàm th°ång có mát vài đối. Ví dā nh° hàm max3s có ba đối là a, b, c. cÁ ba đối này
đßu có giá trß float. Tuy nhiên, cũng có hàm khơng đối nh° hàm main.
- Hàm th°ång cho ta mát giá trß nào đó. Lẽ dĩ nhiên giá trß của hàm phā thuác vào giá trß các đối.
3.4. Hàm không cho các giá trá:
Các hàm khơng cho giá trß giống nh° thủ tāc (procedure) trong mát số ngơn ngā lập
trình khác (Pascal, Visual Basic, &). Trong tr°ång hÿp này, kiáu của nó là void.
Ví dā hàm tìm giá trß max trong ba số là max3s ỗ trờn cú thỏ đ°ÿc viết thành thủ tāc hián thß số cực đ¿i trong ba số nh° sau:
void htmax3s(float a, float b, float c) {
float max; max=a; if (max<b) max=b; if (max<c) max=c;
printf(=\nGia tri lon nhat = %d=, max);
}
Lúc này, trong hàm main ta gái hàm htmax3s bằng câu lánh: htmax3s(x, y, z);
Ví dā : Viết hàm tính chu vi và dián tích hình trịn khi biết bán kính.
#include<stdio.h>
void cv_dt(float r, float *cv, float *dt) {
*cv=2*3.14*r ; *dt=3.14*r*r ; } int main() { float R, S, C ;
printf(=\nNhap ban kinh R ==); scanf(<%f=, &R);
cv_dt(R, &C, &S);
printf(<\nChu vi = %6.2f; dien tich = %6.2f=, C, S);
}
3.5. Hàm đá qui 3.5.1. Mở đầu: 3.5.1. Mở đầu:
C không nhāng cho phép từ hàm này gái tãi hàm khác, mà nó cịn cho phép từ mát điám trong thân của mát hàm gái tãi chính hàm đó. Hàm nh° vậy gái là hàm đá qui.
Khi hàm gái đá qui đến chính nó, thì mßi lÅn gái máy sẽ t¿o ra mát tập các biến cāc bá mãi hoàn toàn đác lập vãi tập các biến cāc bá đã đ°ÿc t¿o ra trong các lÅn gái tr°ãc.
Đá minh ho¿ chi tiết nhāng đißu trên, ta xét mát ví dā vò tớnh giai tha ca s nguyờn dÂng n. Khi khơng dùng ph°¢ng pháp đá qui hàm có thá đ°ÿc viết nh° sau:
long int gt(int n) // Tính n! vãi n>=0 {
long int gtphu=1; int i;
for (i=1; i<=n; ++i) gtphu*=i; return s;
}
Ta nhận thÃy rằng n! có thá tính theo cơng thức truy hồi sau:
n!=1 nếu n=0
n!=n*(n-1)! nếu n>0
Hàm tính n! theo ph°¢ng pháp đá qui có thá đ°ÿc viết nh° sau:
long int gtdq(int n) {
if (n==0 || n==1) return 1; else return(n*gtdq(n-1));
}
Ta đi giÁi thích ho¿t đáng của hàm đá qui khi sÿ dāng trong hàm main d°ãi đây:
#include <stdio.h> int main()
{
printf("\n 3!=%d", gtdq(3)); return 0;
}
LÅn gái đÅu tiên tãi hàm gtdq đ°ÿc thực hián từ hàm main(). Máy sẽ t¿o ra mát tập các biến tự đáng của hàm gtdq. Tập này chỉ gồm các đối n. Ta gái đối n đ°ÿc t¿o ra lÅn thứ nhÃt là n thứ nhÃt. Giá trß của tham số thực (số 3) đ°ÿc gán cho n thứ nhÃt. Lúc này biến n trong thân hàm đ°ÿc xem là n thứ nhÃt. Do n thứ nhÃt có giá trß bằng 3 nên đißu kián trong tốn tÿ
if là sai và do đó máy sẽ lựa chán câu lánh else. Theo câu lánh này, máy sẽ tính giá trß biáu thức: n*gtdq(n-1) (*)
Đá tính biáu thức trên, máy cÅn gái chính hàm gtdq vì thế lÅn gái thứ hai sẽ thực hián. Máy sẽ t¿o ra đối n mãi, ta gái đó là n thứ hai. Giá trß của n-1 ỗ õy li l đối của hàm, đ°ÿc truyßn cho hàm và hiáu là n thứ hai, do vậy n thứ hai có giá trß là 2. Bây giå, do n thứ hai vẫn ch°a thoÁ mãn đißu kián if nên máy l¿i tiếp tāc tính biáu thức: n*gtdq(n-1) (**)
Biáu thức trên l¿i gái hàm gtdq lÅn thứ ba. Máy l¿i t¿o ra đối n lÅn thứ ba v ỗ õy n th ba có giá trß bằng 1. Đối n=1 thứ ba l¿i đ°ÿc truyßn cho hàm, lúc này đißu kián trong lánh if
đ°ÿc thoÁ mãn, máy đi thực hián câu lánh: return 1=gtdq(1) (***)
Bắt đÅu từ đây, máy sẽ thực hián ba lÅn ra khỏi hàm gtdq. LÅn ra khỏi hàm thứ nhÃt ứng vãi lÅn vào thứ ba. Kết quÁ là đối n thứ ba đ°ÿc giÁi phóng, hàm gtdq(1) cho giá trß là 1 và mỏy trỗ vò xột giỏ trò biỏu thức n*gtdq(1) đây là kết quÁ của (**)
æ đây, n là n thứ hai và có giá trß bằng 2. Theo câu lánh return, máy sẽ thực hián lÅn ra khỏi hàm lÅn thứ hai, đối n thứ hai sẽ đ°ÿc giÁi phóng, kết q là biáu thức trong (**) có giá trị l 2.1. Sau ú mỏy trỗ vß biáu thức (*) lúc này là: n*gtdq(2)=n*2*1
n l¿i hiáu là thứ nhÃt, nó có giá trß bằng 3, do vậy giá trß của biáu thức trong (*) là 3.2.1=6. Chính giá trß này đ°ÿc sÿ dāng trong câu lánh printf của hàm main() nên kết quÁ in
ra trên màn hình là: 3!=6
Chú ý:
Hàm đá qui so vãi hàm có thá dùng vịng lặp thì đ¢n giÁn h¢n, tuy nhiên vãi máy tính khi dùng hàm đá qui sẽ dùng nhißu bá nhã trên ngăn xếp và có thá dẫn đến tràn ngăn xếp. Vì vậy khi gặp mát bài tốn mà có thá có cách giÁi lặp (khơng dùng đá qui) thì ta nên dùng cách lặp này. Song vẫn tồn t¿i nhāng bài tốn chỉ có thá giÁi bằng đá qui.
3.5.2. Các bài tốn có thá dùng đá qui:
Ph°¢ng pháp đá qui th°ång áp dāng cho các bài toán phā thuác tham số có hai đặc điám sau:
Bài tốn dß dàng giÁi quyết trong mát số tr°ång hÿp riêng ứng vãi các giá trß đặc biát của tham số. Ng°åi ta th°ång gái là tr°ång hÿp suy biến.
Trong tr°ång hÿp tổng qt, bài tốn có thá qui vß mát bài tốn cùng d¿ng nh°ng giá trß tham số thì bß thay đổi. Sau mát số hāu h¿n b°ãc biến đổi đá qui nó sẽ dẫn tãi tr°ång hÿp suy biến.
Bài tốn tính n giai thừa nêu trên thá hián rõ nét đặc điáu này.
3.5.3. Cách xây dựng hàm đá qui:
Hàm đá qui th°ång đ°ÿc xây dựng theo thuật tốn sau:
Trình bày cách giÁi bài tốn khi suy biến
}
else // Tr°ång hÿp tổng quát
{
Gái đá qui tãi hàm (đang viết) vãi các giá trß khác của tham số
}
3.5.4. Các ví dā vß dùng hàm đá qui:
Ví dā 1:
Bài tốn dùng đá qui tìm USCLN của hai số ngun d°¢ng a và b.
Trong tr°ång hÿp suy biến, khi a=b thì USCLN của a và b chính là giá trß của chúng. Trong tr°ång hÿp chung:
uscln(a, b)=uscln(a-b, b) nếu a>b uscln(a, b)=uscln(a, b-a) nếu a<b
Ta có thá viết ch°¢ng trình nh° sau:
#include <stdio.h>
int uscln(int a, int b); // Nguyên mẫu hàm main()
{
int m, n;
printf("\n Nhap cac gia tri cua a va b:"); scanf("%d%d", &m, &n); printf("\n USCLN cua a=%d va b=%d la:%d", m, m, uscln(m, n)) }
int uscln(int a, int b) {
if (a==b)
return a; else if (a>b)
return uscln(a-b, b); return uscln(a, b-a);
}
Ví dā 2:
Ch°¢ng trình đác vào mát số rồi in nó ra d°ãi d¿ng các ký tự liên tiếp.
# include <stdio.h> void prind(int n); main()
int a;
printf("n="); scanf("%d", &a); prind(a); } void prind(int n) { int i; if (n<0) { putchar('-'); n=-n; } if ((i=n/10)!=0) prind(i); putchar(n%10+'0'); } 3.6. Bß tißn sÿ lý C
C a ra mỏt s cỏch mỗ rỏng ngụn ng bng cỏc bỏ tiòn s lý macro Ân gin. Cú hai cỏch mỗ rỏng chớnh l #define mà ta đã hác và khÁ năng bao hàm nái dung của các file khác vào file đang đ°ÿc dßch.
Bao hàm file:
Đá dß dàng xÿ lý mát tập các #define và khai báo (trong các đối t°ÿng khác), C đ°a ra cách bao hàm các file khác vào file đang dßch có d¿ng:
#include "tên file"
Dịng khai báo trên sẽ c thay th bỗi nỏi dung của file có tên là tên file. Thơng th°ång có vài dịng nh° vậy xt hián t¿i đÅu mßi file gốc đá gái vào các câu lánh #define chung và các khai báo cho các biến ngồi. Các #include đ°ÿc phép lồng nhau. Th°ång thì các
#include c dựng nhiòu trong cỏc chÂng trỡnh lón, nú đÁm bÁo rằng mái file gốc đßu đ°ÿc
cung cÃp cùng các đßnh nghĩa và khai báo biến, do vậy tránh đ°ÿc các lßi khó chßu do viác thiếu các khai báo đßnh nghĩa. TÃt nhiên khi thay đổi file đ°ÿc bao hàm vào thì mái file phā thuác vào nó đßu phÁi dßch l¿i.
Phép thÁ MACRO:
Đßnh nghĩa có d¿ng:
#define biáu thức 1 [ biáu thức 2 ]
sẽ gái tãi mát macro đá thay thế biáu thức 2 (nếu có) cho biáu thức 1.
Ví dā:#define YES 1
Macro thay biến YES bỗi giỏ trò 1 cú ngha l hò cú chò no trong chÂng trỡnh cú xut hián biến YES thì nó sẽ đ°ÿc thay bỗi giỏ trị 1.
Ph¿m vi cho tên đ°ÿc đßnh ngha bỗi #define l t iỏm ònh ngha n cui file gốc. Có thá đßnh nghĩa l¿i tên và mát đßnh nghĩa có thá sÿ dāng các đßnh nghĩa khác tr°ãc đó. Phép
thế khơng thực hián cho các xâu dÃu nháy, ví dā nh° YES là tên đ°ÿc đßnh nghĩa thì khơng có viác thay thế nào đ°ÿc thực hián trong đo¿n lánh có "YES".
Vì viác thiết lập #define là mát b°ãc chuẩn bß chứ khơng phÁi là mát phÅn ca chÂng trỡnh biờn dịch nên có rÃt ít h¿n chế vß văn ph¿m vß viác phÁi đßnh nghĩa cái gì. Chẳng h¿n nh° nhāng ng°åi lập trình °a thích PASCAL có thá đßnh nghĩa:
#define then #define begin { #define end; }
sau đó viết đo¿n ch°¢ng trình:
if (i>0) then
begin
a=i;
......
end;
Ta cũng có thá đßnh nghĩa các macro có đối, do vậy văn bÁn thay thế sẽ phā thuác vào