[Lập trình C] Bài 5 - Hàm - BeeLab

Saturday, October 15, 2016

[Lập trình C] Bài 5 - Hàm

1. Mở đầu về hàm

Hãy nhập và chạy 2 chương trình sau, quan sát kết quả và nhận xét.
Chương trình 1:
#include <stdio.h>

void loiKhuyen(){
 printf("Neu hoc nghiem tuc va cham chi thi ban se thay:\n");
 printf("Khong co viec gi kho\n");
 printf("Chi so long khong ben\n");
 printf("Dao nui va lap bien\n");
 printf("Quyet chi at lam nen\n");
 printf("\n");
}

int main(){
 printf("Hoc bai nay kho qua!\n");
 loiKhuyen();
 
 printf("Hoc C kho qua!\n");
 loiKhuyen();
 
 return 0;
}
Kết quả:
Hoc bai nay kho qua!
Neu hoc nghiem tuc va cham chi thi ban se thay:
Khong co viec gi kho
Chi so long khong ben
Dao nui va lap bien
Quyet chi at lam nen
Hoc C kho qua!
Neu hoc nghiem tuc va cham chi thi ban se thay:
Khong co viec gi kho
Chi so long khong ben
Dao nui va lap bien
Quyet chi at lam nen
Chương trình 2:
#include <stdio.h>

void vechu(char chu, int soluong){
 int i;
 for (i = 0; i < soluong; i++) {
  printf("%c", chu);
 }
 printf("\n");
}

int main(){
 vechu('h', 20);
 vechu('a', 30);
 vechu('h', 12);
 vechu('a', 5);
 vechu('p', 10);
 
 return 0;
}
Kết quả:
hhhhhhhhhhhhhhhhhhhh
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
hhhhhhhhhhhh
aaaaa
pppppppppp
Nếu như trong chương trình có một số lệnh, hoặc công việc nào đó có dạng giống hoặc tương tự nhau cần lặp đi lặp lại nhiều lần ở các vị trí khác nhau, để khỏi mất thời gian và công sức sao chép những dãy lệnh đó ta nên tổ chức và xây dựng nó thành một chương trình con, chỗ nào trong chương trình cần thực hiện công việc đó thì chỉ cần gọi chương trình con đó mà thôi. Các chương trình con đó gọi là hàm.
Hàm là một chương trình con thực hiện một khối công việc được lặp đi lặp lại nhiều lần trong khi chạy chương trình hoặc dùng tách một khối công việc cụ thể để chương trình đỡ phức tạp.
Trong chương trình 1: Có 2 chương trình, 1 chương trình chính: main và 1 chương trình phụ: loiKhuyen không có tham số và không trả về giá trị nào nên kiểu dữ liệu của hàm được khai báo là void. Cả main và loiKhuyen đều gọi là hàm. Hàm main là hàm chính thực thi và chạy toàn bộ chương trình của ta. Hàm loiKhuyen thực hiện một số công việc (xuất ra 5 dòng chữ như trên). Trong hàm main ta có 2 chỗ gọi hàm loiKhuyen();
Trong chương trình 2: cũng tương tự như chương trình 1, nó gồm 2 hàm, 1 hàm chính là main và 1 hàm phụ, nhưng hàm phụ làvechu(char chu, int soluong); có 2 tham số hình thức là chu thuộc kiểu char và soluong thuộc kiểu int. Trong hàm main ta có 3 lần gọi hàm vechu với các tham số thực lần lượt như trong chương trình trên.

2. Nguyên tắc hoạt động của hàm

  • Khi máy gặp lời gọi hàm ở bất kỳ chỗ nào đó thì hàm bắt đầu được thực hiện, tức sẽ rời chỗ đó để đi đến hàm được gọi.
  • Nếu là hàm có tham số thì máy sẽ thực hiện truyền tham số thực cho các tham số hình thức tương ứng trong hàm.
  • Máy bắt đầu thực hiện lần lượt các câu lệnh trong thân hàm đến khi nào lệnh reuturn hoặc dấu } của hàm thì dừng lại thoát khỏi hàm trở về chương trình đã gọi nó và thực hiện tiếp các câu lệnh của chương trình này.
  • Nếu hàm trả về giá trị thì giá trị của biểu thức return trong hàm sẽ là giá trị của hàm.

3. Một số ví dụ và nhận xét quan trọng tương ứng về hàm

VD1: Hàm tính giai thừa của n- hàm trả về giá trị.

#include <stdio.h>

long giaiThua(int n) {
 long gt = 1;
 int i;
 for (i = 2; i <= n; i++){
  gt *= i;
 }
 return gt;
}

int main(){
 printf("5! = %ld \n", giaiThua(5));
 printf("6! + 1 = %ld \n", giaiThua(6) + 1);
 return 0;
}
Kết quả:
5! = 120
6! + 1 = 721
Trong VD này hàm giaiThua trả về giá trị là giai thừa của số n được truyền vào vì vậy nó có thể được coi như 1 biến và được sử dụng trong các biểu thức, sử dụng để in ra luôn kết quả. Khi gặp hàm ở bất ký đâu thì nó sẽ truyền 1 tham số tương ứng cho hàm và trả về giá trị qua lệnh return.

VD2: Sai lầm với hàm đổi chỗ 2 số.

#include <stdio.h>

void hoanvi(int a, int b) {
 int temp = a;
 a = b;
 b = temp;
}

int main(){
 int a, b;
 printf("Nhap 2 so a, b:\n");
 scanf("%d%d", &a, &b);
 
 printf("Ban da nhap:\n a = %d \n b = %d\n", a, b);
 
 hoanvi(a, b);
 
 printf("Sau khi goi ham hoanvi:\n a = %d\n b = %d", a, b);
 return 0;
}
Kết quả khi nhập a = 3, b = 8.
Nhap 2 so a, b:
3
8
Ban da nhap:
a = 3
b = 8
Sau khi goi ham hoanvi:
a = 3
b = 8
Với chương trình này chúng ta xây dựng 1 hàm hoán vị để chuyển đổi vị trí giữa 2 bến a và b, tuy nhiên chúng ta đã sử dụng tham trị nên các giá trị của a và b không thay đổi hay nói cách khác là chúng không được hoán vị cho nhau. Sau đây ta sẽ xem VD3.

VD3: Hàm hoán vị

#include <stdio.h>

void hoanvi(int *a, int *b) {
 int temp = *a;
 *a = *b;
 *b = temp;
}

int main(){
 int a, b;
 printf("Nhap 2 so a, b:\n");
 scanf("%d%d", &a, &b);
 
 printf("Ban da nhap:\n a = %d \n b = %d\n", a, b);
 
 hoanvi(&a, &b);
 
 printf("Sau khi goi ham hoanvi:\n a = %d\n b = %d", a, b);
 return 0;
}
Kết quả:
Nhap 2 so a, b:
3
8
Ban da nhap:
a = 3
b = 8
Sau khi goi ham hoanvi:
a = 8
b = 3
Trong VD3 này ta đã truyền vào 2 biến kiểu con trỏ để lưu địa chỉ của a và b và khi gọi hàm hoanvi chứng ta truyền vào là &a, &b, về điều này ta sẽ tìm hiểu xâu hơn ở bài con trỏ. Ở đây mình chỉ giới thiệu thêm để chúng ta có thể khắc phục được tình trạng sai lầm bên trên.
Chú ý: Nếu chúng ta lưu file là .cpp (tức là dùng C++) thì chúng ta có thể sử dụng được kiểu truyền tham biến bằng dấu & như sau:
#include <stdio.h>

void hoanvi(int &a, int &b) {
 int temp = a;
 a = b;
 b = temp;
}

int main(){
 int a, b;
 printf("Nhap 2 so a, b:\n");
 scanf("%d%d", &a, &b);
 
 printf("Ban da nhap:\n a = %d \n b = %d\n", a, b);
 
 hoanvi(a, b);
 
 printf("Sau khi goi ham hoanvi:\n a = %d\n b = %d", a, b);
 return 0;
}
Tuy nhiên nếu bạn lưu file là .c thì máy sẽ báo lỗi vì trong C không có cách truyền như vậy.
Chúng ta đã biết thì các hàm trả về giá trị thì chỉ có 1 giá trị được trả về, để có thể lấy được nhiều giá trị từ hàm ta phải sử dụng tương tự như ví dụ hoanvi này. Nếu gặp TH phải lấy nhiều giá trị từ hàm thì các bạn hãy sử dụng 1 trong 2 cách mà tạm thời chúng ta vừa minh họa, (chú ý là dùng tham biến chỉ trong C++, tức là file .cpp).

VD4: Các hàm gọi nhau.

#include <stdio.h>

int max_2(int a, int b) {
 return a > b ? a : b;
}

int max_3(int a, int b, int c) {
 return max_2(max_2(a, b), c);
}

int main(){
 int a = 7, b = 13, c = 4;
 printf("So lon nhat la %d", max_3(a, b, c));
 return 0;
}
Ở ví dụ này chúng ta xây dựng 2 hàm để tìm max của 2 số và 3 số. Trong hàm max_2 chúng ta sử dụng toán tử diều kiện để tìm số lớn nhất trong 2 só. Trong hàm max_3 ta đã gọi hàm max_2 đến 2 lần để tìm số lớn nhất trong a, b rồi lấy số tìm được só sánh với c tìm ra số lớn nhất.
Chú ý: Hàm khai báo sau được gọi các hàm đã khai báo trước nó nhưng các hàm khai báo trước không được gọi hàm khai báo sau. Tức là trong VD trên nếu ta gọi hàm max_3 trong hàm max_2 thì máy sẽ báo lỗi. Để khắc phục điều này chúng ta thường khai báo các hàm ở đầu chương trình sau đó chúng ta định nghĩa các hàm ngay bên dưới hoặc sau hàm main() ta xét ví dụ tiếp:

VD5: Khai báo hàm trước khi định nghĩa (viết) hàm

#include <stdio.h>

int max_2(int a, int b);
int max_3(int a, int b, int c);
int max_4(int a, int b, int c, int d);

int max_3(int a, int b, int c) {
 return max_2(max_2(a, b), c);
}

int max_2(int a, int b) {
 return a > b ? a : b;
}

int main(){
 int a = 7, b = 13, c = 4, d = 16;
 printf("So lon nhat trong a, b, c la %d\n", max_3(a, b, c));
 printf("So lon nhat trong a, b, c, d la %d\n", max_4(a, b, c, d));
 return 0;
}

int max_4(int a, int b, int c, int d) {
 return max_2(max_2(a, b), max_2(c, d));
}
Khi làm việc chúng ta nên khai báo các hàm trước như thế này và định nghĩa sau. Như vậy chúng ta sẽ tránh được một số lỗi khi mà chúng ta muốn dùng các hàm trong nhau.
Ta đã biết các hàm có thể gọi lẫn nhau và một hàm cũng có thể gọi chính nó, đó là ta sử dụng hàm đệ quy.

VD6: Hàm đệ quy

#include <stdio.h>

int giaiThua(int n) {
 if(n == 0) return 1; // dieu kien dung
 return giaiThua(n-1) * n; // loi goi chinh no
}

int main() {
 int n = 5;
 printf("%d! = %d",n, giaiThua(n));
 return 0;
}
Lưu ý trong hàm đệ quy luôn luôn có 2 yếu tố là điều kiện dừng và lời gọi chính nó.

VD7: Dùng dẫn hướng #define để định nghĩa hàm đơn giản

#include <stdio.h>

#define tong(x, y) x + y

int main() {
 int a = 5, b = 8;
 printf("%d + %d = %d",a, b, tong(a, b));
 return 0;
}
Đến đây chúng ta đã cơ bản nắm được về hàm, nhưng còn rất nhiều điều cần nói về hàm nữa, đặc biệt là hàm liên quan đến mảng và con trỏ. Chúng ta sẽ tiếp tục tìm hiều chúng ở các bài sau cùng với mảng và con trỏ.

No comments:

Post a Comment