Bài giảng Kỹ thuật lập trình - Bài 9: Gỡ lỗi và kiểm thử - Trịnh Thành Trung

Trịnh Thành Trung (ThS)  
Bài 9  
GỠ LỖI VÀ KIỂM THỬ  
Nội dung  
1. Gỡ lỗi  
2. Kiểm thử  
1.  
Gỡ lỗi  
Debug  
Gỡ rối  
Debug  
▪ Gỡ rối là gì?  
▫ Khi chương trình bị lỗi, gỡ rối là các công việc cần làm để làm  
cho chương trình dịch thông, chạy thông  
▫ Thật không may, gỡ rối luôn là thao tác phải làm khi lập trình,  
thao tác này rất tốn kém  
▪ Cách tốt nhất vẫn là phòng ngừa  
▫ Khi bắt đầu gỡ rối chương trình, bạn đã biết là chương trình  
không chạy.  
▫ Nếu bạn biết lý do tại sao chương trình không chạy, bạn có thể  
sửa được chương trình cho nó chạy  
▫ Nếu bạn hiểu chương trình của bạn, bạn sẽ có ít sai lầm và dễ  
dàng sửa chữa sai sót hơn. Bí quyết là viết mã đơn giản, hiệu quả,  
chú thíclýh.hợp  
Gỡ rối  
Debug  
▪ Đối với mã nguồn, tiêu chí nào quan trọng hơn: rõ ràng hay  
chính xác?  
▫ Nếu mã nguồn rõ ràng, bạn có thể làm cho chương trình trở nên  
chính xác.  
▫ Bạn có chắc là làm cho chương trình trở nên chính xác nếu nó  
không rõ ràng hay không?  
▪ Nếu chương trình được thiết kế với cấu trúc tốt, được viết  
bằng phong cách lập trình tốt và áp dụng các kỹ thuật viết  
chương trình hiệu quả, bẫy lỗi thì chi phí cho việc gỡ rối sẽ  
được giảm thiểu.  
Tìm kiếm và  
gỡ rối  
▪ Khi có lỗi, ta thường đổ cho trình dịch, thư viện hay bất cứ  
nguyên nhân khách quan nào khác… tuy nhiên, cuối cùng thì  
lỗi vẫn là lỗi của chương trình, và trách nhiệm gỡ rối thuộc về  
LTV  
▪ Phải hiểu vấn đề xuất phát từ đâu thì mới giải quyết được:  
▫ Lỗi xảy ra ở đâu? Hầu hết các lỗi thường đơn giản và dễ tìm.  
Hãy khảo sát các đầu mối và cố gắng xác định được đoạn mã  
nguồn gây lỗi  
▫ Lỗi xảy ra như thế nào? Khi đã có một số thông tin về lỗi và nơi  
xảy ra lỗi, hãy suy nghĩ xem lỗi xảy ra như thế nào  
▫ Đâu là nguyên nhân gây lỗi? Suy luận ngược trở lại trạng thái  
của chương trình để xác định nguyên nhân gây ra lỗi  
Gỡ rối liên quan đến việc suy  
luận lùi, giỗng như phá án. Một  
số vấn đề không thể xảy ra và chỉ  
có những thông tin xác thực mới  
đáng tin cậy. Phải đi ngược từ kết  
quả để khám phá nguyên nhân.  
Khi có lời giải thích đầy đủ, ta sẽ  
biết được vấn đề cần sửa và có  
thể phát hiện ra một số vấn đề  
khác  
“
Debugging  
Heuristic  
Debugging Heuristic  
Áp dụng khi nào  
Build-time (dịch)  
(1) Hiểu các thông báo lỗi (error messages)  
(2) Nghĩ trước khi viết lại chương trình  
(3) Tìm kiếm các lỗi (bug) hay xảy ra  
(4) Divide and conquer  
(5) Viết thêm các đoạn mã kiểm tra để  
chương trình tự kiểm tra nó  
Run-time (chạy)  
(6) Hiện thị kết quả  
(7) Sử dụng debugger  
(8) Tập trung vào các lệnh mới viết / mới viết  
lại  
Hiểu  
các thông báo lỗi  
▪ Gỡ rối khi dịch (build-time) chương trình dễ hơn gỡ rối khi  
chạy chương trình nếu LTV hiểu được các thông báo lỗi  
▪ Một số thông báo lỗi đến từ preprocessor  
#include <stdioo.h>  
int main(void)  
Gõ sai tên file cần gọi  
/* Print "hello, world" to stdout and  
return 0.  
Thiếu dấu */  
{
printf("hello, world\n");  
return 0;  
}
$ gcc217 hello.c -o hello  
hello.c:1:20: stdioo.h: No such file or directory  
hello.c:3:1: unterminated comment  
hello.c:2: error: syntax error at end of input  
Hiểu  
các thông báo lỗi  
▪ Một số thông báo lỗi đến từ compiler  
#include <stdio.h>  
int main(void)  
/* Print "hello, world" to stdout and  
return 0. */  
{
Sai từ khóa  
printf("hello, world\n")  
retun 0;  
}
$ gcc217 hello.c -o hello  
hello.c: In function `main':  
hello.c:7: error: `retun' undeclared (first use in this function)  
hello.c:7: error: (Each undeclared identifier is reported only once  
hello.c:7: error: for each function it appears in.)  
hello.c:7: error: syntax error before numeric constant  
Hiểu  
các thông báo lỗi  
▪ Một số thông báo lỗi đến từ linker  
Sai tên hàm  
được gọi  
#include <stdio.h>  
int main(void)  
/* Print "hello, world" to stdout and  
return 0. */  
{
prinf("hello, world\n")  
return 0;  
}
Compiler warning (not error): prinf()  
được gọi trước khi khai báo  
Linker error: không tìm thấy  
định nghĩa hàm prinf()  
$ gcc217 hello.c -o hello  
hello.c: In function `main':  
hello.c:6: warning: implicit declaration of function `prinf'  
/tmp/cc43ebjk.o(.text+0x25): In function `main':  
: undefined reference to `prinf'  
collect2: ld returned 1 exit status  
Việc thay đổi mã nguồn không  
hợp lý có thể gây ra nhiều vấn  
đề hơn là để nguyên không thay  
đổi gì, do đó phải luôn suy nghĩ  
trước khi làm  
“
Suy nghĩ  
trước khi viết  
▪ Gỡ rối ngay khi gặp  
▫ Khi phát hiện lỗi, hãy sửa ngay, đừng để sau mới sửa, vì có thể  
lỗi không xuất hiện lại (do tình huống)  
▫ Cân nhắc: việc sửa chữa này có ảnh hưởng tới các tình huống  
khác hay không ?  
▪ Quan sát lỗi từ góc độ khác  
▫ Viết đoạn mã nguồn gây lỗi ra giấy  
▸ Đừng chép hết cả đoạn không có nguy cơ gây lỗi, hoặc in toàn bộ  
code ra giấy in => phá vỡ cây cấu trúc  
▫ Vẽ hình minh họa các cấu trúc dữ liệu  
▸ Nếu mà giải thuật làm thay đổi CTDL, vẽ lại hình trước khi viết lại  
giải thuật  
▫ Đọc trước khi gõ vào  
▸ Đừng vội vàng, khi không rõ điều gì thực sự gây ra lỗi và sửa  
khôncghđỗúsnẽgcó nguy cơ gây ra lỗi khác  
Suy nghĩ  
trước khi viết  
▪ Tạm dừng viết chương trình  
▫ Khi gặp vấn đề, khó khăn, chậm tiến độ, lập tức thay đổi công  
việc => rút ra khỏi luồng quán tính sai lầm …  
▫ Bỏ qua đoạn chương trình có lỗi  
▫ Khi nào cảm thấy sẵn sàng thì chữa  
▪ Giải thích logic của đoạn mã nguồn:  
▫ Cho chính bạn  
▸ Tạo điều kiện để suy nghĩ lại  
▫ Cho ai khác có thể phản bác  
▸ Extrem programming : làm việc theo cặp, pair programming,  
người này LT, người kia kiểm tra, và ngược lại  
▫ Cho cái gì đó không thể phản bác (cây, cốc trà đá, gấu bông…)  
▸ Tạo điều kiện củng cố suy luận của mình  
Tìm các lỗi  
tương tự  
switch (i) {  
int i;  
…
scanf("%d", i);  
case 0:  
…
/* missing break */  
case 1:  
…
char c;  
…
break;  
…
c = getchar();  
}
if (i = 5)  
while (c = getchar() != EOF)  
…
…
Tips: nếu đặt chế độ cảnh báo  
if (5 < i < 10)  
if (i & j)  
…
…
(warnings) khi dịch thì hầu hết  
các lỗi kiểu này sẽ được phát  
hiện  
Tìm các lỗi  
tương tự  
▪ Khi gặp vấn đề, hãy liên tưởng đến những trường hợp  
tương tự đã gặp  
▫ Vd1 :  
int n; scanf(“%d”,n); ?  
▫ Vd2 :  
int n=1; double d=PI;  
printf(“%d %f \n”,d,n); ??  
▪ Không khởi tạo biến (với C) cũng sẽ gây ra những lỗi khó  
lường.  
Tìm các lỗi  
tương tự  
▪ Làm cho lỗi xuất hiện lại  
▫ Cố gắng làm cho lỗi có thể xuất hiện lại khi cần  
▫ Nếu không được, thì thử tìm nguyên nhân tại sao lại không làm  
cho lỗi xuất hiện lại  
1. Array as a parameter handled improperly –  
Tham số mảng được xử lý không đúng cách  
2. Array index out of bounds – Vượt ra ngoài  
phạm vi chỉ số mảng  
{C/C++}  
3. Call-by-value used instead of call-by  
reference for function parameters to be  
modified – Gọi theo giá trị, thay vì gọi theo  
tham chiếu cho hàm để sửa  
4. Comparison operators misused – Các toán tử  
so sánh bị dùng sai  
5. Compound statement not used - Lệnh phức hợp  
không được dùng  
6. Dangling else - nhánh else khong hợp lệ  
7. Division by zero attempted - Chia cho 0  
8. Division using integers so quotient gets  
truncated – Dùng phép chia số nguyên nên phần  
thập phân bị cắt  
9. Files not closed properly (buffer not  
flushed) - File không được đóng phù hợp (  
buffer không bị dẹp)  
10. Infinite loop - lặp vô hạn  
11. Global variables used – dùng biến tổng thể  
12. IF-ELSE not used properly – dùng if-else  
không chuân  
13. Left side of assignment not an L-value -  
phía trái phép gán không phải biến  
14. Loop has no body – vòng lặp không có thân  
15. Missing "&" or missing "const" with a  
call-by-reference  
{C/C++}  
function parameter – thiếu dấu & hay từ khóa  
const với lời gọi tham số hàm theo tham chiếu  
16. Missing bracket for body of function or  
compound statement – Thiếu cặp {} cho thân của  
hàm hay nhóm lệnh  
17. Mission reference to namespace - Thiếu  
tham chiếu tới tên miền  
18. Missing return statement in a value-  
returning function – Thiếu return  
19. Missing semi-colon in simple statement,  
function prototypes, struct definitions or  
class definitions – thiếu dấu ; trong lệnh  
đơn …  
20. Mismatched data types in expressions –  
kiểu dữ liệu không hợp  
21. Operator precedence misunderstood - Hiểu  
sai thứ tự các phép toán  
22. Off-by-one error in a loop – Thoát khỏi  
bởi 1 lỗi trong vòng lặp  
23. Overused (overloaded) local variable names  
- Trùng tên biến cục bộ  
{C/C++}  
24. Pointers not set properly or overwritten  
in error – Con trỏ không được xác định đúng  
hoặc trỏ vào 1 vị trí không có  
25. Return with value attempted in void  
function – trả về 1 giá trị trong 1 hàm void  
26. Undeclared variable name – không khai báo  
biến  
27. Un-initialized variables – Không khởi tạo  
giá trị  
28. Unmatched parentheses – thiếu }  
29. Un-terminated strings - xâu không kết  
thúc, thiếu "  
30. Using "=" when "= =" is intended or vice  
versa  
31. Using "&" when "&&" is intended or vice  
versa  
32. "while" used improperly instead of "if" –  
while được dùng thay vì if  
Tải về để xem bản đầy đủ
pdf 94 trang yennguyen 22540 Free
Bạn đang xem 20 trang mẫu của tài liệu "Bài giảng Kỹ thuật lập trình - Bài 9: Gỡ lỗi và kiểm thử - Trịnh Thành Trung", để tải tài liệu gốc về máy hãy click vào nút Download ở trên

File đính kèm:

  • pdfbai_giang_ky_thuat_lap_trinh_bai_9_go_loi_va_kiem_thu_trinh.pdf