Các lỗi thường gặp trong lập trình


Trong quá trình viết các chương trinh C++, người mới học thường gặp phải một số lỗi phổ biến như vòng lặp vô hạn, lỗi vượt quá thời gian thực thi (TLE), lỗi thời gian chạy (RTE), hoặc sai số khi làm việc với số thực. Việc nhận diện và hiểu rõ nguyên nhân của các lỗi này giúp bạn viết mã hiệu quả và chính xác hơn.

Vòng lặp vô hạn

Vòng lặp vô hạn xảy ra khi điều kiện kết thúc vòng lặp không bao giờ bị vi phạm, khiến chương trình chạy mãi không dừng lại.

Ví dụ

Chương trình sau sẽ chạy vô tận do giá trị của biến `#!cpp i" không bao giờ thay đổi.

Mã nguồn

C++
int i = 0;
while (i < 10) {
    cout << i << '\n';
}

Hướng khắc phục:

  • Đảm bảo điều kiện của vòng lặp sẽ trở thành false sau một số bước nhất định.
  • Sử dụng lệnh break hợp lý để thoát khỏi vòng lặp.

Lỗi vượt quá thời gian cho phép – Time Limit Exceeded

Time Limited Exceeded (TLE) là lỗi thường gặp khi làm bài lập trình trên các hệ thống chấm bài trực tuyến. Trung bình trong \(1\) giây, C++ có thể xử lý được \(2 \times 10^8\) phép toán. Sử dụng quá nhiều phép toán dẫn đến thời gian chạy quá lâu và gây lỗi TLE.

Ví dụ

Cho số nguyên \(n\) (\(n \leq 10^9\)), tính tổng các số nguyên từ \(1\) đến \(n\).

Nếu bài tập có yêu cầu thời gian 1 giây, chương trình sau sẽ gặp lỗi TLE:

Mã nguồn

C++
int main() {
    int n;
    cin >> n;
    int tong = 0;
    for (int i = 1; i <= n; i++) {
        tong += i;
    }
    cout << tong;
}

Chương trình thường chạy quá lâu do thuật toán chưa tối ưu, tốc độ đọc, xuất dữ liệu bị chậm do khối lượng dữ liệu lớn.

Cách cải thiện:

  • Cải tiến chương trình để sử dụng thuật toán có độ phức tạp tốt hơn
  • Đối với những bài cần nhập nhiều dữ liệu, ta có thể thêm lệnh sau trước khi đọc để tăng tốc đọc dữ liệu:

Mã nguồn

C++
int main() {
    ios::sync_with_stdio(false);
    cin.tie(NULL);
    ...
}

hoặc ngắn gọn hơn:

Mã nguồn

C++
int main() {
    cin.tie(0)->sync_with_stdio(0);
    ...
}

\newpage

Lỗi thời gian chạy – Runtime Error

Runtime Error (RTE) xảy ra khi chương trình chạy nhưng bị dừng bất thường do:

  • Truy cập mảng ngoài phạm vi.

Ví dụ

C++
#include <bits/stdc++.h>
using namespace std;
int a[5] = {0, 1, 2, 3, 4};
int main() {
    cout << a[-1] << "\n"; // LỖI
    cout << a[100] << "\n"; // LỖI
}
  • Chia cho 0.

Ví dụ

C++
int main() {
    int a = 5, b = 0;
    cin >> a >> b;
    cout << a / b; // LỖI
}
  • Khai báo mảng quá lớn, đặc biệt là khai báo ở trong hàm.

Ví dụ

C++
int main() {
    int a[100000000]; // LỖI
}

Sai số khi sử dụng số thực

Do cách lưu trữ số thực trong C++, việc so sánh trực tiếp hai số thực thường gây lỗi.

Ví dụ

Mã nguồn

C++
double a = 0.1 + 0.2;
if (a == 0.3) {
    cout << "Bang nhau";
} else {
    cout << "Khong bang nhau";
}

Đầu ra

Khong bang nhau

Các thao tác tính toán trên số thực thường có sai số. Cụ thể, phép tính \(0.1 + 0.2\) ở một số trình chạy có thể tính ra \(0.3000000004\). Sai số tuy là rất nhỏ nhưng cũng ảnh hưởng đến các phép so sánh bằng trong chương trình.

Để giải quyết vấn đề này, ta thường sử dụng một biến epsilon - sai số cháp nhận được. Nếu hai số thực có khoảng cách không vượt quá số epsilon, hai số được coi như bằng nhau:

Ví dụ

Mã nguồn

C++
const double EPS = 0.0000001;
int main() {
    double a = 0.1 + 0.2;
    if (abs(a - 0.3) < EPS) {
        cout << "Bang nhau";
    } else {
        cout << "Khong bang nhau";
    }
}

Đầu ra

Bang nhau