Visual c++ VS2012静态分析:是否将此指针作为输出指针?

Visual c++ VS2012静态分析:是否将此指针作为输出指针?,visual-c++,visual-studio-2012,static-analysis,Visual C++,Visual Studio 2012,Static Analysis,在此代码段中,Init()函数充当按需初始值设定项,用于填充结构的所有成员变量。这样做是为了避免调用堆栈上大型数组的所有成员的默认构造函数: structfoo{ 国际货币联盟成员; void Init(int i); }; voidfoo::Init(inti){ m_成员=i; //许多其他成员在这里初始化。 } 空函数(int n){ Foo缓冲区[64]; 断言(n只需添加一个默认构造函数(调用Init())。这有什么错 [编辑]根本问题不在于如何对静态分析器或编译器撒谎。问题在于如何确

在此代码段中,Init()函数充当按需初始值设定项,用于填充结构的所有成员变量。这样做是为了避免调用堆栈上大型数组的所有成员的默认构造函数:

structfoo{
国际货币联盟成员;
void Init(int i);
};
voidfoo::Init(inti){
m_成员=i;
//许多其他成员在这里初始化。
}
空函数(int n){
Foo缓冲区[64];

断言(n只需添加一个默认构造函数(调用Init())。这有什么错

[编辑]根本问题不在于如何对静态分析器或编译器撒谎。问题在于如何确保不让foo处于未初始化状态。添加默认构造函数没有错。我认为不这样做会带来风险

也许有些客户机会使用构造糟糕的foo类(在您编写它很久之后,在您离开之后很久),也许他们会忘记调用.Init()?然后呢?他们会留下未初始化的数据

如果您希望强制执行该规则,那么无论进行多少静态分析都不会对您有所帮助


在上屋顶之前要注意基础。

你的问题有点难以捉摸。你已经显示了<代码>某个函数使用<代码> int >代码>,但是希望方法注释<代码> init <代码>或构造函数。< /p> 显示的警告绝对正确,
assert
不会隐藏警告。您需要将
if
置于
if
位置,以检查
n
是否大于
64
,并重置
n
(或者执行其他操作,但在
n>=64
时不要循环)

对于注释,您需要使用
\u in\u bcount
或类似选项。例如:

bool SetBuffer(__in_bcount(8) const char* sBuffer);
它说sBuffer的字节数是8(不是元素)


您可以阅读此内容以了解更多信息。

太难看了,无法添加额外的助手吗

struct Foo {
    int m_Member;
    void Init(int i);
};

void Foo::Init(int i) {
    m_Member = i;
    // Many other members initialized here.
}

void Initialize(__in_bcount(sizeof(Foo) * n) Foo* buffer, int n) {
    // Explicitly initialize what is needed.
    for (int i = 0; i < n; ++i) {
        buffer[i].Init(i * 3);
    }
}

void SomeFunction(int n) {
    Foo buffer[64];
    assert(n <= 64);
    Initialize(buffer, n);
    // Use buffer[0] - buffer[n-1] somehow.
}
structfoo{
国际货币联盟成员;
void Init(int i);
};
voidfoo::Init(inti){
m_成员=i;
//许多其他成员在这里初始化。
}
无效初始化(uuu in_ubcount(sizeof(Foo)*n)Foo*缓冲区,int n){
//显式初始化所需的内容。
对于(int i=0;iassert(n我通过实现一个函数对数组进行索引找到了一个解决方法。我将返回值标记为无效,以便在返回值仅用于初始化的特定情况下,此新函数仅逃避未初始化值检查。我仅在VS2017中对此进行了测试

#define _Ret_invalid_                     _SAL2_Source_(_Ret_invalid_, (), _Ret1_impl_(__notvalid_impl))

template <typename T>
_Ret_invalid_ T& UninitialzedIndex(T* pt, int index)
{
   return pt[index];
}
#定义_Ret_invalid_SAL2_源(_Ret_invalid_,(),_Ret1_impl(_notvalid_impl))
模板
_Ret_invalid_ut&未初始化索引(T*pt,int索引)
{
返回pt[索引];
}
然后,在值被索引的地方,我调用UninitialzedIndex而不是操作符[]

void SomeFunction(int n) {
    Foo buffer[64];
    if (n <= 64)
      return;

    // Explicitly initialize what is needed.
    for (int i = 0; i < n; ++i) {
        UninitialzedIndex(buffer, i).Init(i * 3);
    }
    // Use buffer[0] - buffer[n-1] somehow.
}
void SomeFunction(int n){
Foo缓冲区[64];

如果(n然后,静态分析器应该报告忘记调用
Init
的代码出现问题。但不应该对此代码发出警告。事实上,添加默认构造函数是有害的,因为这样会允许不调用
Init
的代码在没有警告的情况下溜过去。啊,我明白了,我当时在想,默认构造函数uctor调用Init,但从未编写最后一部分(尽管我在编辑中强烈暗示)。我将修改我的答案。如果初始化需要参数,那么默认构造函数不能使对象保持有效/有用的状态。这是一个不同的用例,原始海报从未提出过…但当然,在这种情况下,开发人员必须提供带参数的构造函数,或带参数的初始化函数…但是我们如果统一默认初始化不起作用,这将是一个更好的问题…例如,如果您需要调用
Init(n)
@BenVoigt,谢谢,更新了示例。函数周围的#pragma push/disable:6001/pop是否可以接受?这是可行的,但是已经有很多代码使用Init()-是构造器语义,所以不必重写它们就好了。不过,也许一个模板可以覆盖其中的一些。
struct Foo {
    int m_Member;
    void Init(int i);
};

void Foo::Init(int i) {
    m_Member = i;
    // Many other members initialized here.
}

void Initialize(__in_bcount(sizeof(Foo) * n) Foo* buffer, int n) {
    // Explicitly initialize what is needed.
    for (int i = 0; i < n; ++i) {
        buffer[i].Init(i * 3);
    }
}

void SomeFunction(int n) {
    Foo buffer[64];
    assert(n <= 64);
    Initialize(buffer, n);
    // Use buffer[0] - buffer[n-1] somehow.
}
#define _Ret_invalid_                     _SAL2_Source_(_Ret_invalid_, (), _Ret1_impl_(__notvalid_impl))

template <typename T>
_Ret_invalid_ T& UninitialzedIndex(T* pt, int index)
{
   return pt[index];
}
void SomeFunction(int n) {
    Foo buffer[64];
    if (n <= 64)
      return;

    // Explicitly initialize what is needed.
    for (int i = 0; i < n; ++i) {
        UninitialzedIndex(buffer, i).Init(i * 3);
    }
    // Use buffer[0] - buffer[n-1] somehow.
}