Các lỗi thường gặp

Trong các hãng sản xuất phần mềm, có tới 50% nhân viên làm công việc kiểm tra phần mềm, 50% nhân viên còn lại cũng phải dành ra 50% thời gian làm việc của mình để kiểm tra lỗi. Chi phí cho việc kiểm tra lỗi có thể lên tới hơn 3/4 tổng chi phí dự án. Các con số ở trên cũng đủ cho ta thấy việc tìm và sửa lỗi quan trọng như thế nào. Trong bài viết này, tôi xin được mạn phép trình bày các lỗi mà tôi cho là hay gặp và cần được kiểm tra.

Các lỗi có thể được chia ra làm 4 loại: lỗi từ vựng, lỗi cú pháp, lỗi ngữ nghĩa, và lỗi lô-gíc. 3 loại lỗi đầu tiên có thể dễ dàng phát hiện bằng trình biên dịch, và lỗi logic là khó phát hiện nhất.

Lỗi từ vựng, lỗi cú pháp, và lỗi ngữ nghĩa

- Lỗi từ vựng : ví dụ như viết sai tên hàm, tên biến, giả sử như scnaf, reutrn, ...

- Lỗi cú pháp : ví dụ như đặt dấu ngoặc không cân, có mở ngoặc mà không có đóng ngoặc,...

- Lỗi ngữ nghĩa : ví dụ như tuyền thiếu tham số cho hàm, truyền tham số sai kiểu, gán một int cho một char*, ...

Ba lỗi trên có thể dàng phát hiện trong lúc biên dịch.

Lỗi logic

sau đây tôi xin được liệt kê các lỗi theo tần suất giảm dần

- Vượt quá giới hạn của kiểu dữ liệu, của mảng : khai báo kiểu dữ liệu quá bé, mảng quá bé; tràn số xảy ra trong tính toán, ví dụ nhân hoặc cộng hai số mà kết quả lớn hơn giới hạn của int;

- Chưa khởi tạo nội dung : phải đảm bảo các biến được khởi tạo nếu đề bài có nhiều test; khởi tạo các biến đặc biệt (ví dụ như đảm bảo a[0]=0 để tính mảng cộng dồn của a); cài đồ thị thì phải clear các vector nếu có nhiều test;

- Sai tên file input, output.

- Quy hoạch động mà không lưu giá trị

- Đề bài có chi tiết "hiểm", có input "hiểm" : không phải đề bài nào cũng in đậm hay gạch chân chữ "không"; có nhiều bài cho đồ thị không liên thông, hoặc đa đồ thị; cần kiểm tra các test nho nhỏ như n=0, các số trong input bằng không; không nên bỏ sót bất cứ chữ nào trong đề bài (thường thì khi đọc sách, ta thường bỏ qua các chỗ không cần thiết, thế nhưng khi đọc đề bài, nhất là đề dịch, có những nội dung có thể bị bỏ qua nếu không đọc kĩ)

- Không kiểm tra hết các trường hợp của test : nên tạo một thuật toán chắc chắn đúng và một hàm sinh test ngẫu nhiên để kiểm tra tính đúng đắn của chương trình.

- Mô-đun cho số âm : số âm mô-đun cho số dương vẫn là số âm, cần đảm bảo kết quả mô-đun luôn là số dương.

- Truy cập bất hợp pháp : truy cập vào chỉ số âm của mảng; truyền sai tham số cho hàm scanf và printf; truy cập nội dung của con trỏ chưa được cấp phát (nhất là việc cấp phát động trong cây BST); tính iterator phía trước của a.begin(), tính iterator phía sau của a.end().

- Nhầm lẫn giữa mảng đánh số từ 1 với mảng đánh số từ 0 : nếu bạn dùng cả hai loại mảng này, bạn nên chú thích rõ xem mảng này là đnáh số từ 1 hay đánh số từ 0 (one-based và zero-based).

- Nhầm lẫn trục x với trục y

- Sai số của số thực : để biến đổi số thực thành số nguyên, ta cần viết x = int (y+0.5); không được phép sử dụng dấu == hoặc != để so sánh hai số thực; dùng kiểu double thay cho float; dùng hằng số epsilon để so sánh hai số; hạn chế tối đa việc sử dụng số thực; nếu có thể, bạn có thể thay ố thực bằng phân số (với các bài toán liên quan nhiều đến đại số).

- Nhầm độ phức tạp của hàm : lower_bound, upper_bound mất chi phí là O(logn), truy cập map mất O(logn), chú ý với map của string thì truy cập mất mất O(m logn) với m là chi phí so sánh (tức độ dài của xâu); hàm count(first, last, key) mất chi phí O(n) (ngay cả trên vector đã sắp xếp).

- Nhầm chỉ số với phần tử : viết nhầm a[index] thành index.

- Hàm không được thực hiện trong phép and, or : ví dụ a=a||f(x), f(x) sẽ không được gọi nếu a=true. a=a&&f(x), f(x) sẽ không được gọi nếu a=false; để đảm bảo f(x) luôn được gọi trong hai ví dụ trên thì ta có thể viết a=f(x)||a, a=f(x)&&a;