
Kỹ thuật Debug trong C
nvhoang@fit.hcmuns.edu.vn.
Đối với lập trình viên, việc đề ra ý tưởng để giải quyết vấn đề đã là khó khăn, nhưng việc
cài đặt được ý tưởng đó cũng không đơn giản. Đôi khi chỉ vì một dấu “;” hay sai kiểu dữ
liệu cũng có thể biến bản cài đặt trở nên vô nghĩa vì không thể hiện đúng ý tưởng đề ra.
Nhưng làm sao để phát hiện ra một con sâu (lỗi-bug) trong một “rừng lệnh”? Câu trả lời
rất đơn giản nhưng khó thực hiện : chúng ta phải kiên nhẫn “vạch lá tìm sâu” ! Đó là lý
do tại sao chúng ta phải nắm kỹ thuật debug.
Bài viết này nhằm hướng dẫn các bạn sử dụng kỹ thuật debug để tìm lỗi thông qua việc
phân tích các ví dụ, đồng thời cũng đưa ra một số bài tập từ dễ đến khó để các bạn có thể
từng bước nắm vững kỹ thuật debug.
1 Các loại lỗi trong chương trình:
1.1 Lỗi sai cú pháp:
Đây là những lỗi sai xảy ra khi biên dịch chương trình (sau khi nhấn F9). Khi gặp
lỗi này, chúng ta nên nhấn Ctrl-F1 để gọi giúp đỡ hoặc xem lại tài liệu tham khảo. Ở đây
không để cập chi tiết đến vấn đề này.
1.2 Lỗi sai logic:
1.2.1 Khái niệm :
Các lỗi sai logic là những lỗi khi biên dịch, trình biên dịch không báo lỗi. Những lỗi
này nằm tiềm ẩn trong chương trình và làm cho chương trình của chúng ta đưa ra những
kết quả không như mong muốn.
Các lỗi sai logic thường xuất phát từ 2 nguyên nhân :
- Do “nhầm” (nhầm chứ không sai) cú pháp dẫn đến sai logic.
- Do ý tưởng để giải quyết bài toán đã sai ngay từ đầu.
Loại lỗi này bình thường rất khó nhận ra. Dĩ nhiên, nếu tinh ý đôi khi chúng ta vẫn có
thể phát hiện ra những lỗi này, nhưng việc phát hiện này đòi hỏi chúng ta phải nắm rất
vững cú pháp C, cũng như logic của chương trình cộng thêm một chút kinh nghiệm. Tuy
nhiên, trong đa số các trường hợp chúng ta phải thực hiện công đoạn debug để tìm ra các
lỗi sai logic này.
1.2.2 Một số ví dụ về lỗi sai logic :
Sau đây là một số ví dụ về lỗi sai logic:

VD1 : Xét hàm hoán vị 2 số nguyên
void HoanVi(int a, int b)
{
int c = a;
a = b;
b = c;
}
Thoạt nhìn, chúng ta hàm này không có vấn đề gì cả ! Nhưng nếu tinh ý một chút chúng
ta sẽ thấy cách truyền tham số của hàm là sai, đây là cách truyền tham trị chứ không phải
truyền tham biến, do đó giá trị của a, b sẽ không thay đổi sau khi hàm thực hiện xong.
Chúng ta phải sửa lại như sau :
void HoanVi(int &a, int &b)
{
int c = a;
a = b;
b = c;
}
VD2 : Xét hàm sắp xếp mảng một chiều các số nguyên theo thứ tự tăng dần
/*
Hàm sắp xếp mảng
Tham số vào :
n – số lượng số nguyên
a - mảng một chiều các số nguyên, từ a[0]...a[n-1]
*/
void SapXepMangTangDan(int n, int a[])
{
int i, j;
for (int i = 0; i < n; i++);
for (int j = 0; j < n; j++)
if (a[i] > a[j])
HoanVi(a[i], a[j]);
}
Nếu như chúng ta chưa từng gõ đoạn mã hàm sắp xếp thì có lẽ chúng ta khó phát hiện ra
chỗ sai. Nhưng cho dù như vậy chúng ta vẫn có thể phát hiện ra dòng lệnh “for (int
i = 0; i < n; i++);” là có vấn đề, tại sao lại có dấu “;” ở cuối dòng lặp “for” ?
Nhưng cho dù bạn có bỏ dấu “;” đi thì hàm này vẫn sai. Trong trường hợp này chúng ta
bắt buộc phải debug để loại đi những lỗi logic.
Các bạn có thể tham khảo phục lục để xem một số lỗi sai logic thường gặp.

2 Cơ chế thực thi chương trình :
Để có thể thực hiện công việc debug, trước hết chúng ta sẽ tìm hiểu cơ chế thực thi của
một chương trình C.
(Để cho đơn giản, tôi xin phép chỉ trình bày việc thực thi mức ngôn ngữ C, chứ không
trình bày ở mức ngôn ngữ assembly).
Cơ chế chính thực thi trong chương trình C là thực thi từng dòng lệnh từ trên xuống
dưới, chỉ khi gặp những lệnh rẽ nhánh (if), những lệnh lặp (for, while), những lời gọi
hàm, hay lệnh nhảy (goto) thì C sẽ chuyển đến thực thi dòng lệnh đã được chỉ định, rồi
lại tuân theo cơ chế từ trên xuống dưới. C luôn luôn bắt đầu thực thi từ hàm main().
2.1 Cơ chế chính của việc thực thi :
1 #include <stdio.h>
2 #include <conio.h>
3 void main()
4 {
5 clrscr();
6 printf(“Đây là chương trình ví dụ\n”);
7 getch();
8 }
Như đã nói trên, C sẽ bắt đầu thực thi chương trình từ hàm void main() : dòng lệnh 4. Sau
đó C sẽ tiếp tục thực thi từ trên xuống dưới. Tức là tiếp tục thực thi dòng lệnh 5 : clrscr(),
rồi đến dòng lệnh 6 : printf , rồi đến dòng lệnh 7 : getch(). Sau khi thực thi xong dòng
lệnh 8 : dấu “}”, không còn gì thực thi nữa C sẽ kết thúc việc thực thi.
2.2 Các lệnh if...else, while, do..while :
Các lệnh này các có tham khảo lý thuyết, xin phép không nêu ra ở đây. Tuy nhiên, có một
vài lưu ý sau :
- Các lệnh lặp cũng được thực thi theo cơ chế từ trên xuống dưới, nhưng khi đến cuối
khối lệnh lặp, C sẽ quay lại đầu khối để thực thi (nếu biểu thức điều kiện trong while
thỏa)
- Sau khi thực thi xong các lệnh này (tức là các biểu thức trong if, while đều thực thi
xong), C sẽ tiếp tục thực thi khối lệnh ngay tiếp sau những lệnh này.
VD : Nhập một mảng các số nguyên dương A, việc nhập kết thúc khi nhập một số âm.
Sau đó kiểm tra xem dãy A đó có tăng hay không, xuất ra màn hình “DÃY TĂNG” hoặc
“DÃY KHÔNG TĂNG”.
#include <stdio.h>
#include <conio.h>
Th
ực thi từ tr
ên
xuống dưới, bắt
đầu từ hàm
main()

1 void main()
2 {
3 int a[100]; // mang cac so nguyen duong
4 int n; // so luong phan tu
5 int x;
6 int daytang;
7 clrscr();
8
9 // nhap
10 n = 0;
11 do
12 {
13 printf("nhap a[%d] : ", n);
14 scanf("%d", &x);
15 if (x >= 0)
16 {
17 a[n] = x;
18 n++;
19 }
20 }
21 while (x >= 0);
22 // xu ly
23 daytang = 1; // gia su day tang
24 for (int i = 0; i < n-1; i++)
25 if (a[i] > a[i+1]) // kiem tra tinh tang
26 daytang = 0;
27 // xuat
28 if (daytang)
29 printf("DAY TANG\n");
30 else
31 printf("DAY KHONG TANG\n");
32
33 getch();
34 }
Đầu tiên C sẽ bắt đầu thực hiện từ hàm main() (dòng 1).
Xét khi C thực hiện tới vòng lặp do...while (dòng 11). C sẽ lần lượt thực hiện các dòng
lệnh 12, 13, 14. Đến dòng 15 là dòng lệnh if, C sẽ tính toán xem (x >= 0) hay không, nếu
x >= 0 C sẽ tiếp tục thực thi dòng lệnh 17, 18, 19, rồi sang dòng 20, còn nếu x < 0 thì C
sẽ chuyển sang thực thi dòng 20 (xin lưu ý dòng 20 chính là lệnh tiếp sau lệnh if).
Khi C thực hiện tới dòng 21, chính là cuối khối lệnh do..while, C sẽ kiểm tra biểu thức
điều kiện trong while (x >= 0), nếu biểu thức này thỏa C sẽ quay lại lệnh 11, nếu không C
sẽ thực hiện lệnh 23.
Khi C thực hiện tới dòng 24 : lệnh lặp for. Đầu tiên C sẽ thực hiện int i = 0. Sau đó kiểm
tra i < n-1, nếu đúng thì qua dòng 25, nếu không thì kết thúc dòng for (dòng 28).

Khi đến dòng 25 : lệnh rẽ nhánh if, C sẽ kiểm tra điều kiện (a[i] > a[i+1]) nếu đúng thì
qua dòng 26 rồi kết thúc lệnh if, còn nếu sai thì cũng thúc lệnh if. Nhưng do lệnh if nằm
trong for cho nên C sẽ tiếp tục thực thi dòng for, tức là quay lại dòng 24.
Các đoạn sau tương tự như trên.
2.3 Lệnh gọi hàm :
Khi gặp một lệnh gọi hàm f, C sẽ thực thi như sau :
Đầu tiên C sẽ di chuyển đến đầu hàm f và thực thi từ trên xuống dưới hàm này. Sau khi
thực thi xong hàm, C sẽ quay trở lại thực thi lệnh ngay sau lệnh gọi hàm. Qui trình
được diễn tả trong sơ đồ sau :
3 Cách sử dụng debug trong Borland C++ 3.1 :
Đây là các chức năng dùng để debug trong BC3.1
Phím tắt Chức năng Ý nghĩa
F7 Trace into Dò từng dòng lệnh một. Nếu gặp hàm thì sẽ dò vào
trong hàm.
F8 Step over Tương tự Trace into, nhưng không dò vào trong
hàm.
F4 Go to cursor Yêu cầu C thực thi cho đến khi gặp dòng lệnh ngay
tại vị trí con trỏ thì ngừng để dò vết.
Ctrl-F8 Breakpoint Đặt những điểm dừng tương tự như F4.
Ctrl-F2 Program reset Dừng việc debug.
Đây là bảng các chức năng xem giá trị trong khi debug.
Phím tắt Chức năng Ý nghĩa
Ctrl-F4 Evaluate/Modify Xem giá trị của một biểu thức có các biến hiện thời
hoặc sửa giá trị của một biến
Ctrl-F7 Watch Đưa một biến/biểu thức vào cửa sổ watch để theo
dõi khi debug.
....
void f()
{
}
....
void main()
{
....
f();
lệnh;
...
}
1
2
3
4