"C6011 dereferencing null pointer" 在我的程序中是什么意思?

What does "C6011 dereferencing null pointer" mean in my program?

我有一个简单的 C 练习(见下面的代码)。该程序采用具有三个分量的向量并将每个分量加倍。 IDE 在这一行中向我显示了这个警告(绿色波浪线):C6011 dereferencing null pointer v.v[0] = 12;。我认为这是一个错误,因为在调试器中我读到 the program exited with code 0。你怎么看?

#include <stdlib.h>
#include <stdint.h> 

void twice_three(uint32_t *x) {
    for (size_t i = 0; i < 3; ++i) {
        x[i] = 2 * *x; 
    }
}

int main(void) {
    uint32_t *v = malloc(3 * sizeof(uint32_t)); 
    v[0] = 12;
    v[1] = 59; 
    v[2] = 83; 
    twice_three(v); 
    free(v); 
    return 0; 
}

注意:我正在使用 Visual Studio。

我相信您的 IDE 是在警告您,您没有确保 malloc return 编辑了 NULL 以外的内容。 malloc 可以 return NULL 当你 运行 分配内存不足时。

是否需要这样的检查值得商榷。在不太可能的情况下 malloc returned NULL,您的程序最终会被杀死(在具有虚拟内存的现代计算机上)。[1]所以问题是在非常非常罕见的情况下 运行 内存不足时,您是否想要一条干净的消息。

如果您要添加支票,请不要使用 assert。那是没用的。对于初学者来说,它只适用于开发构建(而不是生产构建),其中 malloc returning NULL 不太可能,并且已经非常容易找到内存泄漏(例如通过使用 valgrind)。使用适当的检查 (if (!v) { perror(NULL); exit(1) }).


  1. 由于人们不顾规则试图在评论中讨论这个问题,看来我必须更详细地阐述我的主张。

    一些人在评论中建议,如果你们不检查 NULL,“任何事情都可能发生”,但事实并非如此在具有虚拟内存的现代计算机上 .

    当 C 规范没有定义某些行为(所谓的“未定义行为”)时,并不意味着任何事情都会发生;这只是意味着 C 语言不关心 compiler/machine 在这种情况下的作用。 NULL 取消引用在此类系统上定义得非常好。抓住这种情况是内存虚拟化存在的理由!

    就像您可以依赖其他特定于编译器的功能(例如 gcc 的字段打包属性)一样,可以说依赖内存虚拟化来检测 malloc 失败是很好的。

  1. 始终检查 malloc 的结果。
  2. 使用对象而不是 sizeof
  3. 中的类型
int main(void) {
    uint32_t *v = malloc(3 * sizeof(*v)); 
    if(v)
    {
        v[0] = 12;
        v[1] = 59; 
        v[2] = 83; 
        twice_three(v); 
    }
    free(v); 
    return 0; 
}

首先,请注意警告是由您的编译器(或静态分析器或 linter)生成的,而不是像您最初写的那样由调试器生成的。

该警告告诉您您的程序可能会取消引用空指针。此警告的原因是您执行 malloc() 然后使用结果(指针)而不检查 NULL 值。在这个特定的代码示例中,malloc() 很可能只是 return 请求的内存块。在任何台式电脑或笔记本电脑上,通常没有理由无法分配 12 个字节。这就是为什么您的应用程序 运行 正常并成功退出的原因。但是,如果这将是内存受限系统(例如嵌入式系统)上的大型应用程序 and/or 运行 的一部分,则 malloc() 可能会失败并且 return NULL.请注意,malloc() 不仅会在没有足够的可用内存时失败,而且如果没有足够大的连续可用内存块(由于碎片),它也可能会失败。

根据 C 标准,取消引用 NULL 指针是未定义的行为,这意味着任何事情都可能发生。在现代计算机上,它可能会导致您的应用程序被终止(这可能会导致数据丢失或损坏,具体取决于应用程序的作用)。在较旧的计算机或嵌入式系统上,问题可能未被检测到,您的应用程序将读取或(更糟)写入地址 NULL(很可能是 0,但即使是 C 标准也不能保证)。这可能会在发生这种情况后的任意时间导致数据损坏、崩溃或其他意外行为。

请注意,compiler/analyzer/linter 对您的应用程序或您将运行使用的平台一无所知,并且它不会对此做出任何假设。它只是警告您这个 可能 的问题。由您决定此特定警告是否与您的情况相关以及如何处理它。

一般来说,您可以做三件事:

  1. 如果您确定 malloc() 永远不会失败(例如,在这样一个玩具示例中,您只会 运行 在具有千兆字节内存的现代计算机上) 或者如果您不关心结果(因为应用程序将被您的 OS 杀死而您不介意),则不需要此警告。只需在您的编译器中禁用它,或忽略警告消息。

  2. 如果您不希望malloc()失败,但希望得到通知当它发生时,快速而简单的解决方案是在 malloc 之后添加 assert(v != NULL);。请注意,这也会在它发生时退出您的应用程序,但以一种稍微更受控制的方式,您将收到一条错误消息,说明问题发生的位置。我建议将此用于简单的爱好项目,您不想在错误处理和极端情况上花费太多时间,而只想进行一些有趣的编程:-)

  3. 当实际更改 malloc() 会失败并且您希望应用程序有明确定义的行为时,您绝对应该添加代码来处理这种情况(检查 NULL 值).如果是这种情况,您通常需要做的不仅仅是添加 if 语句。您将不得不考虑应用程序如何在不需要分配更多内存的情况下继续工作或正常关闭。在嵌入式系统上,您还必须考虑内存碎片等问题。

有问题的示例代码的最简单修复是添加 NULL 检查。这将使警告消失,并且(假设 malloc() 不会失败)您的程序将 运行 仍然相同。

int main(void) {
    uint32_t *v = malloc(3 * sizeof(uint32_t));
    if (v != NULL) {
        v[0] = 12;
        v[1] = 59; 
        v[2] = 83; 
        twice_three(v); 
        free(v); 
    }
    return 0; 
}