3.2.1Định nghĩa hàm
Hàm là một đoạn chương trình độc lập, giải quyết một công việc hoàn chỉnh và có thểđược sử dụng nhiều lần trong chương trình. Bên cạnh việc sử dụng các hàm sẵn
có trong thư viện, lập trình viên có thể tựđịnh nghĩa các hàm cho riêng mình. Cú pháp tổng quát đểđịnh nghĩa hàm như sau:
<kiểu_trả_về> <tên_hàm> ([danh_sách_tham_số]) { /* thân hàm */ <các câu lệnh> [return <giá_trị>;] }
Trong định nghĩa hàm luôn bao gồm hai phần là tiêu đề và thân hàm. Dòng đầu
tiên trong định nghĩa hàm được gọi là phần tiêu đề. Phần này mô tảcác thông tin được
trao đổi giữa bên trong và bên ngoài hàm. Phần tiêu đề gồm 3 thành phần là kiểu_trả_về của hàm, tên_hàm và danh_sách_tham_số.
102
- Kiểu_trả_về: Sau khi thực thi xong, hàm thường cung cấp một kết quả gì đó cho môi trường bên ngoài. Kiểu_trả_về sẽ chỉ ra kiểu của kết quả cần trả về
của hàm. Kiểu_trả_về có thể là kiểu bất kì như int, float, double,… Nếu hàm không cần trả về kết quả thì kiểu_trả_về sẽ là void.
- Tên_hàm: Mỗi hàm đều phải có một tên duy nhất. Tên hàm phải là một
định danh hợp lệ và nên có ý nghĩa phù hợp với chức năng mà hàm thực hiện.
- Danh_sách_tham_số: Tham số của hàm là phương tiện để truyền dữ liệu cần thiết từ bên ngoài vào trong hàm và từ trong hàm ra bên ngoài. Nghĩa
là, sẽ có tham số chứa dữ liệu vào cần cho hoạt động của hàm và có tham số
chứa kết quảmà hàm tính toán được. Các tham sốnày được gọi là tham số
hình thức, khi khai báo chúng cần phải chỉ ra kiểu của tham số và tên của tham số giống như khai báo biến. Một hàm có thể có một, nhiều tham số
hoặc không có tham số nào cả. Nếu có nhiều tham số thì chúng phải cách nhau bởi dấu phẩy. Cần nhớ rằng, dù các tham số có cùng kiểu thì cũng
phải khai báo riêng biệt nhau. Ví dụnhư:
int sum(int a,b) /* không hợp lệ */
int sum(int a, int b). /* hợp lệ */
Trong trường hợp không có tham số thì ta có thể viết theo hai cách như ví
dụ sau: int main() hoặc int main(void)
Dòng tiêu đề sẽ giúp phân biệt các hàm với nhau, có nghĩa là không có hai hàm có dòng tiêu đề giống nhau trong cùng một chương trình. Ở cuối dòng tiêu đề không có dấu chấm phẩy.
Tiếp theo sau dòng tiêu đề là phần thân hàm nằm trong cặp dấu { }. Khi một hàm
được gọi, việc thực hiện hàm sẽ bắt đầu từ thân hàm và kết thúc khi gặp lệnh return hoặc khi gặp dấu ‘}’ cuối cùng. Phần thân hàm có thể chứa 3 phần sau:
- Các khai báo biến cục bộ cần sử dụng trong hàm.
- Các câu lệnh cần thiết cho việc thực hiện tác vụđã đặt ra cho hàm.
- Câu lệnh return dùng để kết thúc việc thực hiện của một hàm, trả kết quả
và chuyển quyền điều khiển vềnơi gọi hàm.
Cú pháp tổng quát của lệnh return: return [(biểu_thức)];
Giá trị sau từ khóa return có thể là hằng số, biến, hoặc biểu thức. Giá trị
này phải có kiểu phù hợp với kiểu_trả_vềđã được khai báo ở dòng tiêu đề. Ví dụ:
return; /* Hàm không trả về giá trị */
return 1; /* Hàm trả về giá trị nguyên */
103 Đối với hàm có giá trị trả về thì phải có ít nhất một câu lệnh return. Ngược lại, hàm không trả về giá trị nào cả thì lệnh return có thể vắng. Một hàm có thể có nhiều câu lệnh return được đặt ở những vị trí khác nhau trong thân hàm khi giá trị cần trả về phụ thuộc vào một điều kiện nào đó.
Ví dụ:
if(x <= 0) return 0; else
return 1;
Hạn chế của lệnh return là nó chỉ có thể trả về một giá trị duy nhất.
Như vậy, muốn xây dựng một hàm ta cần phải xác định các thông tin quan trọng gồm: hàm sẽ thực hiện công việc gì, tên hàm, dữ liệu đầu vào (nếu có), và kết quả trả
về (nếu có).
Ví dụ: Hàm sau đây thực hiện công việc tìm số lớn nhất trong 3 số nguyên a, b, c.
1. #include <stdio.h>
2. #include <conio.h>
3. /* Dinh nghia ham voi a,b,c la tham so hinh thuc */
4. int find_max(int a,int b,int c)
5. {
6. int max = a; /*khai bao bien cuc bo*/
7. if(b > max)
8. max = b;
9. if(c > max)
10. max = c;
11. return max; /*tra ve gia tri lon nhat*/
12. }
13.
14. int main(){
15. int x, y, z, res;
16. printf(“Nhap 3 so nguyen: “);
17. scanf(“%d%d%d”, &x, &y, &z);
18. res = find_max(x, y, z);
19. printf(“So lon nhat la %d \n“, res);
20. getch();
21. return 0;
22. }
Kết quả của chương trình:
Nhap 3 so nguyen: 3 7 5 So lon nhat la 7
Lưu ý: C không cho phép các hàm lồng nhau, nghĩa là phần định nghĩa của hàm này phải độc lập hoàn toàn với hàm khác.
104
3.2.2Nguyên mẫu hàm
Bất kì một hàm nào cũng phải được định nghĩa trước khi sử dụng. Vì vậy, người
ta thường phải viết tất cả các hàm phía trước hàm main(), làm cho nó nằm quá sâu ở dưới gây khó khăn hơn cho việc lập trình. Hàm main() là hàm chính được thực thi đầu tiên thì nên nằm ở đầu sẽ tốt hơn. Khi đó việc khai báo hàm là cần thiết, cho phép sử
dụng hàm trước khi hàm được định nghĩa, giúp cho chương trình sẽ trở nên linh hoạt
hơn. Một khai báo hàm hay còn gọi là nguyên mẫu hàm sẽ cung cấp cho trình biên dịch mô tả về một hàm sẽ được định nghĩa ở một vị trí nào đó trong chương trình. Nguyên mẫu hàm sẽ khai báo các thông tin tối thiểu nhất gồm kiểu trả về của hàm, tên hàm, danh sách tham số. Dưới đây là cú pháp tổng quát của một nguyên mẫu hàm:
<kiểu_trả_về> <tên_hàm> ([danh_sách_tham_số]) ;
Như vậy, sự khác nhau cơ bản giữa nguyên mẫu hàm và định nghĩa hàm nằm ở
chỗ nguyên mẫu hàm phải kết thúc bằng dấu chấm phẩy và không có phần thân hàm. Ví dụ: Kiểm tra một năm bất kỳ có phải là năm nhuận hay không?
1. #include <stdio.h>
2. #include <conio.h>
3. int is_leap(int year); /*nguyen mau ham toan cuc*/
4. int main()
5. {
6. int y, res;
7. printf("Nhap vao nam can kiem tra: ");
8. scanf("%d", &y);
9. res = is_leap(y);
10. if(res == 1)
11. printf("%d la nam nhuan.\n", y);
12. else
13. printf("%d khong phai la nam nhuan.\n", y);
14. getch();
15. return 0;
16. }
17. int is_leap(int year)
18. {
19. if(((year%4 == 0)&&(year%100 != 0))||(year%400 == 0))
20. return 1; /* 1 - La nam nhuan */
21. return 0; /* 0 - Khong phai la nam nhuan */
22. }
Kết quả của chương trình:
Nhap vao nam can kiem tra: 2005 2006 khong phai la nam nhuan.