assert() – 1 công cụ gỡ lỗi hữu ích
Trang 1 trong tổng số 1 trang
assert() – 1 công cụ gỡ lỗi hữu ích
hihi, học gần 3 năm rồi, giờ mình mới biết đến hàm assert(), nên post lên đây mọi người cùng tham khảo luôn.
Có thể bạn chưa bao giờ nghe nói tới assert(), hoặc
bạn biết nó nhưng ít khi sử dụng, hoặc bạn thường xuyên sử dụng nó,
nhưng một điều mình tin chắc là khi bạn đã sử dụng quen nó rồi, bạn sẽ
đồng ý đó là một công cụ để gỡ lỗi rất lợi hại. Dạng thức của
nó như sau:
chương trình của bạn có định nghĩa NDEBUG hay không (bạn có thể định
nghĩa NDEBUG bằng nhiều cách khác nhau, như là sử dụng command line nếu
như compiler của bạn cho phép làm việc đó, hoặc là bạn tự định nghĩa nó
trong chương trình bằng cách thêm một dòng sau : #define NDEBUG).
Nếu trong chương trình của bạn NDEBUG không được định nghĩa thì khi gặp assert(), biểu thức bên trong assert() (tức là expression) sẽ được thực hiện. Nếu kết quả của biểu thức là true, chương trình tiếp tục thực hiện các câu lệnh tiếp theo assert(). Nếu biểu thức có kết quả false, chương trình của bạn sẽ dừng lại tại điểm đó, và thông thường bạn sẽ nhận được thông báo về điều đó.
Nếu trong chương trình của bạn NDEBUG đã được định nghĩa trước khi chương trình thực hiện tới dòng lệnh có chứa assert() thì maco này không làm gì cả (expression sẽ không được thực hiện, và bản thân macro sẽ được thay thế bằng Null Statement, tức là nó không có ảnh hưởng gì tới chương trình).
Sử dụng đặc điểm này của assert(), chúng ta có thể
viết một chương trình ít lỗi hơn. Bí quyết là chúng ta nên thường xuyên
dùng nó để kiểm tra các tham số của hàm, kết quả hàm trả lại, v.v…Để rõ
hơn, mình xin đưa ra một vài ví dụ minh hoa. Giả sử bạn phải viết một
chương trình nén dữ liệu, đâu đó trong chương trình của ban sẽ có một
hàm giống như sau:
khi hàm AllocBuffer() được gọi, nó luôn luôn nhận được một giá trị
nSize hợp lệ. Ngoài việc dùng để kiểm tra xem các tham số của hàm có
hợp lệ ha không, assert() cũng có thể dùng để kiểm tra nhiều giá trị khác nữa, ví dụ như giá trị trả lại từ một hàm:
quả hàm trả lại sẽ luôn luôn nằm trong khoảng (-100, 100), vì thế chúng
ta có thể dùng assert() để kiểm tra xem hàm chúng ta viết có thực hiện đúng hay không.
Ngoài ra còn rất nhiều trường hợp khác mà bạn có thể dùng assert() trong chương trình của bạn. Những ví dụ trên chỉ nhằm giúp bạn hiểu được idea của việc dùng assert() mà thôi.
Sau khi chương trình của bạn đã được test thử nhiều lần để tháo gỡ
các lỗi, sẽ đễn một thời điểm bạn phải tung ra thị trường Release
Version. Khi đó, bạn có thể disable các assert() dùng
trong chương trình của bạn. Bạn làm việc đó một cách dễ dàng, bằng cách
định nghĩa NDEBUG theo những cách mà mình đã nói ở trên.
Khi dùng assert(), bạn phải hết sức chú ý khi nào nên dùng và khi nào không. Về nguyên tắc, chúng ta dùng assert()
để phát hiện ra những bugs do chính chúng ta, những người lập trình vì
vô tình mà gây ra : gọi một hàm với những tham số không hợp lệ, sử dụng
sai thuật toán, v.v… Có những trường hợp chúng ta không nên dùng assert() để kiểm tra, mà phải dùng cơ chế sử lý lỗi trong thoi gian chay chương trình (runtime). Ví dụ :
chỉ có thể dùng để phát hiện ra nhưng lỗi, mà một khi chương trình viết
đúng, thì chúng không còn tồn tại nữa. Nhưng trong trường hợp ở đây, dù
chương trình chúng ta viết có chính xác đi chăng nữa, nhưng cũng không
thể biết được là liệu malloc() cóa trả lại null pointer hay không.
Một điều cần phải chú ý nữa khi dùng assert(), đó là biểu thức expression
chỉ được thực hiện ở chế độ Debug (tức là khi NDEBUG không được định
nghĩa), do đó chúng ta không nên viết những dòng lệnh kiểu như sau:
mà hàm trả lại có hợp lệ hay không. Nhưng khi biên dịch đoạn mã nguồn
này ở chế độ Release (khi NDEBUG được định nghĩa) thì toàn bộ dòng lệnh
trên sẽ được thay thế bởi Null Statement, và như thê hàm GetBuf() sẽ
không được gọi, và con trỏ pBuf cũng sẽ có giá trị bất kỳ.
trích diendantinhoc.org
Có thể bạn chưa bao giờ nghe nói tới assert(), hoặc
bạn biết nó nhưng ít khi sử dụng, hoặc bạn thường xuyên sử dụng nó,
nhưng một điều mình tin chắc là khi bạn đã sử dụng quen nó rồi, bạn sẽ
đồng ý đó là một công cụ để gỡ lỗi rất lợi hại. Dạng thức của
nó như sau:
Mình xin được nói rõ, assert() là macro được định nghĩa trong assert.h, tức là muốn dùng nó, chương trình của bạn phai có một dòng như sau:preassert(expression);
Những gì macro này thực hiện, phụ thuộc vào một điều kiện là trong#include " assert.h"
chương trình của bạn có định nghĩa NDEBUG hay không (bạn có thể định
nghĩa NDEBUG bằng nhiều cách khác nhau, như là sử dụng command line nếu
như compiler của bạn cho phép làm việc đó, hoặc là bạn tự định nghĩa nó
trong chương trình bằng cách thêm một dòng sau : #define NDEBUG).
Nếu trong chương trình của bạn NDEBUG không được định nghĩa thì khi gặp assert(), biểu thức bên trong assert() (tức là expression) sẽ được thực hiện. Nếu kết quả của biểu thức là true, chương trình tiếp tục thực hiện các câu lệnh tiếp theo assert(). Nếu biểu thức có kết quả false, chương trình của bạn sẽ dừng lại tại điểm đó, và thông thường bạn sẽ nhận được thông báo về điều đó.
Nếu trong chương trình của bạn NDEBUG đã được định nghĩa trước khi chương trình thực hiện tới dòng lệnh có chứa assert() thì maco này không làm gì cả (expression sẽ không được thực hiện, và bản thân macro sẽ được thay thế bằng Null Statement, tức là nó không có ảnh hưởng gì tới chương trình).
Sử dụng đặc điểm này của assert(), chúng ta có thể
viết một chương trình ít lỗi hơn. Bí quyết là chúng ta nên thường xuyên
dùng nó để kiểm tra các tham số của hàm, kết quả hàm trả lại, v.v…Để rõ
hơn, mình xin đưa ra một vài ví dụ minh hoa. Giả sử bạn phải viết một
chương trình nén dữ liệu, đâu đó trong chương trình của ban sẽ có một
hàm giống như sau:
Bạn biết rằng để hàm này thực hiện được thì 2 tham số của hàm phải là 2 con trỏ hợp lệ, vì vậy bạn có thể dùng assert() để phát hiện ra trường hợp bạn truyền Null Pointer cho hàm như sau:void Compress (const char *pszSrcFileName, const char *pszDestFileName)
{
/* code goes here */
}
Bây giờ, nếu như bạn có vô tình gọi hàm Compress() với Null Pointer thì bạn sẽ phát hiện ra ngay. Mặc dù assert() hay đựơc dùng để kiểm tra Null Pointer, bạn cũng có thể dùng nó để kiểm tra các kiểu dữ liệu khác nhau. Ví dụ:void Compress (const char *pszSrcFileName, const char *pszDestFileName)
{
assert(pszSrcFileName && pszDestFileName);
/*
hoac cung co the viet nhu sau:
assert(pszSrcFileName);
assert(pszDestFileName);
*/
/* code goes here */
}
Ở đây, chúng ta đã dùng assert() để đảm bảo là mỗichar *AllocBuffer(int nSize)
{
assert(nSize > 0 && nSize < MAX_SIZE);
/* code goes here */
}
khi hàm AllocBuffer() được gọi, nó luôn luôn nhận được một giá trị
nSize hợp lệ. Ngoài việc dùng để kiểm tra xem các tham số của hàm có
hợp lệ ha không, assert() cũng có thể dùng để kiểm tra nhiều giá trị khác nữa, ví dụ như giá trị trả lại từ một hàm:
Trong ví dụ này, chúng ta biết rằng nếu hàm thực hiện đúng thì kếtint SomeFunc(...)
{
/* compute here and store result in variable called res */
assert(res >= -100 && res <= 100);
return res;
}
quả hàm trả lại sẽ luôn luôn nằm trong khoảng (-100, 100), vì thế chúng
ta có thể dùng assert() để kiểm tra xem hàm chúng ta viết có thực hiện đúng hay không.
Ngoài ra còn rất nhiều trường hợp khác mà bạn có thể dùng assert() trong chương trình của bạn. Những ví dụ trên chỉ nhằm giúp bạn hiểu được idea của việc dùng assert() mà thôi.
Sau khi chương trình của bạn đã được test thử nhiều lần để tháo gỡ
các lỗi, sẽ đễn một thời điểm bạn phải tung ra thị trường Release
Version. Khi đó, bạn có thể disable các assert() dùng
trong chương trình của bạn. Bạn làm việc đó một cách dễ dàng, bằng cách
định nghĩa NDEBUG theo những cách mà mình đã nói ở trên.
Khi dùng assert(), bạn phải hết sức chú ý khi nào nên dùng và khi nào không. Về nguyên tắc, chúng ta dùng assert()
để phát hiện ra những bugs do chính chúng ta, những người lập trình vì
vô tình mà gây ra : gọi một hàm với những tham số không hợp lệ, sử dụng
sai thuật toán, v.v… Có những trường hợp chúng ta không nên dùng assert() để kiểm tra, mà phải dùng cơ chế sử lý lỗi trong thoi gian chay chương trình (runtime). Ví dụ :
Tại sao không thể dùng được assert() trong trường hợp này? Bởi vì assert()char *pBuf = (char *)malloc(100);
if (!pBuf)
{
/* su ly truong hop malloc() tra ve null pointer o day */
}
chỉ có thể dùng để phát hiện ra nhưng lỗi, mà một khi chương trình viết
đúng, thì chúng không còn tồn tại nữa. Nhưng trong trường hợp ở đây, dù
chương trình chúng ta viết có chính xác đi chăng nữa, nhưng cũng không
thể biết được là liệu malloc() cóa trả lại null pointer hay không.
Một điều cần phải chú ý nữa khi dùng assert(), đó là biểu thức expression
chỉ được thực hiện ở chế độ Debug (tức là khi NDEBUG không được định
nghĩa), do đó chúng ta không nên viết những dòng lệnh kiểu như sau:
Ở đây, chúng ta muốn gọi hàm GetBuf(), sau đó kiểm tra xem con trỏassert(pBuf = GetBuf());
mà hàm trả lại có hợp lệ hay không. Nhưng khi biên dịch đoạn mã nguồn
này ở chế độ Release (khi NDEBUG được định nghĩa) thì toàn bộ dòng lệnh
trên sẽ được thay thế bởi Null Statement, và như thê hàm GetBuf() sẽ
không được gọi, và con trỏ pBuf cũng sẽ có giá trị bất kỳ.
trích diendantinhoc.org
Re: assert() – 1 công cụ gỡ lỗi hữu ích
Assert and Subscript Checking
Many of the techniques used in writing robust C code also apply in
C++. For example, if you have a function that is supposed to be
passed a person's name, as a C-style string, it would be wise to say:
function (actually a macro) that checks whether its argument is true
(non-zero), and aborts the program if not.
But C++ offers additional opportunities to the designer interested in
producing quality code. For example, consider a common problem in C,
where vector bounds are not checked during a dereference operation,
and a bad location is accessed or written to.
In C++, you can partially solve this problem by defining a Vector
class, with a vector dereferencing class member defined for the
Vector, and the vector size stored:
In this example, we create a vector of 10 elements, and the vector is
indexed 1..10. If the vector is dereferenced illegally, as in:
v[0] = 0;
an assertion failure will be triggered.
One objection to this technique is that it can be slow. If every
vector reference requires a function call (to Vector::operator[]),
then there may be a large performance hit. However, performance
concerns can be dealt with by making the dereferencing function inline.
Two other comments about the above example. We are assuming in these
newsletters that if operator new() fails, it returns a NULL pointer:
an exception is thrown or else a new handler is invoked. Because many
C++ implementations still use the old approach of returning NULL, we
will stick with it for now.
The other comment concerns the use of references. In the code:
the actual code is equivalent to:
and could actually be written this way (see a C++ reference book on
operator overloading for details).
Vector::operator[] returns a reference, which can be used on the
left-hand side of an assignment expression. In C the equivalent code
would be more awkward:
Many of the techniques used in writing robust C code also apply in
C++. For example, if you have a function that is supposed to be
passed a person's name, as a C-style string, it would be wise to say:
to perform basic checks on the passed-in pointer. assert() is a#include "assert.h"
void f(char* name)
{
assert(name && *name);
...
}
function (actually a macro) that checks whether its argument is true
(non-zero), and aborts the program if not.
But C++ offers additional opportunities to the designer interested in
producing quality code. For example, consider a common problem in C,
where vector bounds are not checked during a dereference operation,
and a bad location is accessed or written to.
In C++, you can partially solve this problem by defining a Vector
class, with a vector dereferencing class member defined for the
Vector, and the vector size stored:
#include "stdio.h"
#include "assert.h"
class Vector {
int len; // number of elements
int* ptr; // pointer to elements
public:
Vector(int); // constructor
~Vector(); // destructor
int& operator[](int); // dereferencing
};
//constructor
Vector::Vector(int n)
{
assert(n >= 1);
len = n; // store length
ptr = new int[n]; // get storage to store elements
assert(ptr);
}
//destructor
Vector::~Vector()
{
delete ptr;
}
//dereferencing
int& Vector::operator[](int i)
{
assert(i >= 1 && i <= len);
return ptr[i - 1]; // return reference to vector slot
}
//driver program
main()
{
int i;
const int N = 10;
Vector v(N);
for (i = 1; i <= N; i++) // correct usage
v[i] = i * i;
for (i = 1; i <= N; i++) // correct usage
printf("%d %d\n", i, v[i]);
v[0] = 0; // will trigger assert failure
return 0;
}
In this example, we create a vector of 10 elements, and the vector is
indexed 1..10. If the vector is dereferenced illegally, as in:
v[0] = 0;
an assertion failure will be triggered.
One objection to this technique is that it can be slow. If every
vector reference requires a function call (to Vector::operator[]),
then there may be a large performance hit. However, performance
concerns can be dealt with by making the dereferencing function inline.
Two other comments about the above example. We are assuming in these
newsletters that if operator new() fails, it returns a NULL pointer:
The current draft ANSI standard says that when such a failure occurs,ptr = new int[n];
assert(ptr); // check for non-NULL pointer
an exception is thrown or else a new handler is invoked. Because many
C++ implementations still use the old approach of returning NULL, we
will stick with it for now.
The other comment concerns the use of references. In the code:
v[i] = i * i;
the actual code is equivalent to:
v.operator[](i) = i * i;
and could actually be written this way (see a C++ reference book on
operator overloading for details).
Vector::operator[] returns a reference, which can be used on the
left-hand side of an assignment expression. In C the equivalent code
would be more awkward:
#include "stdio.h"
int x[10]; // use f() to index into x[10]
int* f(int i)
{
return &x[i - 1];
}
main()
{
*f(5) = 37;
printf("%d %d\n", *f(5), x[4]);
return 0;
}
Similar topics
» công dụng của foreach trong C#
» Bài giảng môn Công cụ lập trình Mạng
» Có đi thực tập ở công ty larion hay không?
» Khắc phục sự cố không vào được facebook
» Bài giảng môn Công cụ lập trình Mạng
» Có đi thực tập ở công ty larion hay không?
» Khắc phục sự cố không vào được facebook
Trang 1 trong tổng số 1 trang
Permissions in this forum:
Bạn không có quyền trả lời bài viết
|
|