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ẽ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:

  1. Tính toán hoặc xác định gia_tri_can_tra_ve.
  2. 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 đó.
  3. 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ủa gia_tri_can_tra_ve phải tương thích với kieu_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 ab, sau đó trả về kết quả của a + b.

#include <iostream> // Cho cout, endl

// Khai báo và định nghĩa hàm add
// Hàm này nhận hai số nguyên (int a, int b)
// và trả về một số nguyên (int)
int add(int a, int b) {
    // Tính tổng a + b và trả về kết quả đó
    return a + b;
}

int main() {
    int number1 = 10;
    int number2 = 20;

    // Gọi hàm add. Giá trị 30 được hàm add trả về
    // và được lưu trữ vào biến result.
    int result = add(number1, number2);

    // In kết quả ra màn hình
    cout << "Tong cua " << number1 << " va " << number2 << " la: " << result << endl;

    // Chúng ta cũng có thể sử dụng giá trị trả về trực tiếp
    cout << "Tong cua 5 va 7 la: " << add(5, 7) << endl;

    return 0;
}

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ểu intab. Quan trọng nhất, int đứng trước tên hàm add cho biết hàm này sẽ trả về một giá trị có kiểu int.
  • Dòng return a + b;: Đây là câu lệnh return. Nó tính tổng của ab, sau đó gửi kết quả này trở lại nơi hàm add được gọi.
  • Dòng int result = add(number1, number2); trong main: Khi add(number1, number2) được gọi, hàm add thực thi, tính 10 + 20 = 30. Giá trị 30 này được trả về và gán vào biến result kiểu int trong hàm main.
  • 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àm add 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> // Cho cout, endl
#include <cmath>    // Cho M_PI (hoặc bạn có thể tự định nghĩa PI)

// Khai báo và định nghĩa hàm calculateArea
// Nhận bán kính (double radius)
// Trả về diện tích (double)
double calculateArea(double radius) {
    // Kiểm tra bán kính không âm (optional nhưng là good practice)
    if (radius < 0) {
        cerr << "Loi: Ban kinh khong the am!" << endl;
        // Trả về một giá trị đặc biệt để báo hiệu lỗi, ví dụ -1.0
        return -1.0;
    }
    // Sử dụng hằng số PI từ cmath
    const double PI = M_PI;
    // Tính diện tích và trả về
    return PI * radius * radius;
}

int main() {
    double r1 = 5.0;
    double r2 = 10.0;
    double r3 = -2.0; // Ví dụ bán kính âm

    // Tính và in diện tích cho r1
    double area1 = calculateArea(r1);
    // Kiểm tra giá trị trả về để xem có lỗi không
    if (area1 >= 0) {
        cout << "Dien tich hinh tron ban kinh " << r1 << " la: " << area1 << endl;
    }

    // Tính và in diện tích cho r2
    cout << "Dien tich hinh tron ban kinh " << r2 << " la: " << calculateArea(r2) << endl;

    // Tính diện tích cho r3 - sẽ gặp lỗi
    double area3 = calculateArea(r3); // Hàm sẽ in thông báo lỗi và trả về -1.0
    if (area3 < 0) {
         cout << "Khong the tinh dien tich voi ban kinh am." << endl;
    }


    return 0;
}

Giải thích code:

  • Dòng double calculateArea(double radius): Hàm này nhận một tham số radius kiểu double và được khai báo sẽ trả về một giá trị kiểu double (diện tích).
  • Câu lệnh return PI * radius * radius;: Tính toán diện tích và gửi kết quả về.
  • Phần kiểm tra if (radius < 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ọi calculateArea(r1) và lưu kết quả vào area1. Sau đó, chúng ta kiểm tra giá trị của area1 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> // Cho cout, endl, boolalpha

// Khai báo và định nghĩa hàm isEven
// Nhận một số nguyên (int number)
// Trả về true nếu số đó là chẵn, false nếu là lẻ (bool)
bool isEven(int number) {
    // Số chẵn là số chia hết cho 2
    return number % 2 == 0;
}

int main() {
    int num1 = 14;
    int num2 = 7;

    // Gọi hàm isEven và sử dụng giá trị trả về trong câu lệnh if
    if (isEven(num1)) {
        cout << num1 << " la so chan." << endl;
    } else {
        cout << num1 << " khong la so chan." << endl;
    }

    // Gọi hàm isEven và sử dụng giá trị trả về trong câu lệnh if khác
    if (isEven(num2)) {
        cout << num2 << " la so chan." << endl;
    } else {
        cout << num2 << " khong la so chan." << endl;
    }

    // Có thể in trực tiếp giá trị bool trả về
    // boolalpha giúp in ra "true" hoặc "false" thay vì 1 hoặc 0
    cout << boolalpha; // Bật chế độ in bool dạng chữ

    cout << "So 10 co phai so chan? " << isEven(10) << endl;
    cout << "So 9 co phai so chan? " << isEven(9) << endl;

    return 0;
}

Giải thích code:

  • Dòng bool isEven(int number): Hàm isEven nhận một int và được khai báo trả về một bool.
  • Dòng return number % 2 == 0;: Biểu thức number % 2 == 0 cho kết quả là true nếu number chia hết cho 2, và false nếu không. Giá trị true hoặc false này được trả về bởi hàm.
  • Trong main, giá trị bool trả về từ isEven được sử dụng trực tiếp trong điều kiện của câu lệnh if. Nếu isEven(num1) trả về true, khối lệnh của if được thực thi. Nếu trả về false, khối lệnh của else được thực thi.
  • boolalpha là một "manipulator" (bộ điều khiển) của cout giúp chúng ta hiển thị giá trị bool dưới dạng từ khóa true hoặc false thay vì số 1 hoặc 0.

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> // Cho cout, endl
#include <algorithm> // Cho max

// Khai báo và định nghĩa hàm getMax
// Nhận hai số nguyên (int a, int b)
// Trả về số nguyên lớn hơn (int)
int getMax(int a, int b) {
    // Sử dụng hàm max từ thư viện <algorithm>
    // Hàm max(a, b) sẽ trả về a nếu a >= b, ngược lại trả về b
    return max(a, b);

    // Hoặc tự viết logic so sánh:
    // if (a > b) {
    //     return a;
    // } else {
    //     return b;
    // }
}

int main() {
    int x = 100;
    int y = 200;

    // Gọi hàm getMax và lưu kết quả
    int maximum = getMax(x, y);
    cout << "So lon nhat giua " << x << " va " << y << " la: " << maximum << endl;

    cout << "So lon nhat giua 50 va 30 la: " << getMax(50, 30) << endl;

    return 0;
}

Giải thích code:

  • Dòng int getMax(int a, int b): Hàm này nhận hai int 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ẵn max để 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ữa ab.
  • Trong main, giá trị trả về từ getMax được gán vào biến maximum 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> // Cho cout, endl
#include <string>   // Cho string

// Khai báo và định nghĩa hàm createGreeting
// Nhận một tên (string name)
// Trả về một chuỗi chào mừng (string)
string createGreeting(const string& name) { // Sử dụng const reference cho hiệu quả
    // Tạo chuỗi chào mừng và trả về
    return "Xin chao, " + name + "!";
}

int main() {
    string user_name = "Doc gia C++";

    // Gọi hàm createGreeting và lưu chuỗi trả về
    string greeting_message = createGreeting(user_name);
    cout << greeting_message << endl;

    // Gọi hàm và in trực tiếp chuỗi trả về
    cout << createGreeting("FullhouseDev") << endl;

    return 0;
}

Giải thích code:

  • Dòng string createGreeting(const string& name): Hàm này nhận một string và được khai báo sẽ trả về một string. Việc sử dụng const 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, " + name + "!";: Nối chuỗi "Xin chao, " với giá trị của tham số name và chuỗi "!" rồi trả về kết quả là một string mới.
  • Trong main, chuỗi trả về được gán vào biến greeting_message 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> // Cho cout, endl

// Hàm nhân đôi giá trị
int multiplyByTwo(int num) {
    cout << "* Buoc 1: Nhan " << num << " voi 2..." << endl;
    return num * 2;
}

// Hàm cộng thêm 5
int addFive(int num) {
     cout << "* Buoc 2: Cong " << num << " voi 5..." << endl;
    return num + 5;
}

int main() {
    int start_value = 10;

    // Gọi hàm multiplyByTwo, nhận kết quả (20)
    int step1_result = multiplyByTwo(start_value); // step1_result = 20

    // Gọi hàm addFive với kết quả từ bước 1 (20), nhận kết quả (25)
    int final_result = addFive(step1_result);      // final_result = 25

    cout << "Gia tri ban dau: " << start_value << endl;
    cout << "Ket qua cuoi cung: " << final_result << endl;

    cout << "\n--- Goi truc tiep ---" << endl;
    // Hoặc gọi lồng nhau: addFive(multiplyByTwo(start_value))
    // multiplyByTwo(10) trả về 20
    // addFive(20) trả về 25
    cout << "Ket qua khi goi truc tiep: " << addFive(multiplyByTwo(start_value)) << endl;


    return 0;
}

Giải thích code:

  • Chúng ta có hai hàm đơn giản, multiplyByTwoaddFive, cả hai đều nhận một int và trả về một int.
  • Trong main, multiplyByTwo(start_value) đượ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ến step1_result.
  • Tiếp theo, addFive(step1_result) được gọi. Lưu ý rằng step1_result chính là giá trị 20 được trả về từ hàm trước. Hàm addFive nhận 20, 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ào final_result.
  • Việc gọi lồng nhau addFive(multiplyByTwo(start_value)) minh họa rõ ràng hơn cách giá trị trả về của hàm bên trong (multiplyByTwo) trở thành tham số đầu vào cho hàm bên ngoài (addFive).

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ễ đọcdễ bảo trì hơn.

Comments

There are no comments at the moment.