有没有避免警告C6386的方法,不需要完全禁用它或者关闭整个代码分析。

3

Visual Studio 2019默认会在编辑器中展示代码分析警告,以绿色波浪线的形式呈现。这对于学习C编程的学生来说可能非常有用,因为它们可以捕捉典型的错误,例如数组访问越界。

不幸的是,假阳性可能会完全破坏学习体验,我担心我不得不要求学生禁用此功能,以避免他们对不存在的问题感到担忧。

这个简短的片段不会引起任何警告:

#include <stdlib.h>

int main(void)
{
    size_t n = 6;
    int *v = malloc(n * sizeof(int));
    if (v == NULL) {
        return 1;
    }
    for (size_t i = 0; i < n; ++i) {
        v[i] = i;
    }
    free(v);
    return 0;
}

不幸的是,如果您将分配操作移动到一个函数中,像这样:

#include <stdlib.h>

int *test(size_t n)
{
    int *v = malloc(n * sizeof(int));
    if (v == NULL) { 
        return NULL;
    }
    for (size_t i = 0; i < n; ++i) {
        v[i] = i;
    }
    return v;
}

int main(void)
{
    size_t n = 6;
    int *v = test(n);   
    free(v);
    return 0;
}

您会获得一个警告 C6386:在向“v”写入数据时缓冲区溢出:可写大小为“n*sizeof(int)”字节,但可能写入“8”字节。

即使在Stack Overflow上阅读,我也不知道“8”来自何处,但更重要的是为什么它无法识别i永远不会越界。

因此问题是:有没有一种方法可以编写此类代码而不会生成警告?

我知道可以转到工具 > 选项 > 文本编辑器 > C/C++ > 实验性 > 代码分析并将禁用代码分析下划线设置为True,或使用#pragma warning(disable:6386),但我宁愿避免这样做,当然也要避免建议我的学生后者。


1
无论如何,这似乎是一个典型的“误报”,它只是分析工具中的一个bug。当使用静态分析工具时,标准程序是禁用所有有问题和有bug的诊断。顺便说一句,你不应该首先使用Visual Studio来教授C语言,因为它不符合标准且非常过时。 - Lundin
@Lundin:关于代码分析警告,重点在于它对许多实际情况都能很好地发挥作用,并且可以成为学生的真正帮助,因此禁用它是一种遗憾。 - Costantino Grana
1
@Lundin:我不同意Visual Studio是“不合规和非常过时”的说法,但我不会为此而陷入宗教战争。我认为使用命令行和printf()作为调试工具来进行教学要糟糕得多... - Costantino Grana
2
@CostantinoGrana:微软在去年九月份宣布,他们正在为VS编译器添加对C11和C17的支持。是的,它已经过时了一段时间。 - John Bode
@Lundin:“问题是,n可能不一定在SIZE_MAX处回绕”,在这种情况下,我认为这只是malloc(+realloccalloc)实现需要担心的事情。 - Ian Abbott
显示剩余5条评论
2个回答

3

我非常感谢每个人的贡献,我同意这是代码分析器中的一个错误(通过查看微软网站,它在两年前已经“关闭 - 优先级降低”...)。

Adrian Mole的max(n,0)技巧指出了一种应对代码警告的方法,即检查n是否大于零。有趣的是,您仍然可以将零用于n原本应该使用的内容。正如John Bollinger所指出的那样,虽然有经验的程序员可以使用这个想法(他们可能会禁用警告),但对于学生来说并不适用。

因此,在告诉学生这是一个错误以及如何关闭代码分析波浪线或禁用警告后,我会进行以下操作:

int *test(size_t n)
{
    if (n == 0) {
        return NULL;
    }
    int *v = malloc(n * sizeof(int));
    if (v == NULL) {
        return NULL;
    }
    for (size_t i = 0; i < n; ++i) {
        v[i] = i;
    }
    return v;
}

这也可以理解为:不允许分配零个元素。

1
你可以通过确保传递给mallocn值在溢出后没有“环绕”来抑制此警告(这可能被视为错误),如Eric Postpischill的评论中所示。
为此,您可以将n参数替换为看似奇怪的max(n,0)
int* test(size_t n)
{
//  int* v = malloc(n * sizeof(int));         // Warning C6386 on v[i] = i
    int* v = malloc(max(n, 0) * sizeof(int)); // No warning
    if (v == NULL) {
        return NULL;
    }
    for (size_t i = 0; i < n; ++i) {
        v[i] = i;
    }
    return v;
}

1
我猜这只是巧合,对于警告来说并没有什么影响。例如,当颠倒max的参数时,警告会返回。 - Ian Abbott
@IanAbbott 当然是Fluke。我猜这是由于max宏的定义方式(使用三元运算符)所导致的。 - Adrian Mole
1
有趣的是,这种方法可以使静态分析器认为代码没有问题。但是从教学的角度来看,我认为演示这样的变通方法,更不用说教授学生使用它,比忍受警告更糟糕。至少有了警告,你会被提醒要仔细审查涉及到的分配情况,以判断警告是否合理。 - John Bollinger
@JohnBollinger 我同意。但是,如果学生被迫使用MSVC代码分析器,那么选择就有限了:教他们接受警告,向他们展示如何使用 #pragma 禁用它,或告诉他们这是MSVC的一个错误并教他们如何欺骗该工具。 - Adrian Mole
但是我在这里提出的“解决方法”可能对其他开发人员有用,他们拥有大型代码库,并希望在否则会显示此类误报的地方抑制该警告。 - Adrian Mole
@IanAbbott:在VS2017代码分析器中反转参数仍会抑制警告,使用任何其他数字也是如此。当然,这并不是正确的行为。 - Costantino Grana

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接