You are on page 1of 39

TRƯỜNG ĐẠI HỌC KHOA HỌC TỰ NHIÊN - ĐHQG TPHCM

KHOA TOÁN - TIN HỌC


BỘ MÔN ỨNG DỤNG TIN HỌC

HÀM
THỰC HÀNH CƠ SỞ LẬP TRÌNH
TUẦN 13 - THÁNG 5 NĂM 2023
GIỚI THIỆU
Hàm trong C dùng để tổ chức và sắp xếp hiệu quả
các đoạn code trong chương trình.
Ta có thể tách riêng các hàm thành các file mã nguồn
riêng hoặc viết trực tiếp hàm trong mã nguồn chính.
NỘI DUNG

CƠ CHẾ TRUYỀN
01 HÀM TRONG C 02 THAM SỐ 03 MỘT SỐ VÍ DỤ
- Hàm là gì? - Biến địa phương - swap(x;y)
- Khai báo hàm trong C - Biến toàn cục
- Lệnh return - Tham trị & Tham chiếu
- Hàm trả về
- Hàm không trả về
01
HÀM TRONG C
[return type] [name] ([arguments]){

return [value];
}
HÀM LÀ GÌ?
- Hàm là một đoạn chương trình có
tên, đầu vào và đầu ra.
- Hàm có chức năng giải quyết một
số vấn đề chuyên biệt cho chương
trình chính.
- Có thể gọi hàm để sử dụng nhiều
lần với nhiều đầu vào khác nhau.
- Các hàm có chức năng chuyên biệt
hỗ trợ cấu trúc gọn gàng cho
chương trình chính, dễ sử dụng và
dễ sửa lỗi.
KHAI BÁO HÀM TRONG C
[kiểu trả về] [tên hàm] ([các tham số đầu vào])
{
[thân hàm]
}

● [kiểu trả về] : kiểu dữ liệu bất kỳ của C như int, float, double, char, long, …
Kiểu trả về có thể là con trỏ (trả về một địa chỉ của biến ở hàm main hoặc
hàm con đang gọi)
● [tên hàm]: đặt theo quy tắc camelCase.
● [các tham số đầu vào]: tham số hình thức đầu vào giống khai báo biến, có
quy định thứ tự, cách nhau bằng dấu phẩy (,)
KHAI BÁO HÀM TRONG C
[kiểu trả về] [tên hàm] ([các tham số đầu vào])
{
[thân hàm]
}

Hàm trả về sum Hàm không trả về input


int sum(int mot , int hai){ void input(int*so){
int tong = mot + hai; printf(“Nhap so:”);
return tong; scanf(“%d”,so);
} }
Lệnh Return trong C
Return trong hàm có 2 mục đích chính:
● Ngay lập tức trở về hàm chính/hàm hoặc chương trình con đang gọi
(tương tự như lệnh break trong vòng lặp)
● Bất kỳ giá trị/ hàm/… trong ngoặc () theo sau return sẽ được trả về như 1
giá trị cho chương trình gọi.
Tuy nhiên, giới hạn của return trong C là chỉ trả về 1 giá trị duy nhất.

Ta có thể return:
return;
return(hằng);
return(biến);
return(biểu thức);
return(câu lệnh đánh giá);
Hàm trả về trong C
Hàm trả về có chức năng thực hiện nhiều bước tính toán, sau đó gán giá trị cần
tìm cho biến hàm rồi kết thúc bằng lệnh return.

Ví dụ: Hàm kiểm tra một số có phải là số nguyên tố.

Khai báo hàm trả về kiểu nguyên, tên primeTest có 01 tham số hình thức
int primeTest(int number){
kiểu nguyên là number.

for (int i=2; i <= sqrt(number);


Cho i chạy từ 2 đến căn number
i++)

if(number%i == 0) Nếu number chia hết cho i nghĩa là number không phải là số nguyên tố.

return 0; Trả về giá trị 0 cho primeTest (0 có nghĩa là không) rồi dừng hàm.

Nếu hàm không bị dừng ở trên thì number là số nguyên tố (không chia
return 1; hết cho số nào ngoài 1 và chính nó), trả về giá trị 1 cho primeTest và kết
thúc hàm.

}
Hàm không trả về trong C
Nếu hàm không cần trả về giá trị gì (hàm thực hiện biến đổi) thì ta dùng kiểu
void. Hàm không trả về vẫn có thể có tham số hình thức.
Ví dụ: Hàm nhập mảng.
Khai báo hàm nhập mảng tên inputArray có hai tham số hình thức là con trỏ
void inputArray(int *array, int *n){ mảng *array và con trỏ số lượng phần tử mảng *n.
Khi ta gọi hàm, ta sẽ truyền địa chỉ của mảng và biến số lượng để biến đổi.

printf(“Nhap n: ”); scanf(“%d”,n); Nhập n.

for(int i=0, i<n, i++){ Nhập mảng.

printf(“Nhap a[%d]: ”,i);

scanf(“%d”,array+i);

} Kết lúc vòng lặp.

} Kết thúc hàm.


02
CƠ CHẾ TRUYỀN THAM SỐ
Local variable
Global variable
BIẾN CỤC BỘ VÀ BIẾN TOÀN CỤC
Biến cục bộ:
#include <stdio.h>
float chuan;
- Cấp phát một vùng nhớ tạm thời. int n;

- Được khai báo bên trong hàm con.


float chuanMax(float x[], int n) {
int max = *x;
- Hỗ trợ lưu các giá trị tính toán trong hàm. for (int i = 0; i < n; i++)

- Biến mất khi ra khỏi hàm con. if (max < *(x+i))


max = *(x+i);
- Biến cục bộ tĩnh là biến cục bộ nằm trong return max;

hàm main. }
void main(){
float x[1000];
Biến toàn cục: printf(“Nhap so chieu n = ”);
- Khai báo/ định nghĩa bên ngoài các khối scanf(“%d”,&n);
for (int i = 0; i<n; i++){
lệnh, kể cả hàm main. printf(“Nhap x(%d) = ”,i);
- Dùng toán tử ‘::’ nếu có biến cục bộ trùng scanf(“%f”,x+i);
}
tên. chuan = chuanMax(x,n);
printf(“||x|| = %f”,chuan);
}
CƠ CHẾ TRUYỀN THAM SỐ
Cú pháp gọi hàm là [tên hàm] ([các tham số]); #include <stdio.h>
float chuan;
1. Khi ta gọi hàm và truyền tham số vào, các giá int n;

trị sẽ ngay lập tức được gán cho các tham số float chuanMax(float x[], int n) {
int max = *x;
hình thức bên trong hàm. for (int i = 0; i < n; i++)

2. Khi gặp lệnh return hoặc khi hết hàm, các if (max < *(x+i))
max = *(x+i);
tham số hình thức và các biến cục bộ biến return max;

mất. Hàm sẽ được trả về một giá trị hoặc }


void main(){
mang giá trị null. float x[1000];
Ta có thể gọi hàm trả về trong hàm main rồi gán giá printf(“Nhap so chieu n = ”);
scanf(“%d”,&n);
trị tính toán được cho một biến khác. Đối với hàm for (int i = 0; i<n; i++){
không trả về, ta chỉ cần gọi tên hàm đó kèm các printf(“Nhap x(%d) = ”,i);
scanf(“%f”,x+i);
tham số truyền vào. Các tham số truyền vào sẽ }
thay đổi theo tính chất của hàm đó. chuan = chuanMax(x,n);
printf(“||x|| = %f”,chuan);
}
CƠ CHẾ TRUYỀN THAM SỐ
Có hai phương pháp truyền tham số vào hàm thường dùng trong C là:
- Truyền tham trị
- Truyền tham chiếu (tham số)

Tham trị Tham chiếu

- Gán giá trị của biến truyền vào cho - Gán địa chỉ của biến truyền vào cho
tham số hình thức (copy). tham số hình thức (con trỏ).
- Mọi thay đổi liên quan đến tham số - Mọi thay đổi liên quan đến tham số
hình thức không ảnh hưởng lên biến hình thức đều ảnh hưởng lên biến
truyền vào. truyền vào.

void swap(int x, int y){ void swap(int *x, int *y){


int tam = x; int tam = *x;
x = y; *x = *y;
y = tam; *y = tam;
} }
03
MỘT SỐ VÍ DỤ
swap(int x, int y)
Ví dụ: Viết hàm hoán đổi giá trị hai số x và y với x và y được nhập từ bàn phím

Tham trị Tham chiếu (con trỏ) Tham chiếu (truyền địa chỉ)

#include <stdio.h> #include <stdio.h> #include <stdio.h>


void hoanVi(float x, float y){ void hoanVi(float *x, float void hoanVi(float &x, float &y){
int z; *y){ int z;
z= x; int z; z= x;
x= y; z= *x; x=y;
y= z; *x= *y; y= z;
} *y= z; }
int main() } int main()
{ int main() {
float x, y; { float x, y;
printf("Nhap 2 so x,y: "); float x, y; printf("Nhap 2 so x,y: ");
scanf("%f %f",&x,&y); printf("Nhap 2 so x,y: "); scanf("%f %f",&x,&y);
hoanVi(x, y); scanf("%f %f",&x,&y); hoanVi(x,y);
printf("Sau khi hoan v, x=%f hoanVi(&x,&y) ; printf("Sau khi hoan v, x=%f
y=%f", x, y) ; printf("Sau khi hoan v, x=%f y=%f", x, y) ;
return 0; y=%f", x, y) ; return 0;
return 0;
} => Kết thúc hàm con giá trị } => PP sử dụng tốt hơn khi áp dụng
không đổi } => Áp dụng cho C và cả C++ cho C++
Ví dụ:
Viết chương trình nhập vào 3 giá trị
kiểu thực (float/ double).
Tìm giá trị lớn nhất, yêu cầu:
- Chương trình dạng hàm
- Không được dùng biến toàn
cục
- Được phép dùng biến a,b,c
DEBUG VÀ XEM LỖI

Code các chương trình dưới đây theo thứ tự từ 1, sửa lỗi lần lượt
theo hình 2, 3, 4 vào IDE rồi debug để kiểm tra có điều gì bất thường
không? So sánh sự khác nhau giữa các hình.

Đề bài: Nhập hai số nguyên khác nhau từ bàn phím, xuất ra số


lớn hơn.
BÀI TẬP TỰ LUYỆN
Viết các hàm thực hiện các chức năng sau:
1. Tính tổng, hiệu, tích, thương của hai số thực.
2. Hàm P(n) giai thừa của n (n nguyên dương và 0! = 1)
3. Hàm P(n;k) tính chỉnh hợp chập k của n phần tử (n >= k > 0)
4. Hàm C(n;k) tính tổ hợp chập k của n phần tử (n >= k > 1)
5. Hàm giải phương trình bậc 1.
6. Hàm giải phương trình bậc 2.
7. Hàm nhập, xuất chuỗi.
21
MỞ RỘNG
TỔ CHỨC HÀM VỚI MÃ NGUỒN
Trong các chương trình phần mềm ngôn ngữ C có hỗ trợ nhiều loại tập tin mã
nguồn:
• Các tập tin có phần mở rộng ‘.h’: mô tả giao tiếp lập trình, chứa các lệnh khai
báo hàm và các hằng số
• Các tập tin có phần mở rộng ‘.cpp’ hay ‘.c’: cài đặt mã nguồn, chứa chi tiết
đoạn mã của các hàm đã khai báo trong phần mô tả giao tiếp. Cần dùng từ khóa
‘include’ để tham chiếu tới phần mô tả giao tiếp
TỔ CHỨC HÀM VỚI MÃ NGUỒN
TỔ CHỨC HÀM VỚI MÃ
NGUỒN
TỔ CHỨC HÀM VỚI MÃ NGUỒN
•HÀM CÓ THAM
SỐ MẶC ĐỊNH
•Mục tiêu nhằm giản lược Dòng Mô tả
các hàm trùng tên (nếu cần
thiết) 1 #include <stdio.h>
Dòng Mô tả
•Hàm round 2 #include <math.h>
3 double round(double, int=0); 11 void main(){
• Thay vì viết hai hàm ta
dùng một hàm có tham 12 double a = 10.237;
4 double round(double x, int n){
số mặc định 14 double kq1 = round(a, 2);
5 double kq, s = pow(10, n);
• Tham số mặc định là 15 double kq2 = round(a);
6 x*=s; 16 printf(“kq1 = %lf”, kq1);
tham số nếu ta không
truyền giá trị nó sẽ có 7 if(x >= 0) kq = floor(x + 0.5)/s; 17 printf(“kq2 = %lf”, kq2);
giá trị mặc định trước 8 else kq = -floor(-x + 0.5)/s; 18 }

•Ví dụ 9 return kq;


• double round(double, int 10 }
= 0)
•HÀM CÓ THAM SỐ
KIỂU
• Mục tiêu nhằm hỗ
trợ viết các hàm
độc lập kiểu dữ liệu
• Hàm swap:
• Hàm hay sử dụng
• Cần viết chồng
nhiều hàm khi thay
đổi kiểu dữ liệu
•HÀM CÓ THAM SỐ LÀ
HÀM
•Ngôn ngữ C/C++ cho phép ta cài
đặt một hàm có tham số là hàm
•Điều này làm tăng tính linh hoạt và Dòng Mô tả Dòng Mô tả
tổ chức hơn cho chương trình
10 int Dem(int a, int KT(int)){
•Xét ví dụ viết hàm đếm 1 #include <stdio.h>
theo yêu cầu 11 int tmp, count = 0;
2 int Dem(int, int KT(int));
12 do{
• DemTheoYeuCau(long, 3 int KTSNT(int);
13 tmp = a%10; a = a/10;
int KiemTra(int)): sẽ 4 int KTSNT(int n){ 14 if(KT(tmp) == 1)count++;
đếm xem các kí số có
5 if(n == 1 || n == 0) return 0; 15 }while(a!=0);
thỏa hàm KiemTra hay
16 return count;
không: 6 for(int i = 2; i < n; i++)
17 }
• Ví dụ: 7 if(n % i == 0) return 0; 18 void main(){
• 1239 có 2 kí số nguyên 8 return 1; 19 int a = 1239;
tố nếu KiemTra là hàm
KiemTraSNT 9 } 20 int d = Dem(a, KTSNT);
• 1239 có 3 kí số lẻ nếu 21 printf(“d = %d\n”, d);
KiemTra là hàm 22 }
KiemTraSoLe
Dòng Mô tả
viết hàm xét phương trình
ax + b = 0 1 #include <stdio.h>
2 #include <math.h>
3 #define NoSolution 0
4 #define Undetermined -1
5 int EqualDeg1(double a, double b, double& x){
6 int nSolution;
7 if(a!=0){ x = -b/a; nSolution = 1; }
8 else{
9 x = 0;
10 if(b == 0) nSolution = Undetermined;
11 else nSolution = NoSolution;
12 }
13 return nSolution; }
Dòng Mô tả
viết hàm tính nghiệm phương
trình bậc hai một ẩn 1 void EquaDeg2(double a, double b, double c, double& x1, double& x2, int&
ax2 + bx + c = 0 sn){
2 double delta, sqrtDelta;
3 delta = b*b – 4*a*c;
4 if(delta > 0){
5 sn = 2; sqrtDelta = sqrt(delta);
6 x1 = (-b + sqrtDelta)/(2*a); x2 = (-b - sqrtDelta)/(2*a);
7 }
8 else if(delta == 0){
9 sn = 1; x1 = x2 = (-b)/(2*a);
10 }
11 else {sn = 0;}
12 }

Hàm dùng void


Dòn Mô tả
g
viết hàm tính nghiệm phương
1 int EquaDeg2(double a, double b, double c, double&
trình bậc hai một ẩn x1, double& x2){
ax2 + bx + c = 0 2 double delta, sqrtDelta; int sn ;
3 delta = b*b – 4*a*c;
4 if(delta > 0){
5 sn = 2; sqrtDelta = sqrt(delta);
6 x1 = (-b + sqrtDelta)/(2*a); x2 = (-b -
sqrtDelta)/(2*a);
7 }
8 else if(delta == 0){
9 sn = 1; x1 = x2 = (-b)/(2*a);
10 }
11 else {sn = 0;}
12 return sn;
13 }

Hàm dùng
return
Dòng Mô tả
viết hàm tính nghiệm phương
trình bậc hai một ẩn 1 …
ax2 + bx + c = 0 2 int EqualDeg2(double a, double b, double c, double& x1, double&
x2){

3 int nSolution; x1 = x2 = 0;
4 if(a==0) nSolution = EqualDeg1(b, c, x1);
5 else{
6 double delta = b*b – 4*a*c, two_a = 2*a;
7 if(delta < 0) nSolution = NoSolution;
8 else if(delta == 0) { x1 = x2 = -b/two_a; nSolution = 1;}
9 else{
10 double sqrtDelta = sqrt(delta);

Cách khác 11 x1 = (-b-sqrtDelta)/two_a; x2 = (-b+sqrtDelta)/two_a;


12 nSolution = 2; }}
13 return nSolution;}
Dòng Mô tả
viết hàm kiểm tra số nguyên tố
1 int isPrime(long n){
2 int Prime;
3 if(n < 0) n = -n;
4 if(n == 0) Prime = 1;
5 else if(n == 1) Prime = 0;
6 else{
7 long i = 2; Prime = 1;
8 while(i <= sqrt(n)){
9 if(n % i == 0) { Prime = 0; break; }

10 }
11 i++;
12 }
13 return Prime;}
Viết hàm liệt kê các số nguyên tố
nhỏ hơn n

Dòng Mô tả
1 void PrimeListing(long n){
2 for(long i = 2; i <= n; i++){
3 if(isPrime(i)) printf(“%ld\n”, i);
4 }}
Viết hàm in ra số nguyên tố thứ n

Dòng Mô tả
1 void GetPrime(long n){
2 long p = 2, c = 1, nextNum = 3;
3 while(c < n){
4 if(isPrime(nextNum)){p = nextNum; c++; }
5 nextNum+=2;
6 }}
Dòn Mô tả
g
viết hàm xét phương trình
ax4+bx2+c = 0 1 …
2 int EqualQuartic(double a, double b, double c, double& x1, double& x2,
double& x3, double& x4){

Đặt y = x2 ⇒ ay2 + by + c = 0 (2) 3 int nSolution, nSol1, nSol2; double y1, y2; x1 = x2 = x3 = x4 = 0;
Nếu (2) vô nghiệm thì (1) vô 4 nSol1 = EqualDeg2(a, b, c, y1, y2);
nghiệm
Nếu (2) có 1 nghiệm y = y1 = x2 ⇔ 5 switch(nSol1){
x2 – y1 = 0 (3): 6 case NoSolution: case Undetermined: nSolution = nSol1; break;
Giải (3) tìm x1 & x2
7 case 1: nSolution = EqualDeg2(1, 0, -y1, x1, x2); break;
Nếu (2) có 2 nghiệm
y = y1 = x2 ⇔ x2 – y1 = 0 (4): Giải (4) tìm x 8 case 2: nSol2 = EqualDeg2(1, 0, -y1, x1, x2);
y = y2 = x2 ⇔ x2 – y2 = 0 (5): Giải (5) tìm x
9 switch(nSol2){
10 case NoSolution: nSolution = EqualDeg2(1, 0, -y2, x1, x2); break;
•Lưu ý: không cần kiểm tra
điều kiện y ≥ 0 vì việc đó sẽ 11 case 1: nSolution = 1 + EqualDeg2(1, 0, -y2, x2, x3); break;
do EqualDeg2 xử lý 12 case 2: nSolution = 2 + EqualDeg2(1, 0, -y2, x3, x4); break;
13 }} return nSolution; }
viết hàm giải gần đúng Dòng Mô tả
phương trình f(x)=0
1 double f(double x){
2 return pow(x, 9) + x + 1;//f(x)=x9+x+1
3 }
4 void Solve(int& x){//Tìm x để f(x) = 0
5 const double epsilon = 0.000000001;
6 double left = -1, right = 0; // a=-1, b=0

7 while(right – left > epsilon){


8 double mid = (left + right)/2;
9 if(f(left)*f(mid) < 0) right = mid;
10 else left = mid;
11 }
12 x = (left + right)/2;
13 }
THE END

You might also like