30/09/2018, 16:29

lập trình C bài nói về hàm

Hàm

  1. Hàm, tổ chức chương trình thành các hàm.

  2. Tại sao phải sử dụng hàm:

Ví dụ: khi ta viết một chương trình tìm số nguyên tố từ 1 đến 100. Thì như các bạn cũng biết để kiểm tra 1 số có là số nguyên tố hay không ta có một đoạn code khá dài. Vì vậy nếu là 100 số thì ta phải viết 100 đoạn code hoặc sử dụng vòng while. Nhưng như vậy bài toán sẽ rất phức tạp. Và không có khả năng sử dụng lại mã nguồn. ví dụ như ta có 2 phần cần tìm số nguyên tố thì bạn phải viết 2 lần thuật tìm nguyên tố. Nếu có 100 phần. thì chắc là bạn sẽ không muốn code nữa. Vậy sử dụng hàm sẽ phù hợp trong trường hợp này

Lợi ích của sử dụng hàm:

• Tiết kiệm code
• Tiết kiệm thời gian thực hiện.
• Tăng tính hiệu quả của câu lệnh truy vấn bằng việc gọi các hàm

  1. Các nguyên tắc xây dựng hàm:

Hàm có thể xem là một đơn vị độc lập của chương trình. Các hàm có vai trò ngang nhau, vì vậy không cho phép xây dựng một hàm bên trong các hàm khác.

Một số chú ý liên quan đến hàm:

• Tên hàm.
• Kiểu giá trị trả về của hàm. (kiểu dữ liệu trả về).
• Đối hay tham số hình thức (hàm main gửi dữ liệu cho hàm con ở dạng kiểu dữ liệu nào, và cách thức gửi là gì).
• Thân hàm (nội dung của hàm).
• Khai báo hàm tức khai báo prototype (báo trước cho trình dịch là ta sẽ sử dụng hàm gì).
• Lời gọi hàm. (hàm main gọi hàm con để thực hiện công việc).
• Tham số thực (tham số thực truyền vào).

Đối với hàm bao giờ cũng có 2 giai đoạn là khai báo và sử dụng.

  1. Cấu trúc tổng quát của chương trình.

Chương trình gồm nhiều hàm được viết theo trình tự như sau.

  • Các #include
  • Các #define
  • Khai báo các đối tượng dữ liệu ngoài // khai báo biến toàn cục
    (biến, mảng, cấu trúc,hợp,…)
  • Khai báo nguyên mẫu của các hàm. // báo trước là ta sẽ dùng hàm gì.
  • Hàm main. // hàm chính được chạy đầu tiên.
  • Định nghĩa các hàm // bắt đầu xây dựng nội dung những hàm mà ta đã khai báo ở trên.

Chú ý: hàm main có thể được đặt sau hoặc trước hay xen giữa các hàm con. Ví dụ có thể làm như sau.

  • Khai báo nguyên mẫu của các hàm. // báo trước là ta sẽ dùng hàm gì.
  • Định nghĩa các hàm
  • Hàm main. // hàm chính được chạy đầu tiên.
  • Định nghĩa các hàm // bắt đầu xây dựng nội dung những hàm mà ta đã khai báo ở trên.
  1. Xây dựng hàm được viết theo mẫu sau:

<Kiểu dữ liệu> tên_hàm( <danh sách các đối số> )
{
Khai báo các biến cục bộ. // tức là các biến sẽ sử dụng trong hàm. Biến này chỉ được phép hoạt động trong hàm. Khi ra khỏi hàm biến này không còn nữa.
Các câu lệnh làm nhiệm vụ của hàm.
[ <return biểu_thức> ] ; // có thể có hoặc không
}

Kiểu dữ liệu có thể là kiểu void. Tức là không có giá trị trả về. Lúc này ta sẽ không sử dụng return <biểu thức>;
Nếu kiểu dữ liệu là kiểu float, double, int,… cấu trúc, mảng, con trỏ,… thì ta phải sử dụng lệnh return <biểu thức>; với biểu thức có kiểu giữ liệu là kiểu dữ liệu của tên hàm.

Ví dụ:
Xây dựng một hàm tính chuvi hình chữ nhật
float chuvi(float x, float y)
{ float cv; // khai báo biến cục bộ. Khi thoát khỏi hàm thì biến này sẽ không còn.
printf(“biến được truyền vào hàm: &x=%p x= %2.2f , &y=%p y=%2.2f ”,&x,x,&y,y);
cv=(x+y)*2;
return cv;
}

  1. Lời gọi hàm.

Tên_hàm(<danh sách các tham số thực sự>);
Ví dụ gọi hàm chuvi
Chuvi(14,5);

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

  2. Ví dụ minh hoạ

Code:

  1. #include <conio.h>

  2. #include <stdio.h>

  3. float chuvi(float x, float y); // khai báo nguyên mẫu hàm

  4. void main() // hàm main (thân chương trình chính)

  5. { float a,b,cv; clrscr();

  6. printf(“ vao chieu dai va rong”);

  7. scanf(“%f,%f”,&a,&b);

  8. printf(“ du lieu vua nhap: &a=%p a= %2.2f , &b=%p b=%2.2f”,&a,a,&b,b);

  9. cv= chuvi(a,b); // gọi hàm tính chu vi, truyền tham số thực sự cho hàm (tham số có giá trị vừa nhập). Và gán kết quả trả về của hàm cho biến cv

  10. printf(“ chu vi hinh chu nhat la : %2.2f”,cv);

  11. getch();

  12. }

  13. // bắt đầu xây dựng hàm đã khai báo ở trên

  14. float chuvi(float x, float y)

  15. { float cv; // khai báo biến cục bộ

  16. printf(“biến được truyền vào hàm: &x=%p x= %2.2f , &y=%p y=%2.2f ”,&x,x,&y,y);

  17. cv=(x+y)*2;

  18. return cv;

  19. }
    Dòng thứ 3 khai báo nguyên mẫu hàm. Lợi ích của việc này là giúp cho trình dịch tự động phát hiện lỗi khi gọi hàm (đối số không đúng, kiểu dữ liệu đối bị sai).
    Dòng 9. Gọi hàm và truyền tham số thực cho hàm. Lúc này sẽ tạm dừng thực hiện các lệnh ở hàm main và chuyển sang thực hiện các lệnh trong hàm chuvi.
    Khi thực hiện các lệnh trong hàm chuvi thì ở dòng 18 báo dấu hiệu kết thúc hàm và trả về cho hàm main kết quả sau khi thực hiện tính toán. Lúc này ở hàm main biến cv sẽ nhận giá trị mà hàm chuvi đã trả về. Bây giờ hàm main tiếp tục thực hiện các công việc tiếp theo của mình.

Ví dụ. Hàm không có đối.

Code:

  1. #include <conio.h>

  2. #include <stdio.h>

  3. float chuvi(); // khai báo nguyên mẫu hàm,Không có đối

  4. void main() // hàm main (thân chương trình chính)

  5. { clrscr();

  6. printf(“ tinh chu vi hinh chu nhat”);

  7. printf(“ chu vi la : %2.2f”,chuvi() ); // gọi hàm tính chu vi . kết quả được in luôn lên màn hình không cần gán qua biến trung gian.

  8. getch();

  9. }

  10. // bắt đầu xây dựng hàm đã khai báo ở trên

  11. float chuvi()

  12. {

  13. float x,y,cv; /// khai báo biến cục bộ

  14. printf(“ vao chieu dai va rong”);

  15. scanf(“%f,%f”,&x,&y);

  16. printf(“du lieu vua nhap: &x=%p x= %2.2f , &y=%p y=%2.2f ”,&x,x,&y,y);

  17. return (x+y)*2; // trả về kết quả tính chu vi

  18. }
    Ví dụ : hàm không có giá trị trả về.
    Code:

  19. #include <conio.h>

  20. #include <stdio.h>

  21. void hoanvi(float x, float y); // khai báo nguyên mẫu hàm. Không có giá trị trả về

  22. void main() // hàm main (thân chương trình chính)

  23. { float a,b;

  24. clrscr();

  25. printf(“ vao so a, so b”);

  26. scanf(“%f,%f”,&a,&b);

  27. printf(“ du lieu vua nhap: &a=%p a= %2.2f , &b=%p b=%2.2f”,&a,a,&b,b);

  28. hoanvi(a,b); // gọi hàm hoán vị, truyền tham số thực sự cho hàm (tham số có giá trị vừa nhập).

  29. printf(“ sau khi hoan vi: &a=%p a= %2.2f , &b=%p b=%2.2f”,&a,a,&b,b);

  30. getch();

  31. }

  32. // bắt đầu xây dựng hàm đã khai báo ở trên

  33. void hoanvi(float x, float y)

  34. {

  35. printf(“biến được truyền vào hàm: &x=%p x= %2.2f , &y=%p y=%2.2f ”,&x,x,&y,y);

  36. // thuc hien hoan vi hai so a va b

  37. float temp=x;

  38. x=y;

  39. y=temp;

  40. printf(“ ket qua hoan vi: &x=%p x= %2.2f , &y=%p y=%2.2f”,&x,x,&y,y);

  41. }

Sau khi thực hiện hoán đổi chúng ta thấy ở hàm main kết quả không có thay đổi gì. Vậy làm thế nào để khi truyền đối vào hàm mà ta thay đổi kết quả của đối trong hàm khi ra ngoài hàm kết quả cũng thay đổi theo. Để làm được điều này ta đi vào phần 2.

  1. Hàm có đối là con trỏ, hàm có giá trị trả về là con trỏ.

  2. Hàm có đối là con trỏ.

Giả sử ta có hàm như sau
Void chunhat(float a, float b, float *cv, float *dt);
ở đây hàm này có 2 đối là con trỏ. Đó là cv và dt. Như các bạn cũng đã biết con trỏ là biến dùng để lưu địa chỉ của một ô nhớ. Vậy để truyền tham số thực vào cho hàm khi đối là con trỏ ta không thể sử dụng cách thông thường.
Ví dụ:
Ta khai báo
Float a=5, b=10, chuvi,dientich
Chunhat(a,b,&chuvi,&dientich); // gọi hàm chunhat và truyền tham số thực.
ở đây ta truyền tham số thực cho biến cv và dt ta sử dụng cách truyền là địa chỉ .
bởi vì con trỏ chỉ lưu địa chỉ của ô nhớ. Nên không thế truyền như hai biến a và b.

ở đây mặc dù hai biến chuvi và dientich ta chưa có giá trị khởi tạo nhưng vẫn truyền được vào hàm đó là do ta truyền địa chỉ. Nếu truyền theo cách thông thường mà không gán giá trị khởi tạo trước sẽ bị lỗi.

Ví dụ minh hoạ 1.
Code:
#include <conio.h>
#include <stdio.h>
void chunhat(float, float, float *, float *);

void chunhat(float a, float b, float *cv, float *dt)
{

printf(“ &a=%p a=%2.2f, &b=%p b=%2.2f , &cv=%p, &dt=%p ”, &a,a,&b,b, cv, dt);

cv=2(a+b);
dt=ab;
}

void main()
{
float a,b,chuvi,dientich;
clrscr();
printf(“ vao chieu dai va chieu rong = ”);
scanf(“%f%f”,&a,&b);
printf(“ &a=%p a=%2.2f, &b=%p b=%2.2f , &chuvi=%p, &dientich=%p ”, &a, a, &b, b, &chuvi, &dientich);

chunhat(a,b,&chuvi,&dientich); // goi ham chunhat va truyen tham so thuc.
printf(“ dien tich = %2.2f, chuvi=%2.2f ”,dientich, chuvi);

getch();
}
Khi chạy ta thấy rằng biến chuvi và dientich của hàm main lúc đầu chưa có giá trị nhưng sau khi gọi hàm đã nhận được giá trị từ trong hàm chunhat

Ví dụ 2:
Code:

  1. #include <conio.h>

  2. #include <stdio.h>

  3. void hoanvi(a,b);

  4. void main()

  5. { float a,b;

  6. clrscr();

  7. printf(“ vao so a, so b”);

  8. scanf(“%f,%f”,&a,&b);

  9. hoanvi(&a,&b); // gọi hàm hoán vị, truyền tham số thực sự cho hàm (ta truyền địa chỉ vào hàm).

  10. printf(“ sau khi hoan vi: &a=%p a= %2.2f , &b=%p b=%2.2f”,&a,a,&b,b);

  11. getch();

  12. }

  13. // bắt đầu xây dựng hàm hoán vị đã khai báo ở trên

  14. void hoanvi(float *x, float *y)

  15. { // thuc hien hoan vi hai so a va b

  16. float temp=*a;

  17. *a=*b;

  18. *b=temp;

  19. }
    Chúng ta thấy rằng khi truyền bằng địa chỉ vào hàm thông qua đối là con trỏ thì khi ta thay đổi giá trị của đối bên trong hàm thì giá trị của tham số truyền vào cũng thay đổi theo.

  20. Hàm có giá trị trả về là con trỏ.

Như đã nói ở trên hàm có thể có giá trị trả về là một con trỏ. Vậy để hàm trả về một giá trị con trỏ thì ta khai báo nguyên mẫu hàm như sau:

<kiểu dữ liệu> *<tên hàm>(<danh sách các đối>);

Ví dụ:
Int *timkiem( int);

Ví dụ minh hoạ sử dụng hàm có giá trị trả về là con trỏ
Code:
#include <stdio.h>
#include <stdio.h>

int *min(int a, int b)
{ int *m;
m=(int *)malloc(sizeof(int));
*m=(a<b) ? a : b;

return m; // trả về con trỏ
}

void main()
{
Int a=5, b=8, *nho;

nho=min(a,b); // gọi hàm có giá trị trả vè là con trỏ

printf(“so nho nhat la : %d”, *nho);
getch();
}
Ví dụ 2.
Code:
#include <conio.h>
#include <stdio.h>
#include <stdlib.h>
int *nhap(int *n)
{ int *a;
printf(“nhap so phan tu cua mang ”);
scanf(“%d”,n); // do ta truyền vào là địa chỉ nên không phải viết &

a=(int*)malloc((*n)*sizeof(int));// cấp phát n ô nhớ
printf(“nhap mang a ”);
for (int i=0; i<*n; i++)
scanf(“%d”,&a[i]);

return a; // trả về địa chỉ đầu của mảng a. (hay có thể nói trả về mảng a)
}

void main()
{
int *a,n;
clrscr();
a=nhap(&n);
printf(“ mang vua nhap la “);
for (int i=0; i<n; i++)
printf(“%d ”,a[i]);
getch();
}

Bài tập bắt buộc

Bài 1: Nhập hàm tính tổng 1! +2! + 3! + … +n! với n<=12.

Bài 2: Xây dựng một hàm tính chu vi và diện tích hình tròn.(hàm chỉ thực hiện công việc tính toán có thể nhập. Nhưng kết quả không được in ra màn hình ngay từ trong hàm. Mà phải thoát khỏi hàm mới được in kết quả. Tức là in kết quả ở hàm main).
Bài 3: Xây dựng hàm int *nhap(int *n, int *s); để nhập một mảng một chiều, tính tổng các phần tử của mảng kết quả lưu trong biến s. Kết quả được in khi thoát khỏi hàm

Bài tập bổ xung
Bài 4: Viết hàm tối giản một phân số. Kết quả được xuất ra màn hình sau khi ra khỏi hàm.

Bài 5: Lập hàm giải phương trình bậc 2.
Int ptB2(float a, float b, float c, float *x1, float *x2);
Trong đó a,b,c là hệ số phương trình. X1,x2 là nghiệm.
Hàm nhận giá trị 0 nếu vô nghiệm. Nhận giá trị 1 nếu có 1 nghiệm, nghiệm này được đặt trong x1. Hàm nhận giá trị 2 nếu có 2 nghiệm, nghiệm được lưu tại x1, x2.

Bài 6: Hàm int doixung(int x); kiểm tra một số nguyên hệ 10 có là số đối xứng không.
Ví dụ: 24642 thì doixung(24642) ==1. (là số đối xứng). Nếu không là số đối xứng thì trả về 0.
Gợi ý: hàm char *itoa(int x, char *s, int cs); chuyển đổi số nguyên x trong hệ đếm cơ số
Cs=10 (hệ 10). Cs=8(hệ 8). Cs=16(hệ 16). Sang xâu và lưu trong vùng nhớ xâu s.
Ví dụ:
Char *s;
Itoa(1234, s, 10); ta được xâu s=”1234”;

Nguyễn Minh Dũng viết 18:37 ngày 30/09/2018

This topic is now unlisted. It will no longer be displayed in any topic lists. The only way to access this topic is via direct link.

Bài liên quan
0