Bài 10.3: Bài tập thực hành hàm trả về trong C++

Bài 10.3: Bài tập thực hành hàm trả về trong C++
Chào mừng các bạn quay trở lại với series học lập trình C++ của FullhouseDev! Ở các bài trước, chúng ta đã làm quen với khái niệm cơ bản về hàm, cách khai báo, định nghĩa và sử dụng các hàm void
- những hàm thực hiện một tác vụ nào đó nhưng không trả lại một giá trị cụ thể cho nơi gọi nó.
Hôm nay, chúng ta sẽ tiến thêm một bước quan trọng: tìm hiểu về hàm trả về giá trị (functions returning values). Đây là một bước đột phá giúp các hàm của chúng ta trở nên mạnh mẽ và linh hoạt hơn rất nhiều. Thay vì chỉ thực hiện hành động, hàm trả về cho phép chúng ta tính toán, xử lý dữ liệu và gửi kết quả đó ngược lại cho phần còn lại của chương trình sử dụng.
Hãy cùng đi sâu vào cách chúng hoạt động và thực hành qua các ví dụ nhé!
Hàm Trả Về Giá Trị Hoạt Động Như Thế Nào?
Khác với hàm void
, hàm trả về giá trị được khai báo với một kiểu dữ liệu trả về cụ thể (ví dụ: int
, double
, bool
, string
,...). Kiểu dữ liệu này quy định loại giá trị mà hàm sẽ gửi trả lại sau khi hoàn thành công việc của mình.
Cú pháp cơ bản khi định nghĩa một hàm trả về:
kieu_du_lieu_tra_ve ten_ham(tham_so_1, tham_so_2, ...) {
// Các câu lệnh xử lý logic của hàm
// ...
// Câu lệnh return để trả về giá trị
return gia_tri_can_tra_ve;
}
Điểm mấu chốt ở đây là câu lệnh return
. Khi chương trình gặp câu lệnh return gia_tri;
bên trong hàm, nó sẽ thực hiện các việc sau:
- Tính toán hoặc xác định
gia_tri_can_tra_ve
. - Dừng ngay lập tức việc thực thi các câu lệnh còn lại trong hàm đó.
- Gửi
gia_tri_can_tra_ve
này trở lại cho nơi đã gọi hàm. Kiểu dữ liệu củagia_tri_can_tra_ve
phải tương thích vớikieu_du_lieu_tra_ve
đã khai báo cho hàm.
Nơi gọi hàm có thể lưu trữ giá trị trả về này vào một biến, hoặc sử dụng nó trực tiếp trong một biểu thức hoặc câu lệnh khác.
Hãy bắt đầu với một ví dụ đơn giản nhất: một hàm tính tổng hai số nguyên.
Ví Dụ 1: Hàm Tính Tổng Hai Số Nguyên
Chúng ta muốn một hàm nhận hai số nguyên a
và b
, sau đó trả về kết quả của a + b
.
#include <iostream>
int add(int a, int b) {
return a + b;
}
int main() {
int s1 = 10;
int s2 = 20;
int kq = add(s1, s2);
cout << "Tong cua " << s1 << " va " << s2 << " la: " << kq << endl;
cout << "Tong cua 5 va 7 la: " << add(5, 7) << endl;
return 0;
}
Output:
Tong cua 10 va 20 la: 30
Tong cua 5 va 7 la: 12
Giải thích code:
- Dòng
int add(int a, int b)
: Khai báo một hàm tên làadd
. Nó nhận hai tham số kiểuint
làa
vàb
. Quan trọng nhất,int
đứng trước tên hàmadd
cho biết hàm này sẽ trả về một giá trị có kiểuint
. - Dòng
return a + b;
: Đây là câu lệnhreturn
. Nó tính tổng củaa
vàb
, sau đó gửi kết quả này trở lại nơi hàmadd
được gọi. - Dòng
int kq = add(s1, s2);
trongmain
: Khiadd(s1, s2)
được gọi, hàmadd
thực thi, tính10 + 20 = 30
. Giá trị30
này được trả về và gán vào biếnkq
kiểuint
trong hàmmain
. - Dòng
cout << add(5, 7) << endl;
: Minh họa việc sử dụng trực tiếp giá trị trả về của hàmadd
mà không cần lưu vào biến trung gian.
Ví dụ này cho thấy sự trao đổi thông tin hiệu quả giữa hàm add
và hàm main
thông qua giá trị trả về. Hàm add
làm công việc tính toán, và main
nhận kết quả để sử dụng tiếp.
Ví Dụ 2: Tính Diện Tích Hình Tròn
Hãy viết một hàm nhận bán kính hình tròn (kiểu double
) và trả về diện tích của nó (kiểu double
).
#include <iostream>
#include <cmath>
double tinh_dt(double r) {
if (r < 0) {
cerr << "Loi: Ban kinh khong the am!" << endl;
return -1.0;
}
return M_PI * r * r;
}
int main() {
double r1 = 5.0;
double r2 = 10.0;
double r3 = -2.0;
double dt1 = tinh_dt(r1);
if (dt1 >= 0) {
cout << "Dien tich hinh tron ban kinh " << r1 << " la: " << dt1 << endl;
}
cout << "Dien tich hinh tron ban kinh " << r2 << " la: " << tinh_dt(r2) << endl;
double dt3 = tinh_dt(r3);
if (dt3 < 0) {
cout << "Khong the tinh dien tich voi ban kinh am." << endl;
}
return 0;
}
Output:
Loi: Ban kinh khong the am!
Dien tich hinh tron ban kinh 5 la: 78.5398
Dien tich hinh tron ban kinh 10 la: 314.159
Loi: Ban kinh khong thể am!
Khong the tinh dien tich voi ban kinh am.
Giải thích code:
- Dòng
double tinh_dt(double r)
: Hàm này nhận một tham sốr
kiểudouble
và được khai báo sẽ trả về một giá trị kiểudouble
(diện tích). - Câu lệnh
return M_PI * r * r;
: Tính toán diện tích và gửi kết quả về. - Phần kiểm tra
if (r < 0)
: Đây là một ví dụ về cách xử lý các trường hợp ngoại lệ. Nếu bán kính âm, chúng ta thông báo lỗi và trả về một giá trị đặc biệt (-1.0
) để báo hiệu cho nơi gọi biết rằng có vấn đề xảy ra, thay vì trả về một kết quả tính toán sai. - Trong
main
, chúng ta gọitinh_dt(r1)
và lưu kết quả vàodt1
. Sau đó, chúng ta kiểm tra giá trị củadt1
trước khi in ra, minh họa cách xử lý giá trị trả về, kể cả các giá trị báo lỗi.
Ví Dụ 3: Kiểm Tra Số Chẵn/Lẻ (Sử Dụng bool
)
Hàm trả về kiểu bool
rất phổ biến khi chúng ta muốn kiểm tra một điều kiện nào đó và nhận kết quả là true
hoặc false
.
#include <iostream>
bool la_chan(int n) {
return n % 2 == 0;
}
int main() {
int n1 = 14;
int n2 = 7;
if (la_chan(n1)) {
cout << n1 << " la so chan." << endl;
} else {
cout << n1 << " khong la so chan." << endl;
}
if (la_chan(n2)) {
cout << n2 << " la so chan." << endl;
} else {
cout << n2 << " khong la so chan." << endl;
}
cout << boolalpha;
cout << "So 10 co phai so chan? " << la_chan(10) << endl;
cout << "So 9 co phai so chan? " << la_chan(9) << endl;
return 0;
}
Output:
14 la so chan.
7 khong la so chan.
So 10 co phai so chan? true
So 9 co phai so chan? false
Giải thích code:
- Dòng
bool la_chan(int n)
: Hàmla_chan
nhận mộtint
và được khai báo trả về mộtbool
. - Dòng
return n % 2 == 0;
: Biểu thứcn % 2 == 0
cho kết quả làtrue
nếun
chia hết cho 2, vàfalse
nếu không. Giá trịtrue
hoặcfalse
này được trả về bởi hàm. - Trong
main
, giá trịbool
trả về từla_chan
được sử dụng trực tiếp trong điều kiện của câu lệnhif
. Nếula_chan(n1)
trả vềtrue
, khối lệnh củaif
được thực thi. Nếu trả vềfalse
, khối lệnh củaelse
được thực thi. boolalpha
là một "manipulator" (bộ điều khiển) củacout
giúp chúng ta hiển thị giá trịbool
dưới dạng từ khóatrue
hoặcfalse
thay vì số1
hoặc0
.
Ví Dụ 4: Tìm Số Lớn Nhất (Sử Dụng max
)
Một hàm trả về cũng có thể trả về một trong các giá trị đầu vào sau khi xử lý logic.
#include <iostream>
#include <algorithm>
int lay_max(int a, int b) {
return max(a, b);
}
int main() {
int x = 100;
int y = 200;
int lon_nhat = lay_max(x, y);
cout << "So lon nhat giua " << x << " va " << y << " la: " << lon_nhat << endl;
cout << "So lon nhat giua 50 va 30 la: " << lay_max(50, 30) << endl;
return 0;
}
Output:
So lon nhat giua 100 va 200 la: 200
So lon nhat giua 50 va 30 la: 50
Giải thích code:
- Dòng
int lay_max(int a, int b)
: Hàm này nhận haiint
và trả vềint
(số lớn hơn). - Dòng
return max(a, b);
: Đây là cách dùng hàm có sẵnmax
để tìm giá trị lớn nhất giữa hai số.max
cũng là một hàm trả về giá trị. Nó tính toán và trả về số lớn hơn giữaa
vàb
. - Trong
main
, giá trị trả về từlay_max
được gán vào biếnlon_nhat
hoặc sử dụng trực tiếp.
Ví Dụ 5: Hàm Trả Về Chuỗi (string
)
Hàm cũng có thể trả về các kiểu dữ liệu phức tạp hơn như chuỗi ký tự (string
).
#include <iostream>
#include <string>
string tao_loi_chao(const string& ten) {
return "Xin chao, " + ten + "!";
}
int main() {
string ten = "Doc gia C++";
string loi_chao = tao_loi_chao(ten);
cout << loi_chao << endl;
cout << tao_loi_chao("FullhouseDev") << endl;
return 0;
}
Output:
Xin chao, Doc gia C++!
Xin chao, FullhouseDev!
Giải thích code:
- Dòng
string tao_loi_chao(const string& ten)
: Hàm này nhận mộtstring
và được khai báo sẽ trả về mộtstring
. Việc sử dụngconst string&
cho tham số là một kỹ thuật tối ưu hiệu năng khi truyền chuỗi lớn, chúng ta sẽ tìm hiểu kỹ hơn sau, tạm thời hiểu nó vẫn nhận vào một chuỗi. - Dòng
return "Xin chao, " + ten + "!";
: Nối chuỗi "Xin chao, " với giá trị của tham sốten
và chuỗi "!" rồi trả về kết quả là mộtstring
mới. - Trong
main
, chuỗi trả về được gán vào biếnloi_chao
hoặc in trực tiếp.
Ví Dụ 6: Kết Hợp Nhiều Hàm Trả Về
Sức mạnh thực sự của hàm trả về nằm ở khả năng xâu chuỗi các thao tác xử lý. Kết quả trả về của một hàm có thể trở thành đầu vào cho hàm khác.
#include <iostream>
int nhan_doi(int n) {
cout << "* Buoc 1: Nhan " << n << " voi 2..." << endl;
return n * 2;
}
int cong_nam(int n) {
cout << "* Buoc 2: Cong " << n << " voi 5..." << endl;
return n + 5;
}
int main() {
int bd = 10;
int kq1 = nhan_doi(bd);
int kqf = cong_nam(kq1);
cout << "Gia tri ban dau: " << bd << endl;
cout << "Ket qua cuoi cung: " << kqf << endl;
cout << "\n--- Goi truc tiep ---" << endl;
cout << "Ket qua khi goi truc tiep: " << cong_nam(nhan_doi(bd)) << endl;
return 0;
}
Output:
* Buoc 1: Nhan 10 voi 2...
* Buoc 2: Cong 20 voi 5...
Gia tri ban dau: 10
Ket qua cuoi cung: 25
--- Goi truc tiep ---
* Buoc 1: Nhan 10 voi 2...
* Buoc 2: Cong 20 voi 5...
Ket qua khi goi truc tiep: 25
Giải thích code:
- Chúng ta có hai hàm đơn giản,
nhan_doi
vàcong_nam
, cả hai đều nhận mộtint
và trả về mộtint
. - Trong
main
,nhan_doi(bd)
được gọi đầu tiên. Nó thực hiện công việc của mình (nhân 10 với 2) và trả về20
. Giá trị20
này được lưu vào biếnkq1
. - Tiếp theo,
cong_nam(kq1)
được gọi. Lưu ý rằngkq1
chính là giá trị20
được trả về từ hàm trước. Hàmcong_nam
nhận20
, thực hiện công việc của mình (cộng 5), và trả về25
. Giá trị25
này được lưu vàokqf
. - Việc gọi lồng nhau
cong_nam(nhan_doi(bd))
minh họa rõ ràng hơn cách giá trị trả về của hàm bên trong (nhan_doi
) trở thành tham số đầu vào cho hàm bên ngoài (cong_nam
).
Cách tiếp cận này cho phép chúng ta xây dựng các chương trình phức tạp bằng cách kết hợp các hàm nhỏ, chuyên biệt. Mỗi hàm thực hiện một nhiệm vụ cụ thể và trả về kết quả, kết quả đó lại được sử dụng làm đầu vào cho nhiệm vụ tiếp theo.
- Để tạo hàm trả về, hãy khai báo nó với một kiểu dữ liệu trả về (khác
void
). - Sử dụng câu lệnh
return
để gửi giá trị từ hàm về nơi gọi. - Giá trị trả về phải tương thích với kiểu dữ liệu trả về đã khai báo.
- Nơi gọi hàm có thể lưu trữ giá trị trả về vào một biến hoặc sử dụng trực tiếp.
- Hàm trả về cho phép xâu chuỗi các phép tính và làm cho code của chúng ta ngăn nắp, dễ đọc và dễ bảo trì hơn.
Comments