有用的指针验证宏






4.33/5 (5投票s)
2002年10月21日
2分钟阅读

171513

532
一些宏用于验证传递给函数的指针
引言
大多数人都知道各种库提供的实用宏,例如ASSERT, VERIFY
(以及类似的),它们只是验证提供的参数。如果验证结果为false
,则会引发调试通知。
在许多情况下,这对于简单的验证来说已经足够了,但是验证指针并不是这种方法的优势。看看这个例子
void MyFunction(LPSOMESTRUCT pData)
{
ASSERT(pData != NULL);
// ... more code
}
当您将NULL
指针传递给此函数时,它会正确检测到它,但是如果您传递0xcdcdcdcd
会怎样?它不是NULL
,并且很可能也不是有效的地址。ASSERT
不会捕获它,并且您的应用程序将抛出异常。
更多宏
在这里,需要一个更高级的解决方案。一种可能的解决方案是使用 Windows API 提供的函数:IsBadReadPtr()
,IsBadWritePtr()
,IsBadStringPtr()
。这些函数将内存位置和大小作为参数,并验证调用进程是否真的具有对该位置的读取和/或写入权限。可能该位置的内存只能由您的进程部分访问,或者该内存是只读或只写的。这些函数还可以检测这些情况。
我已经将这些函数包装成方便的宏,您可以像使用ASSERT
和VERIFY
宏一样使用它们。
#ifdef _DEBUG
#define VERIFY_ISWRITEPOINTER(a) \
{ if(::IsBadWritePtr(a, sizeof(LPDWORD))) \
{ ::OutputDebugString(_T("Parameter ") _T(#a) \
_T(" is not a valid write pointer\r\n"));}}
#define VERIFY_ISREADPOINTER(a) \
{ if(::IsBadReadPtr(a, sizeof(LPDWORD)))\
{ ::OutputDebugString(_T("Parameter ") _T(#a) \
_T(" is not a valid read pointer\r\n"));}}
#define VERIFY_ISWRITEDATA(a, l)\
{ if(::IsBadWritePtr(a, l)) \
{ ::OutputDebugString(_T("Parameter ") _T(#a) \
_T(" is not a valid write area\r\n"));}}
#define VERIFY_ISREADDATA(a, l)\
{ if(::IsBadReadPtr(a, l)) \
{ ::OutputDebugString(_T("Parameter ") _T(#a) \
_T(" is not a valid read area\r\n"));}}
#define ASSERT_ISWRITEPOINTER(a)\
{ if(::IsBadWritePtr(a, sizeof(LPDWORD))) \
{ ::OutputDebugString(_T("Parameter ") _T(#a) \
_T(" is not a valid write pointer\r\n")); ASSERT(false);}}
#define ASSERT_ISREADPOINTER(a)\
{ if(::IsBadReadPtr(a, sizeof(LPDWORD))) \
{ ::OutputDebugString(_T("Parameter ") _T(#a) \
_T(" is not a valid read pointer\r\n")); ASSERT(false);}}
#define ASSERT_ISWRITEDATA(a, l)\
{ if(::IsBadWritePtr(a, l)) \
{ ::OutputDebugString(_T("Parameter ") _T(#a) \
_T(" is not a valid write area\r\n")); ASSERT(false);}}
#define ASSERT_ISREADDATA(a, l) { if(::IsBadReadPtr(a, l)) \
{ ::OutputDebugString(_T("Parameter ") _T(#a)\
_T(" is not a valid read area\r\n")); ASSERT(false);}}
#else
#define VERIFY_ISWRITEPOINTER(a)
#define VERIFY_ISREADPOINTER(a)
#define VERIFY_ISWRITEDATA(a, l)
#define VERIFY_ISREADDATA(a, l)
#define ASSERT_ISWRITEPOINTER(a)
#define ASSERT_ISREADPOINTER(a)
#define ASSERT_ISWRITEDATA(a, l)
#define ASSERT_ISREADDATA(a, l)
#endif
我们之前的示例可以更改为这样
void MyFunction(LPSOMESTRUCT pData)
{
ASSERT_ISREADDATA(pData, sizeof(SOMESTRUCT));
// ... more code
}
现在,当您传递地址0xcdcdcdcd
或该函数无法从中读取至少sizeof(SOMESTRUCT)
字节的任何其他位置时,它将正确断言,并且调试输出将显示“Parameter pData is not a valid read area
”。
我发现这在编写接受输入或输出指针的函数时是一个有价值的工具。许多与错误指针相关的问题可以通过使用这些验证宏轻松解决。
兼容性
这与任何 Windows 版本兼容,没有限制。它可以与任何 Visual C++ 版本和所有 eVC 版本一起使用。无论如何,使用这些宏是您自己的责任。 :)
许可证
本文未附加明确的许可证,但可能在文章文本或下载文件本身中包含使用条款。如有疑问,请通过下面的讨论区联系作者。
作者可能使用的许可证列表可以在此处找到。