Trang 1 trên tổng số 2 12 Cuối cùngCuối cùng
Từ 1 tới 10 trên tổng số 16 kết quả

Đề tài: Nghệ thuật Traps Code trong Visual C++

  1. #1
    Ngày gia nhập
    01 2008
    Nơi ở
    Gameloft Studio
    Bài viết
    294

    Mặc định Nghệ thuật Traps Code trong Visual C++

    I. MỞ ĐẦU

    Traps Code là gì vậy? Nói nôn na đó là 1 kỹ thuật Debug bằng cách đặt bẫy trên những đoạn code.

    Dĩ nhiên bất kỳ ai trong chúng ta học lập trình cũng đều biết Debug. Nhưng có bao giờ bạn rơi vào trường hợp phải khóc vì Debug chưa? Ví dụ như là Debug trong MultiThread hay trong Đệ Quy chẳn hạn.

    Thậm chí có lúc bạn phải lập trình mà ko cần Debug. Ví dụ như lập trình PHP chẳn hạn. Các trình dịch GCC thì Debug lại khó khăn hơn VC++ rất nhiều.

    Do đó hầu như trình biên dịch nào cũng hộ trợ một số function giúp bạn làm việc này tốt hơn.

    Mình sẽ mở đầu đề tài này bằng 1 đoạn code tương đối kỳ cục như sau:

    C++ Code:
    1. int _tmain(int argc, _TCHAR* argv[])
    2. {
    3.     {
    4.         int i = 100;
    5.         printf("%d\n",i);
    6.     }
    7.  
    8.     {
    9.         int i = 100;
    10.         printf("%d\n",i);
    11.     }
    12.  
    13.     return 0;
    14. }

    Đoạn code trên không sinh ra lỗi (vì mình đã dịch rồi) mặc dù int i được khai báo tới 2 lần nhưng nó lại ở 2 block khác nhau { }.

    Trình biên dịch hỗ trợ điều này để giúp ta viết MACRO. Ta sẽ viết đoạn code trên bằng macro DUMP như sau:

    C++ Code:
    1. #define DUMP() \
    2.     {\
    3.         int i = 100;\
    4.         printf("%d\n",i);\
    5.     }
    6.  
    7. int _tmain(int argc, _TCHAR* argv[])
    8. {
    9.     DUMP();
    10.     DUMP();
    11.     DUMP();
    12.     return 0;
    13. }

    Kết quả sẽ ra 3 lần sô 100 (giá trị i).

    Tiếp tục 1 lần nữa nhé:
    C++ Code:
    1. #define DUMP_INT(VAL) {printf("%s = %d\n",#VAL,VAL);}
    2.  
    3. int _tmain(int argc, _TCHAR* argv[])
    4. {
    5.     int a = 100;
    6.     int b = 200;
    7.     int c = 300;
    8.  
    9.     DUMP_INT(a);
    10.     DUMP_INT(b);
    11.     DUMP_INT(c);
    12.  
    13.     return 0;
    14. }

    Trong đoạn macro trên thì VAL mang giá trị của biến, còn #VAL lại mang tên biến. Do đó nó xuất ra trên màn hình như sau:

    Trích dẫn Nguyên bản được gửi bởi Kết quả output:
    a = 100
    b = 200
    c = 300
    Press any key to continue . . .
    Hy vọng nhưng đoạn code kỳ quặc trên sẽ làm bạn thấy 1 chút gì đó thú vị.
    Mình sẽ tiếp tục bài viết này sau...

  2. #2
    Ngày gia nhập
    01 2008
    Nơi ở
    Gameloft Studio
    Bài viết
    294

    II. BUG FILE & LINE CODE

    Bây giờ code tiếp 1 đoạn đơn giản thôi:

    C Code:
    1. int main()
    2. {
    3.     printf("%s - %d\n", __FILE__, __LINE__);
    4. }

    Kết quả sẽ là:
    Trích dẫn Nguyên bản được gửi bởi Output
    c:\users\compaq\desktop\debugpointer\debugpointer\ debugpointer.cpp - 97
    Press any key to continue . . .
    Ở trên ta dùng 2 macro đặc biệt được trình biên dịch khai báo hỗ trợ.

    __FILE__: Trả về tên code file (.c hay .cpp) đang thực thi hành
    __LINE__: Trả về dòng đang thi hành.

    Kết quả in ra như vậy vì hàm printf của mình nằm ở dòng thứ 97.

    Chúng ta sẽ lợi dùng 2 macro này để hỗ trợ đánh dấu BUG.
    Đã được chỉnh sửa lần cuối bởi ZCoder87 : 11-11-2008 lúc 11:33 AM.

  3. #3
    Ngày gia nhập
    01 2008
    Nơi ở
    Gameloft Studio
    Bài viết
    294

    III. TRACE

    Trong các trường hợp trên mình đều in kết quả ra màn hình Console nhưng không hẳn trong mọi chương trình của sổ console luôn xuất hiện.

    Chúng ta cũng có thể chọn giải pháp in bug ra file. Và kiểm soát lỗi trên file LOG này.

    Tuy nhiên mình sẽ bug ra 1 của sổ tương đối khá đặc biệt. Đó chính là cửa sổ Ouput của trình biên dịch.

    Ta sẽ làm quen với hàm: OutputDebugStringA. Hàm này của WINAPI (windows.h). Hầu như mọi trình biên dịch đều hỗ trợ nó.

    C Code:
    1. #include <windows.h>
    2. int main()
    3. {
    4.     char lpStringDebug[100];
    5.     sprintf(lpStringDebug,"MY DEBUG: %s - %d\n", __FILE__, __LINE__);
    6.     OutputDebugStringA(lpStringDebug);
    7. }

    Kết quả màn hình là dòng mình đã gạch chân bằng màu đỏ.
    Click vào hình ảnh để lấy hình ảnh lớn

Tên:		TRACE.jpg
Lần xem:	90
Size:		48.2 KB
ID:		7978

    Output của trình biên dịch là một cửa sổ khá lý tưởng để DEBUG. Tuy nhiên đoạn code Debug trên làm mình mất hết 3 dòng CODE và rất lằng nhằng nữa. Hình sẽ cải tiến nó thành 1 Macro như sau.

    C Code:
    1. #define ZDEBUG_TRACE(FORMAT,...) \
    2.     { \
    3.         char debugString[200]; \
    4.         sprintf(debugString,FORMAT, __VA_ARGS__); \
    5.         OutputDebugStringA(debugString); \
    6.     }

    Lúc này mình sẽ đơn giản hơn:

    C++ Code:
    1. int main()
    2. {  
    3.     ZDEBUG_TRACE("MY DEBUG: %s - %d\n", __FILE__, __LINE__);
    4. }
    Đã được chỉnh sửa lần cuối bởi ZCoder87 : 11-11-2008 lúc 11:33 AM.

  4. #4
    Ngày gia nhập
    08 2008
    Bài viết
    46

    Tớ hỏi chút: macro là gì? Viết macro là như thế nào?
    Hạnh phúc luôn đợi ta mỉn cười lại với nó.(^,,^).

  5. #5
    Ngày gia nhập
    01 2008
    Nơi ở
    Gameloft Studio
    Bài viết
    294

    Uổng công tớ viết phần "mở đầu". Chắc tớ viết khó hiểu quá hả?

    C Code:
    1. #define PI 3.14
    2.  
    3. void main()
    4. {
    5.        printf("%f", PI);
    6. }

    PI chính là Macro đó.
    Macro là một sự thay thế. Nó được trình biên dịch dịch trước khi chương trình bạn chạy.

    Bạn có thể hiểu như là 1 cách viết tắt.
    Ví dụ ta thường viết tắt là "TP HCM" nhưng khi đọc thì sẽ là "Thành Phố Hồ Chí Minh".

    Mình lấy lại ví dụ DUMP ban đầu.
    C Code:
    1. #define DUMP() \
    2.     {\
    3.         int i = 100;\
    4.         printf("%d\n",i);\
    5.     }
    6.  
    7. int _tmain(int argc, _TCHAR* argv[])
    8. {
    9.     DUMP();
    10.     DUMP();
    11.     DUMP();
    12.     return 0;
    13. }

    Khi bạn khai báo Macro DUMP như vậy thì khi trình biên dịch hoạt động nó sẽ thay thế chỗ nào có "DUMP thành đoạn lệnh trên".

    Lúc này đoạn code trên thực sự là như thế này:
    C Code:
    1. int _tmain(int argc, _TCHAR* argv[])
    2. {
    3.    // DUMP sẽ thay thế thành
    4.    {
    5.         int i = 100;
    6.         printf("%d\n",i);
    7.     }
    8.    // DUMP sẽ thay thế thành
    9.    {
    10.         int i = 100;
    11.         printf("%d\n",i);
    12.     }
    13.    // DUMP sẽ thay thế thành
    14.    {
    15.         int i = 100;
    16.         printf("%d\n",i);
    17.     }
    18. }

  6. #6
    Ngày gia nhập
    11 2008
    Bài viết
    186

    Mặc định Nghệ thuật Traps Code trong Visual C++

    Bài viết của anh Z hay quá
    em thấy lão X code chuyên xài Macro, nhiều cái đọc không tài nào hiểu nổi -_-;;
    Tiếp tục kĩ thuật trap đi anh Z. Thêm nữa, nếu anh có thể thì truyền bá kĩ thuật trap trên Linux luôn nhé. Vì linux không có mấy API của Windows

  7. #7
    Ngày gia nhập
    01 2008
    Nơi ở
    Gameloft Studio
    Bài viết
    294

    Xin cảm ơn bạn!

    III. TRACE (tiếp)

    Bây giờ mình sẽ hoàn chỉnh hơn lệnh TRACE
    C Code:
    1. #include <stdio.h>
    2. #include <string.h>
    3. #include <stdlib.h>
    4.  
    5. // Nếu chạy ở chế độ DEBUG (F5)
    6. #ifdef _DEBUG
    7.     #define ZDEBUG_TRACE(FORMAT,...) \
    8.     { \
    9.         char debugString[200]; \
    10.         sprintf(debugString,FORMAT, __VA_ARGS__); \
    11.         OutputDebugStringA(debugString); \
    12.     }
    13.  
    14.  
    15. // Nếu chạy ở mode Release (Ctrl + F5) -> Macro không giá trị!
    16. #else
    17.     #define ZDEBUG_TRACE(FORMAT,...)
    18. #endif
    19.  
    20. double giaithua(int k)
    21. {
    22.     ZDEBUG_TRACE("Module: giaithua(%d)\n",k);
    23.     if (k == 1)
    24.     {
    25.         ZDEBUG_TRACE("Result Module: giaithua(1) return 1\n");
    26.         return 1;
    27.     }
    28.     double r = k*giaithua(k-1);
    29.     ZDEBUG_TRACE("Result Module: giaithua(%d) return %f\n",k,r);
    30.     return r;
    31. }
    32.  
    33. int _tmain(int argc, _TCHAR* argv[])
    34. {
    35.    
    36.     double f = giaithua(10);
    37.     printf("%f\n", f);
    38.     return 1;
    39. }

    Đây là cách mình đã traps 1 hàm đệ Quy đơn giản nhất.

    Kết quả không có gì đặc biệt từ của sổ Console:
    3628800.000000
    Press any key to continue . . .
    Và màn hình Output thể hiện stack của đệ quy
    Click vào hình ảnh để lấy hình ảnh lớn

Tên:		OutputResultTRACE.jpg
Lần xem:	82
Size:		86.5 KB
ID:		7979

    Màn hình output này chỉ chạy khi bạn bấm F5 (Còn chạy bằng Ctrl+F5 hay .exe từ file thì sẽ không hiển thị).
    Bởi vì mình chỉ định nghĩa Macro ZDEBUG_TRACE chạy ở mode _DEBUG

    Hy vọng sẽ giúp cho các bạn một chút kiến thức gì đó.

    Còn tiếp... mình sẽ cập nhật sau.
    Đã được chỉnh sửa lần cuối bởi ZCoder87 : 12-11-2008 lúc 10:28 PM.

  8. #8
    Ngày gia nhập
    01 2008
    Nơi ở
    Gameloft Studio
    Bài viết
    294

    IV. ASSERT

    Assert lại là một cách kiểm lỗi khác.
    Thông thường nó có những lỗi không hề xuất hiện mà phải chạy một lúc nó mới chịu báo báo ra cho chúng ta.

    Do đó ngôn ngữ C cũng đã có 1 hàm khắc phục chỗ này chính là assert. Hàm này trong thư viện "assert.h".

    Hàm này sẽ dump ra lỗi nếu giá trị đưa vào là 0 bao gồm NULL, UNHANDLE, FAILED, false...

    Nếu bạn xem các sơ qua source MFC của MS hay các soft opensource khác thì bạn thấy assert đầy rẫy vì nó giúp chúng ta xác định chính xác lỗi tại đâu.

    Mình ví dụ:

    C++ Code:
    1. #include <assert.h>
    2.  
    3. int _tmain(int argc, _TCHAR* argv[])
    4. {  
    5.     int *pBuffer = (int*) malloc(sizeof(int)*100); 
    6.     assert(pBuffer);
    7.     free(pBuffer);
    8.  
    9.     return 1;
    10. }

    hay là:

    C Code:
    1. HWND hWnd = CreateWindowA( NULL, "windows", WS_CHILD|WS_VISIBLE,
    2.            0,0,800,600,
    3.            NULL,NULL, NULL, NULL);
    4. assert(hWnd);

    Thay vì "if (pBuffer == NULL) ..." thì bạn có thể đặt assert ngay đó.

    Chương trình sẽ dừng lại ngay và chỉ rõ vị trí lỗi cho chúng ta nếu p không cấp phát được bộ nhớ (NULL).

    Đây là 1 trường hợp mình gán thẳng pBuffer = NULL và assert(pBuffer)
    Click vào hình ảnh để lấy hình ảnh lớn

Tên:		AsserFail.jpg
Lần xem:	38
Size:		34.2 KB
ID:		7980

    Nó sẽ thông báo lại tên biến, file và line lỗi. Để debug bạn có thể bấm nút Retry.

    Vì vậy. Sau khi biết hàm assert này mình nghĩ chúng ta nên tạo 1 thói quen đặt bẫy code ở các biến con trỏ hoặc có các đoạn lệnh return false. Bởi lẽ khả năng lỗi luôn đến bất cứ lúc nào? Và hơn nữa giúp người khác sử dụng chương trình của mình có thể biết lỗi ở đâu.

    Ví dụ 1 chương trình đặt bẫy ASSERT:
    C Code:
    1.  
    2. #include <assert.h>
    3.  
    4. // Cái này không hẵn là cần thiết nhưng mình muốn define nó lại.
    5. #define ZDEBUG_ASSERT(exp) assert(exp);
    6.  
    7. template<class T>
    8. void swap(T* val1, T* val2)
    9. {
    10.     // Đặt bẫy code
    11.     ZDEBUG_ASSERT(val1);
    12.     ZDEBUG_ASSERT(val2);
    13.  
    14.     T temp = *val1;
    15.     *val1 = *val2;
    16.     *val2 = temp;
    17. }
    18.  
    19. int _tmain(int argc, _TCHAR* argv[])
    20. {  
    21.    
    22.     int i = 10, j = 20;
    23.     swap<int>(&i,&j);
    24.    
    25.     printf("i = %d\n", i);
    26.     printf("j = %d\n", j);
    27.  
    28.     return 1;
    29. }

    Assert có thể chạy ở DEBUG lẫn RELEASE do đó cũng không nên để nó trong #if _DEBUG giống như TRACE.

    Còn tiếp...
    Đã được chỉnh sửa lần cuối bởi ZCoder87 : 13-11-2008 lúc 12:02 PM.

  9. #9
    Ngày gia nhập
    08 2008
    Bài viết
    46

    tại sao tớ chạy mấy chương trình của bạn thì báo lỗi ở cái biến: _TCHAR* nó chưa được định nghĩa! ?
    Hạnh phúc luôn đợi ta mỉn cười lại với nó.(^,,^).

  10. #10
    Ngày gia nhập
    11 2008
    Bài viết
    186

    Trích dẫn Nguyên bản được gửi bởi hahonga3 Xem bài viết
    tại sao tớ chạy mấy chương trình của bạn thì báo lỗi ở cái biến: _TCHAR* nó chưa được định nghĩa! ?
    @ha..3: code của anh Z chỉ mang tính chất minh họa. Bạn muốn run các code đó thì phải hiểu bản chất của code và những yếu tố cần thiết để chương trình đó chạy được (thư viện)
    Cho nên nếu cậu chưa vững những điều cơ bản như: hàm nào thuộc thư viện nào, sử dụng các hàm đơn giản thế nào, nhìn vào định nghĩa có thể hiểu cách sử dụng...
    thì thread này cậu chưa nên đọc vội
    C++ Code:
    1.  
    2. #include <tchar.h>

Các đề tài tương tự

  1. Source code visual 2008 không chay được trên visual 2005
    Gửi bởi hungmq trong diễn đàn Thắc mắc lập trình C#
    Trả lời: 9
    Bài viết cuối: 11-07-2013, 11:28 AM
  2. Tính năng cho phép người dùng tự định nghĩa report trong visual ?
    Gửi bởi nltt7777 trong diễn đàn Thắc mắc lập trình C#
    Trả lời: 4
    Bài viết cuối: 09-09-2012, 10:44 PM
  3. Class trong lập trình Visual C++ nghĩa là gì?
    Gửi bởi Zack Fair trong diễn đàn Thắc mắc lập trình Visual C++
    Trả lời: 1
    Bài viết cuối: 09-02-2011, 10:54 AM
  4. Hàm MakeWord trong Visual C++ có ý nghĩa gì?
    Gửi bởi hailuavitinh trong diễn đàn Thắc mắc lập trình Visual C++
    Trả lời: 4
    Bài viết cuối: 16-12-2010, 04:50 PM
  5. CALLBACK trong Visual C++ có ý nghĩa gì?
    Gửi bởi langman trong diễn đàn Thắc mắc lập trình Visual C++
    Trả lời: 17
    Bài viết cuối: 05-10-2010, 11:54 PM

Quyền hạn của bạn

  • Bạn không thể gửi đề tài mới
  • Bạn không thể gửi bài trả lời
  • Bạn không thể gửi các đính kèm
  • Bạn không thể chỉnh sửa bài viết của bạn