65.9K
CodeProject 正在变化。 阅读更多。
Home

有用的指针验证宏

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.33/5 (5投票s)

2002年10月21日

2分钟阅读

viewsIcon

171513

downloadIcon

532

一些宏用于验证传递给函数的指针

引言

大多数人都知道各种库提供的实用宏,例如ASSERT, VERIFY(以及类似的),它们只是验证提供的参数。如果验证结果为false,则会引发调试通知。

在许多情况下,这对于简单的验证来说已经足够了,但是验证指针并不是这种方法的优势。看看这个例子

void MyFunction(LPSOMESTRUCT pData)
{
  ASSERT(pData != NULL);
  // ... more code
}

当您将NULL指针传递给此函数时,它会正确检测到它,但是如果您传递0xcdcdcdcd会怎样?它不是NULL,并且很可能也不是有效的地址。ASSERT不会捕获它,并且您的应用程序将抛出异常。

更多宏

在这里,需要一个更高级的解决方案。一种可能的解决方案是使用 Windows API 提供的函数:IsBadReadPtr()IsBadWritePtr()IsBadStringPtr()。这些函数将内存位置和大小作为参数,并验证调用进程是否真的具有对该位置的读取和/或写入权限。可能该位置的内存只能由您的进程部分访问,或者该内存是只读或只写的。这些函数还可以检测这些情况。

我已经将这些函数包装成方便的宏,您可以像使用ASSERTVERIFY宏一样使用它们。

#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 版本一起使用。无论如何,使用这些宏是您自己的责任。 :)

许可证

本文未附加明确的许可证,但可能在文章文本或下载文件本身中包含使用条款。如有疑问,请通过下面的讨论区联系作者。

作者可能使用的许可证列表可以在此处找到。

© . All rights reserved.