Bài 10.1: Hàm cơ bản trong C++

Chào mừng trở lại với series blog về C++! Hôm nay chúng ta sẽ cùng nhau đi sâu vào một trong những khái niệm cốt lõiquan trọng nhất trong lập trình: hàm (functions). Hàm giống như những "cỗ máy" nhỏ thực hiện một công việc cụ thể, giúp code của chúng ta gọn gàng, dễ đọc và dễ tái sử dụng.

Trong bài này, chúng ta sẽ không chỉ tìm hiểu về cấu trúc của hàm mà còn thực hành ngay với những ví dụ thật đơn giản, giúp bạn làm quen với việc định nghĩa và gọi hàm trong C++.

Tại sao cần dùng Hàm?

Hãy tưởng tượng bạn cần thực hiện một công việc giống nhau ở nhiều vị trí khác nhau trong chương trình, ví dụ như tính diện tích hình tròn hay in ra một thông báo đặc biệt. Nếu không có hàm, bạn sẽ phải viết đi viết lại cùng một đoạn code. Điều này rất tốn thời gian, dễ gây lỗi và khó bảo trì.

Hàm ra đời để giải quyết vấn đề này:

  • Tái sử dụng: Viết code một lần, gọi dùng ở nhiều nơi.
  • Module hóa: Chia chương trình lớn thành các phần nhỏ hơn, độc lập, dễ quản lý.
  • Dễ đọc: Code được chia thành các hàm với tên gọi ý nghĩa giúp chương trình dễ hiểu hơn.
  • Dễ sửa lỗi: Khi có lỗi, bạn có thể khoanh vùng tìm lỗi trong từng hàm cụ thể.

Cấu trúc cơ bản của một Hàm trong C++

Một hàm trong C++ có cấu trúc như sau:

kieu_tra_ve ten_ham(danh_sach_tham_so) {
    // Thân hàm (Function Body)
    // Các câu lệnh thực hiện công việc của hàm

    // Nếu kieu_tra_ve KHÔNG phải là void, cần có lệnh return
    // return gia_tri_tra_ve;
}
  • kieu_tra_ve: Kiểu dữ liệu của giá trị mà hàm sẽ trả về sau khi thực hiện xong (ví dụ: int, double, string, bool). Nếu hàm không trả về giá trị nào, chúng ta dùng từ khóa void.
  • ten_ham: Tên bạn đặt cho hàm, nên tuân thủ quy tắc đặt tên biến và thể hiện rõ chức năng của hàm.
  • danh_sach_tham_so: (Tùy chọn) Danh sách các biến mà hàm cần nhận để thực hiện công việc của nó. Mỗi tham số gồm kiểu dữ liệu và tên biến, cách nhau bởi dấu phẩy (ví dụ: int a, double b). Nếu hàm không nhận tham số nào, danh sách này để trống ().
  • Thân hàm: Khối lệnh nằm trong cặp dấu ngoặc nhọn {}. Đây là nơi chứa logic thực hiện công việc của hàm.
  • return: (Nếu kiểu trả về không phải là void) Lệnh này dùng để kết thúc hàm và trả về một giá trị có kiểu dữ liệu giống với kieu_tra_ve.

Thực hành 1: Hàm đơn giản nhất (Không tham số, không trả về)

Đây là loại hàm chỉ thực hiện một hành động nào đó mà không cần dữ liệu đầu vào và không trả lại kết quả.

#include <iostream>
using namespace std;

void say_hello() {
    cout << "Xin chao tu ham say_hello!" << endl;
}

int main() {
    say_hello();
    say_hello();
    say_hello();
    return 0;
}
Xin chao tu ham say_hello!
Xin chao tu ham say_hello!
Xin chao tu ham say_hello!

Giải thích:

  • Hàm say_hellovoid là kiểu trả về, tức là nó không trả về bất kỳ giá trị nào.
  • Nó không có tham số nào trong dấu ngoặc đơn ().
  • Thân hàm chỉ chứa một lệnh in ra màn hình.
  • Trong hàm main, chúng ta gọi hàm say_hello() bằng cách viết tên hàm theo sau là dấu ngoặc () và kết thúc bằng dấu ;. Mỗi lần gọi, code trong thân hàm say_hello sẽ được thực thi.

Thực hành 2: Hàm có tham số (Nhận dữ liệu đầu vào)

Hàm có thể nhận dữ liệu từ bên ngoài để thực hiện công việc của mình. Dữ liệu này được truyền vào qua các tham số.

#include <iostream>
#include <string>
using namespace std;

void print_message_n_times(string s, int n) {
    for (int i = 0; i < n; ++i) {
        cout << s << endl;
    }
}

int main() {
    print_message_n_times("Hello, C++!", 3);
    print_message_n_times("Day la bai tap ve ham.", 2);
    return 0;
}
Hello, C++!
Hello, C++!
Hello, C++!
Day la bai tap ve ham.
Day la bai tap ve ham.

Giải thích:

  • Hàm print_message_n_times nhận hai tham số: string textint n.
  • Kiểu trả về vẫn là void vì hàm này chỉ thực hiện hành động in ấn chứ không trả lại giá trị nào.
  • Khi gọi hàm trong main, chúng ta truyền vào các đối số (arguments) là "Hello, C++!"3, hoặc "Day la bai tap ve ham."2. Các đối số này sẽ được gán cho các tham số tương ứng textn bên trong hàm.
  • Thân hàm sử dụng vòng lặp for để in chuỗi text ra n lần.

Thực hành 3: Hàm có trả về giá trị

Hàm có thể tính toán hoặc xử lý dữ liệu và trả lại một kết quả để chúng ta sử dụng tiếp trong phần còn lại của chương trình.

#include <iostream>
using namespace std;

int tong(int a, int b) {
    return a + b;
}

double dtHinhTron(double r) {
    const double PI = 3.14159;
    return PI * r * r;
}

int main() {
    int n1 = 10;
    int n2 = 25;
    int ketQua = tong(n1, n2);

    cout << "Tong cua " << n1 << " va " << n2 << " la: " << ketQua << endl;
    cout << "Tong cua 50 va 70 la: " << tong(50, 70) << endl;

    double r = 5.0;
    double dt = dtHinhTron(r);

    cout << "Dien tich hinh tron ban kinh " << r << " la: " << dt << endl;

    return 0;
}
Tong cua 10 va 25 la: 35
Tong cua 50 va 70 la: 120
Dien tich hinh tron ban kinh 5 la: 78.53975

Giải thích:

  • Hàm add_numbers có kiểu trả về là int. Nó nhận hai tham số int a, int b.
  • Câu lệnh return sum; (hoặc return a + b;) tính toán tổng và "đưa" giá trị tổng đó ra ngoài.
  • Hàm calculate_area_circle có kiểu trả về là double, nhận tham số double radius và trả về kết quả tính toán kiểu double.
  • Trong hàm main, chúng ta gọi các hàm này và gán giá trị trả về của chúng cho các biến cùng kiểu (result_sum, area). Chúng ta cũng có thể sử dụng trực tiếp giá trị trả về của hàm trong biểu thức hoặc câu lệnh in.

Thực hành 4: Nguyên mẫu hàm (Function Prototype)

Đôi khi, bạn muốn gọi một hàm trước khi định nghĩa chi tiết nó (ví dụ: định nghĩa tất cả các hàm sau hàm main). Lúc này, bạn cần khai báo nguyên mẫu hàm để trình biên dịch biết về sự tồn tại của hàm đó.

#include <iostream>
using namespace std;

int tich(int x, int y);

int main() {
    int a = 8;
    int b = 9;

    int kq = tich(a, b);

    cout << "Tich cua " << a << " va " << b << " la: " << kq << endl;

    return 0;
}

int tich(int x, int y) {
    return x * y;
}
Tich cua 8 va 9 la: 72

Giải thích:

  • Dòng int multiply(int x, int y); là nguyên mẫu hàm. Nó chỉ bao gồm kiểu trả về (int), tên hàm (multiply) và danh sách kiểu tham số (int, int), kết thúc bằng dấu ;. Tên biến trong ngoặc (x, y) là tùy chọn trong nguyên mẫu, nhưng thường được thêm vào để dễ đọc.
  • Nguyên mẫu hàm này nằm trước hàm main, báo cho trình biên dịch biết rằng hàm multiply tồn tại và sẽ được định nghĩa ở đâu đó sau này.
  • Điều này cho phép chúng ta gọi multiply(num_a, num_b) bên trong main mà không gặp lỗi "hàm chưa được khai báo".
  • Phần định nghĩa chi tiết của hàm multiply nằm sau hàm main và chứa toàn bộ logic thực hiện công việc.

Các bài tập thực hành nhỏ

Hãy thử tự viết các hàm cho các công việc sau để luyện tập:

  1. Viết hàm is_positive nhận một số nguyên, trả về true nếu số đó dương, ngược lại trả về false.
  2. Viết hàm get_smaller nhận hai số double, trả về số nhỏ hơn trong hai số đó.
  3. Viết hàm print_greeting nhận một chuỗi tên và in ra lời chào "Xin chào, [Tên]!".

Comments

There are no comments at the moment.