C++ 如何正确重写断言代码以在msvc中传递/分析?
Visual Studio为C/C++添加了代码分析(C++ 如何正确重写断言代码以在msvc中传递/分析?,c++,code-analysis,visual-c++,assert,C++,Code Analysis,Visual C++,Assert,Visual Studio为C/C++添加了代码分析(/analyze),以帮助识别错误代码。这是一个很好的特性,但当您处理旧项目时,您可能会被大量的警告所淹没 大多数问题都是由于旧代码在方法或函数的开头执行断言而产生的 我认为这是代码中使用的断言定义(来自afx.h) 示例代码: ASSERT(pBytes != NULL); *pBytes = 0; // <- warning C6011: Dereferencing NULL pointer 'pBytes' ASSERT(pBy
/analyze
),以帮助识别错误代码。这是一个很好的特性,但当您处理旧项目时,您可能会被大量的警告所淹没
大多数问题都是由于旧代码在方法或函数的开头执行断言而产生的
我认为这是代码中使用的断言定义(来自afx.h
)
示例代码:
ASSERT(pBytes != NULL);
*pBytes = 0; // <- warning C6011: Dereferencing NULL pointer 'pBytes'
ASSERT(pBytes!=NULL);
*pBytes=0;// 您似乎认为ASSERT(ptr)
某种程度上意味着ptr
之后不为空。这不是真的,代码分析器也没有做出这样的假设。PREFast告诉您代码中有缺陷;不要忽视它。你确实有一个,但你只是匆匆忙忙地承认了它。问题是:仅仅因为pBytes
在开发和测试中从未为空,并不意味着它不会在生产中使用。你不能处理这种可能性。PREfast知道这一点,并试图警告您,生产环境是敌对的,并且会让您的代码留下大量毫无价值的字节
/咆哮
有两种方法可以解决这个问题:正确的方法和破解
正确的方法是在运行时处理空指针:
void DoIt(char* pBytes)
{
assert(pBytes != NULL);
if( !pBytes )
return;
*pBytes = 0;
}
这会让你安静下来
技巧是使用注释。例如:
void DoIt(char* pBytes)
{
assert(pBytes != NULL);
__analysis_assume( pBytes );
*pBytes = 0;
}
编辑:描述预览注释。无论如何,这是一个起点。/analyze
不能保证产生相关且正确的警告。
它可以而且将错过很多问题,并且还会出现一些误报(它将这些问题识别为警告,但它们是完全安全的,并且永远不会发生)
期望在/analyze中没有警告是不现实的
它指出了一种情况,即您取消引用它无法验证始终有效的指针。就PREfast所知,不能保证它永远不会为空
但这并不意味着它可以为空。只是证明其安全性所需的分析对于PREfast来说太复杂了
您可以使用特定于Microsoft的扩展来告诉编译器它不应该生成此警告,但更好的解决方案是保留此警告。每次使用/analyze编译时(不必每次编译时都使用/analyze),您都应该验证它确实出现的警告仍然是误报
如果您正确使用断言(在编程过程中捕捉逻辑错误,防止发生无法发生的情况),那么我认为您的代码或保留警告没有问题。添加代码来处理永远不会发生的问题是毫无意义的。您无缘无故地添加了更多代码和更复杂的问题(如果它永远不会发生,那么你就没有办法从中恢复,因为你完全不知道程序将处于什么状态。你所知道的只是它进入了一个你认为不可能的代码路径
但是,如果您使用断言作为实际的错误处理(在异常情况下,该值可以为NULL,您只是希望它不会发生),那么这是代码中的一个缺陷。然后需要正确的错误处理(通常是异常)
永远不要对可能出现的问题使用断言。使用它们来验证不可能的事情没有发生。当/analyze向您发出警告时,请查看它们。如果是误报,请忽略它(不要抑制它,因为今天是误报,明天签入的代码可能会将其变成真正的问题).请记住,ASSERT()在零售版本中消失,因此C6011警告在上面的代码中绝对正确:您必须检查pBytes是否为非空以及执行ASSERT()。如果调试错误中满足该条件,ASSERT()只会将您的应用程序扔进调试器
我工作很多/分析和预告,所以如果你还有其他问题,请告诉我。
< P>我的合作作者David LeBlanc会告诉我,这个代码被破坏了,假设你正在使用C++,你应该使用一个引用而不是一个指针,而一个REF不能是空的:首先,断言语句必须保证抛出或终止应用程序。经过一些实验后,我发现/analysis忽略了模板函数、内联函数或普通函数中的所有实现。您必须使用宏和do{}while(0)技巧,内联抑制
如果您查看Microsoft use uu analysis u success()在其宏中的定义,他们还提供了几段非常好的文档,介绍了迁移ATL以使用此宏的原因和方式
作为一个例子,我以同样的方式修改了CPPUNIT_ASSERT宏,以清除单元测试中成千上万的警告
#define CPPUNIT_ASSERT(condition) \
do { ( CPPUNIT_NS::Asserter::failIf( !(condition), \
CPPUNIT_NS::Message( "assertion failed" ), \
CPPUNIT_SOURCELINE() ) ); __analysis_assume(!!(condition)); \
__pragma( warning( push)) \
__pragma( warning( disable: 4127 )) \
} while(0) \
__pragma( warning( pop))
您可以显示一些警告消息吗?您还可以显示您的ASSERT
实现吗?/analyze
不会在我的机器上抱怨ASSERT
。我更新了描述,顺便说一句/analyze不会抱怨ASSERT,但它知道ASSERT可以跳过,并且作为一种副作用,它会触发警告拿铁r当使用变量时。这是正确的,但它没有回答问题。呃,你盲目地假设pBytes
实际上可以是NULL
。这不一定是一个有效的假设。PREfast只是安全起见:它经常给出误报。这就是为什么它在默认情况下没有启用。它会告诉你它发现了什么可疑,并且由您来验证它是否正确,如果正确,则解决问题。但是说您实际上需要处理空案例,因为PREfast这么说纯粹是胡说八道。如果它在逻辑上确实可以发生,请处理它。断言旨在捕获逻辑错误:逻辑上永远不会发生的事情。根据定义,这些是不可能记录的呃,你会的
#define CPPUNIT_ASSERT(condition) \
do { ( CPPUNIT_NS::Asserter::failIf( !(condition), \
CPPUNIT_NS::Message( "assertion failed" ), \
CPPUNIT_SOURCELINE() ) ); __analysis_assume(!!(condition)); \
__pragma( warning( push)) \
__pragma( warning( disable: 4127 )) \
} while(0) \
__pragma( warning( pop))