0

Chương trình con trong C++

đã đăng vào 31, Tháng 7, 2026, 22:51

CHƯƠNG TRÌNH CON TRONG C++

Chào các bạn học sinh và lập trình viên! Khi giải quyết các bài toán lập trình quy mô lớn hoặc tham gia các cuộc thi Học sinh giỏi Tin học trên hệ thống Sen Code Online Judge!, mã nguồn chương trình của chúng ta sẽ ngày một dài hơn. Nếu tất cả các câu lệnh đều viết chung vào hàm main, chương trình sẽ trở nên cực kỳ rối rắm, khó phát hiện lỗi và gặp khó khăn khi cần tái sử dụng một đoạn code nhiều lần.

Để giải quyết vấn đề này, các ngôn ngữ lập trình cung cấp khái niệm Chương trình con (Subprogram). Trong C++, chương trình con được triển khai dưới dạng Hàm (Function)Thủ tục (Procedure). Bài viết này sẽ hướng dẫn chi tiết cú pháp và cách áp dụng cụ thể.


1. Khái niệm chương trình con

Chương trình con là một khối lệnh thực hiện một nhiệm vụ xác định, được đặt tên và có thể được gọi để thực thi từ nhiều vị trí khác nhau trong chương trình chính.

Lợi ích của chương trình con:
  • Tránh trùng lặp code: Viết một lần, gọi dùng nhiều lần.
  • Dễ quản lý và gỡ lỗi: Chia nhỏ một bài toán lớn phức tạp thành các khối chức năng nhỏ, độc lập, giúp mã nguồn sáng sủa, dễ đọc.
  • Hỗ trợ lập trình theo nhóm: Các thành viên có thể viết các chương trình con khác nhau độc lập rồi ghép nối lại.

2. Hàm trong C++ (Function)

Định nghĩa:

Hàm là một chương trình con nhận vào các tham số đầu vào, thực hiện tính toán và trả về một giá trị duy nhất có kiểu dữ liệu xác định (như int, double, string, bool...) cho nơi gọi nó thông qua câu lệnh return.

Cú pháp khai báo:
kiểu_trả_về tên_hàm(danh_sách_tham_số) {
    // Khối lệnh xử lý bên trong
    return giá_trị_trả_về; // Giá trị trả về phải có kiểu trùng với kiểu_trả_về
}
Ví dụ minh họa:

Viết hàm kiểm tra xem một số nguyên dương N có phải số nguyên tố hay không.

#include <bits/stdc++.h>

using namespace std;

// Hàm kiểm tra số nguyên tố, trả về kiểu bool (true hoặc false)
bool laNguyenTo(int n) {
    if (n <= 1) return false;
    for (int i = 2; i <= sqrt(n); ++i) {
        if (n % i == 0) {
            return false; // Trả về false ngay lập tức và thoát khỏi hàm
        }
    }
    return true; // Trả về true nếu không tìm thấy ước nào
}

int main() {
    int x;
    cin >> x;

    // Gọi hàm laNguyenTo trong câu lệnh điều kiện
    if (laNguyenTo(x)) {
        cout << x << " la so nguyen to.\n";
    } else {
        cout << x << " khong phai so nguyen to.\n";
    }

    return 0;
}

3. Thủ tục trong C++ (Procedure)

Định nghĩa:

C++ không có từ khóa riêng biệt để khai báo thủ tục giống như Pascal (procedure). Trong C++, thủ tục thực chất là một hàm có kiểu trả về đặc biệt là void (rỗng).

Thủ tục thực thi một chuỗi các hành động (như in ấn kết quả, tráo đổi phần tử...) và không trả về bất kỳ giá trị nào cho nơi gọi.

Cú pháp khai báo:
void tên_thủ_tục(danh_sách_tham_số) {
    // Khối lệnh thực thi
    // Không cần dùng lệnh return để trả về giá trị (có thể dùng 'return;' để thoát sớm khỏi thủ tục)
}
Ví dụ minh họa:

Viết một thủ tục nhận vào một mảng số nguyên và thực hiện in mảng đó ra màn hình.

#include <bits/stdc++.h>

using namespace std;

// Thủ tục in mảng, không trả về giá trị (kiểu void)
void inMang(int A[], int N) {
    for (int i = 0; i < N; ++i) {
        cout << A[i] << (i == N - 1 ? "" : " ");
    }
    cout << "\n";
}

int main() {
    int A[] = {3, 5, 8, 2, 9};
    int N = 5;

    // Gọi thủ tục inMang
    inMang(A, N);

    return 0;
}

4. Lời gọi chương trình con và Tham số truyền vào

Khi gọi một chương trình con, ta truyền các giá trị cụ thể (gọi là Tham số thực sự) vào các biến đại diện của hàm (gọi là Tham số hình thức). C++ hỗ trợ hai cơ chế truyền tham số chính:

a. Truyền tham trị (Pass-by-value):
  • Cơ chế: Hệ thống chỉ sao chép giá trị của biến gốc và nạp vào hàm. Mọi biến đổi đối với tham số bên trong chương trình con chỉ tác động lên bản sao đó, không làm thay đổi giá trị của biến gốc ban đầu ngoài chương trình chính.
  • Nhận biết: Khai báo danh sách tham số thông thường (ví dụ: void thayDoi(int x)).
b. Truyền tham chiếu (Pass-by-reference):
  • Cơ chế: Hàm không tạo bản sao mà sử dụng trực tiếp địa chỉ vùng nhớ của biến gốc. Mọi thay đổi đối với tham số bên trong chương trình con sẽ tác động trực tiếp và làm thay đổi giá trị của biến gốc ban đầu.
  • Nhận biết: Thêm ký tự và (&) trước tên tham số hình thức (ví dụ: void thayDoi(int &x)).
Ví dụ so sánh Tham trị và Tham chiếu:

Viết chương trình tráo đổi giá trị của hai số nguyên A và B.

#include <bits/stdc++.h>

using namespace std;

// Thủ tục hoán vị sử dụng Tham chiếu (&) để thay đổi trực tiếp biến gốc
void hoanVi(int &x, int &y) {
    int temp = x;
    x = y;
    y = temp;
}

// Thủ tục thử thay đổi giá trị sử dụng Tham trị (Không có &)
void thuThayDoi(int x) {
    x = 999;
}

int main() {
    int a = 5, b = 10;

    cout << "Ban dau: a = " << a << ", b = " << b << "\n";

    // Thử dùng tham trị
    thuThayDoi(a);
    cout << "Sau thuThayDoi (tham tri): a = " << a << " (khong thay doi)\n";

    // Dùng tham chiếu
    hoanVi(a, b);
    cout << "Sau hoanVi (tham chieu): a = " << a << ", b = " << b << " (da hoan vi)\n";

    return 0;
}

5. ĐỀ XUẤT 10 BÀI TẬP THỰC HÀNH THỰC TẾ (CHƯƠNG TRÌNH CON)

Hãy áp dụng kỹ năng xây dựng chương trình con để thực hiện đọc/ghi dữ liệu bằng tệp thông qua freopen đối với 10 bài tập thực tế sau:

Bài 1: Tính diện tích nông trại phức tạp
  • Ngữ cảnh: Một nông trại hình đa giác được chia thành N mảnh đất nhỏ hình tam giác. Người quản lý cần biết tổng diện tích đất bằng cách tính diện tích từng tam giác rồi cộng lại.
  • Yêu cầu: Viết một hàm double tinhDienTich(double a, double b, double c) tính diện tích tam giác theo công thức Heron khi biết độ dài 3 cạnh:
    • p = (a + b + c) / 2
    • diệntích = cănbậc_hai(p * (p - a) * (p - b) * (p - c))
    • Áp dụng hàm để tính tổng diện tích mảnh đất từ tệp farm.inp ghi ra tệp farm.out.
  • Đầu vào (Input - tệp farm.inp):
    • Dòng 1 chứa số nguyên dương N (1 <= N <= 1000).
    • N dòng tiếp theo, mỗi dòng chứa 3 số thực là độ dài 3 cạnh của một tam giác.
  • Đầu ra (Output - tệp farm.out): Một số thực duy nhất là tổng diện tích nông trại (lấy chính xác 2 chữ số thập phân).
  • Ví dụ:

    • Input:

      2
      3.0 4.0 5.0
      6.0 8.0 10.0
      
    • Output: 30.00 (Diện tích tam giác 1 là 6.0, tam giác 2 là 24.0 -> Tổng = 30.0)

Bài 2: Chuẩn hóa tự động mã vạch hàng hóa
  • Ngữ cảnh: Trong hệ thống quản lý kho, các mã vạch quét về chứa nhiều ký tự lỗi không hợp lệ. Quy tắc chuẩn hóa: Chỉ giữ lại các chữ số và viết hoa tất cả chữ cái thường.
  • Yêu cầu: Viết một thủ tục void chuanHoa(string &s) để chuẩn hóa trực tiếp chuỗi tham chiếu s. Đọc dữ liệu từ tệp barcode.inp ghi ra tệp barcode.out.
  • Đầu vào (Input - tệp barcode.inp):
    • Dòng 1 chứa số nguyên N (1 <= N <= 100).
    • N dòng tiếp theo, mỗi dòng chứa một chuỗi ký tự mã vạch S (độ dài không quá 100 ký tự).
  • Đầu ra (Output - tệp barcode.out): N dòng là các mã vạch sau khi đã được chuẩn hóa.
  • Ví dụ:

    • Input:

      2
      abc-123-XYZ
      12aB34#
      
    • Output:

      ABC123XYZ
      12AB34
      
Bài 3: Tìm kiếm số siêu nguyên tố bảo mật
  • Ngữ cảnh: Số siêu nguyên tố là số nguyên tố mà khi ta cắt bỏ các chữ số ở phía bên phải của nó, các số còn lại cũng là số nguyên tố. Ví dụ số 373 là số siêu nguyên tố vì 373, 37, 3 đều là các số nguyên tố.
  • Yêu cầu: Viết hàm bool laNguyenTo(int n). Áp dụng hàm này để viết chương trình tìm tất cả các số siêu nguyên tố có K chữ số. Đọc K từ tệp sprime.inp ghi ra tệp sprime.out.
  • Đầu vào (Input - tệp sprime.inp): Một số nguyên duy nhất K (1 <= K <= 8).
  • Đầu ra (Output - tệp sprime.out): In ra tất cả các số siêu nguyên tố có K chữ số theo thứ tự tăng dần, cách nhau bởi khoảng trắng.
  • Ví dụ:
    • Input: 2
    • Output: 23 29 31 37 53 59 71 73 79
Bài 4: Tối giản phân số số học
  • Ngữ cảnh: Một ứng dụng học tập toán học trực tuyến cần hiển thị kết quả phân số ở dạng tối giản nhất.
  • Yêu cầu: Viết hàm int timUCLN(int a, int b) và thủ tục void toiGian(int &tu, int &mau) truyền tham chiếu để rút gọn phân số. Đọc tử và mẫu từ tệp fraction.inp ghi kết quả ra tệp fraction.out.
  • Đầu vào (Input - tệp fraction.inp): Một dòng chứa hai số nguyên dương là Tử số và Mẫu số (1 <= Tử, Mẫu <= 109).
  • Đầu ra (Output - tệp fraction.out): Tử số và mẫu số của phân số tối giản cách nhau bởi dấu cách.
  • Ví dụ:
    • Input: 12 18
    • Output: 2 3
Bài 5: Số Fibonacci lớn nhất nhỏ hơn N
  • Ngữ cảnh: Trong nghiên cứu sinh học cấu trúc hoa hướng dương, ta cần tìm số lượng hạt lớn nhất thuộc dãy Fibonacci nhưng phải nhỏ hơn một ngưỡng giới hạn N cho trước.
  • Yêu cầu: Viết hàm long long fibo(int k) trả về số Fibonacci thứ k. Dùng hàm này để tìm số Fibonacci lớn nhất nhưng nhỏ hơn N từ tệp targetfibo.inp ghi ra tệp targetfibo.out.
  • Đầu vào (Input - tệp targetfibo.inp): Một số nguyên dương duy nhất N (2 <= N <= 1018).
  • Đầu ra (Output - tệp targetfibo.out): Một số nguyên duy nhất thỏa mãn yêu cầu.
  • Ví dụ:
    • Input: 10
    • Output: 8 (Dãy Fibonacci gồm: 1, 1, 2, 3, 5, 8, 13,... Số lớn nhất nhỏ hơn 10 là 8)
Bài 6: Sắp xếp điểm số bằng thủ tục hoán vị tự viết
  • Ngữ cảnh: Hệ thống cần sắp xếp danh sách N điểm số theo thứ tự giảm dần. Bạn không được dùng hàm std::swap hay std::sort có sẵn, mà phải tự viết hàm đổi chỗ.
  • Yêu cầu: Viết thủ tục void hoanVi(double &x, double &y) sử dụng tham chiếu để đổi chỗ hai số thực. Cài đặt thuật toán sắp xếp nổi bọt (Bubble Sort) sử dụng thủ tục này. Đọc điểm số từ tệp swapsort.inp ghi kết quả ra tệp swapsort.out.
  • Đầu vào (Input - tệp swapsort.inp):
    • Dòng 1 chứa số nguyên N (1 <= N <= 1000).
    • Dòng 2 chứa N số thực điểm số.
  • Đầu ra (Output - tệp swapsort.out): Dãy điểm số sau khi đã sắp xếp giảm dần cách nhau bởi dấu cách (lấy 1 chữ số thập phân).
  • Ví dụ:

    • Input:

      4
      8.2 9.5 7.0 8.5
      
    • Output: 9.5 8.5 8.2 7.0

Bài 7: Số lượng số đối xứng trong khoảng
  • Ngữ cảnh: Một nhà toán học cần đếm xem trong khoảng từ số nguyên dương A đến số nguyên dương B có bao nhiêu số đối xứng (số đọc xuôi hay ngược đều giống nhau, ví dụ: 121, 55, 7887).
  • Yêu cầu: Viết hàm bool laDoiXung(long long n). Áp dụng hàm để đếm số lượng số đối xứng trong đoạn [A, B] từ tệp palincount.inp ghi ra tệp palincount.out.
  • Đầu vào (Input - tệp palincount.inp): Một dòng gồm hai số nguyên dương A và B (1 <= A <= B <= 105).
  • Đầu ra (Output - tệp palincount.out): Một số nguyên duy nhất là số lượng số đối xứng tìm được.
  • Ví dụ:
    • Input: 10 100
    • Output: 9 (Các số đối xứng là 11, 22, 33, 44, 55, 66, 77, 88, 99)
Bài 8: Bộ chuyển đổi hệ cơ số
  • Ngữ cảnh: Trong kỹ thuật mạng và phần cứng máy tính, ta cần chuyển số nguyên dương N hệ thập phân sang hệ cơ số B bất kỳ từ 2 đến 16.
  • Yêu cầu: Viết hàm string doiCoSo(long long N, int B) nhận vào N và cơ số B, trả về xâu ký tự là kết quả biểu diễn của N ở hệ cơ số B (sử dụng các ký tự A đến F cho các chữ số từ 10 đến 15). Đọc dữ liệu từ tệp baseconv.inp ghi kết quả ra tệp baseconv.out.
  • Đầu vào (Input - tệp baseconv.inp): Một dòng chứa hai số nguyên N và B (1 <= N <= 1012, 2 <= B <= 16).
  • Đầu ra (Output - tệp baseconv.out): Chuỗi ký tự kết quả biểu diễn số.
  • Ví dụ:
    • Input: 255 16
    • Output: FF
Bài 9: Biểu diễn phân tích thừa số nguyên tố viết tay
  • Ngữ cảnh: Trong giảng dạy toán học cơ sở, học sinh cần viết phân tích thừa số nguyên tố của số N dưới dạng tích các số cách nhau bởi dấu nhân (ví dụ: 20 phân tích thành 2 * 2 * 5).
  • Yêu cầu: Viết thủ tục void phanTichNguyenTo(int n) thực hiện tính toán và in ra màn hình dạng phân tích của N theo định dạng trên. Đọc dữ liệu từ tệp primefact.inp ghi kết quả ra tệp primefact.out.
  • Đầu vào (Input - tệp primefact.inp): Một số nguyên dương duy nhất N (2 <= N <= 108).
  • Đầu ra (Output - tệp primefact.out): Chuỗi ký tự biểu thị tích các thừa số nguyên tố.
  • Ví dụ:
    • Input: 60
    • Output: 2 * 2 * 3 * 5
Bài 10: Truy vấn kỳ hạn gửi tiền lãi kép
  • Ngữ cảnh: Công ty tài chính có Q đối tác muốn gửi tiền tiết kiệm với lãi suất lũy kế R% mỗi tháng. Mỗi đối tác có số tiền gốc ban đầu là M đồng và số tiền mục tiêu cần đạt là T đồng.
  • Yêu cầu: Viết hàm int tinhSoThang(double M, double R, double T) nhận các thông tin và trả về số tháng tối thiểu cần gửi. Áp dụng hàm để giải quyết Q câu hỏi truy vấn của đối tác từ tệp finance.inp ghi kết quả ra tệp finance.out.
  • Đầu vào (Input - tệp finance.inp):
    • Dòng 1 chứa số nguyên dương Q là số lượng đối tác (1 <= Q <= 1000).
    • Q dòng tiếp theo, mỗi dòng chứa 3 số thực: Số tiền gốc M, lãi suất R%, số tiền mục tiêu T (1 <= M <= T <= 109, 0.1 <= R <= 10.0).
  • Đầu ra (Output - tệp finance.out): Q số nguyên tương ứng là số tháng tối thiểu cần gửi của từng đối tác cách nhau bởi khoảng trắng.
  • Ví dụ:

    • Input:

      2
      100.0 5.0 120.0
      500.0 2.0 550.0
      
    • Output: 4 5


Lời kết

Chương trình con (Hàm và Thủ tục) là công cụ tuyệt vời nhất để tổ chức mã nguồn của bạn một cách khoa học, chuyên nghiệp và tối ưu. Hãy lưu ý phân biệt rõ ràng khi nào cần dùng Tham trị (truyền giá trị) và khi nào cần dùng Tham chiếu (truyền địa chỉ vùng nhớ dùng dấu &) để tránh các lỗi logic đáng tiếc khi làm bài.

Hãy lập trình và gửi lời giải của 10 bài tập chương trình con trên tại hệ thống Sen Code Online Judge! nhé! Chúc các bạn học tập thật tốt!


Bình luận

Hãy đọc nội quy trước khi bình luận.



  • 0
    admin  đã bình luận lúc 31, Tháng 7, 2026, 16:25

    Test comments!