【问题标题】:C : Splint pointers related warnings. What does they mean?C:夹板指针相关的警告。他们是什么意思?
【发布时间】:2014-04-01 04:52:20
【问题描述】:

以下代码编译并执行,没有任何警告或错误。但是,当我使用splint 分析代码时,它显示了 4 个警告(显示在代码下方)。

我见过的大多数示例(带有那种警告)都使用 malloc 和 free。由于此代码不使用 malloc,我们可以说忽略这些警告是安全的吗?在不删除指针的情况下编写代码的正确方法应该是什么?

#include <stdio.h>

typedef struct
{
    void (*Doit) ( void );
} func;

typedef struct
{
    func f;
    int val;
} obj;

typedef struct
{
    obj *o;
} world;

static void Read( void ) {
    printf( "Read\n");
}

static void Init( world *w ) {
    obj pc;
    w->o = &pc;           //(1)
    w->o->val = 10;
    w->o->f.Doit = Read;
    w->o->f.Doit();
}

int main() {
    world w;
    Init( &w );     //(2)
    return 0;      //(3)
}

(1) :28:5: 仅隐式存储 w->o(类型 obj *)在分配之前未释放: w->o = &pc 。检测到内存泄漏。唯一限定的存储在最后一次引用丢失之前不会释放。

(1) :28:5: 直接地址 &pc 仅隐式分配给: w->o = &pc 。立即地址(& 运算符的结果)传输不一致。

(2) :33:11: 定义前使用的变量 w 使用的右值可能未初始化为某个执行路径上的值。

(3) :34:14: 只有从这个范围内声明的变量派生的存储 w.o(类型 obj *)不被释放(内存泄漏)。怀疑由于结构或深度指针的不完全释放导致的存储泄漏。可从正在解除分配的引用访问的非共享存储尚未被解除分配。 Splint 假设当一个对象作为一个 out only void 指针传递时,外部对象将被释放,但内部对象不会。

这段代码只是对我想要实现的其他东西的测试,但由于我不精通 C,我想了解使用上述方法的内存泄漏风险。

提前致谢。

【问题讨论】:

  • 虽然我很久以前学过 C 课程,但我必须继续阅读有关 C 中内存管理的更多详细信息(我已经习惯了高级语言)。

标签: c pointers memory-leaks splint


【解决方案1】:

我发现splint 的输出非常难以阅读。根据您的平台,您可能还想尝试valgrind

不管怎样,您的代码的主要问题是 pc 是一个局部变量。 它在Init() 的末尾变得无效,因为它(可能与每个编译器)都位于堆栈上。

你可能会想要做

w->o = (obj *) malloc(sizeof(obj));

改为。

另一种方法是将 w 中 o 的类型更改为 obj 而不是 obj *

注意C中的内存管理(强烈推荐进一步阅读):

函数参数和局部变量通常位于所谓的stack 上。一旦函数返回,它们就会失效。 堆栈对象的大小总是需要编译器知道的。

通过malloc() 分配的对象位于堆上。 malloc() 可用于未知大小的对象。由malloc() 分配的对象保持有效,直到它们的指针传递给realloc()free()。如果您忘记将malloc'd 指针传递给free,您就会发生内存泄漏。如果您在对象变得无效后访问它,您将获得未定义的行为(堆中的那个位置现在可能被其他东西重用,但也许数据仍然存在,谁知道?)。在最坏的情况下,您可能会遇到分段错误或堆损坏。如果您将无效的对象指针传递给 free()realloc(),您可能会遇到堆损坏。

全局变量和静态函数成员位于其他地方(tm),并且在程序的整个执行过程中有效(至少从进入 main 直到从它返回,就像 main 的局部变量一样)。

malloc() 是一个复杂的函数,它在称为堆的复杂数据结构中内部管理可用的内存块。堆损坏意味着您损坏了该数据结构本身。分段错误意味着您在有效内存块之外的某处写入/读取。

请注意,C 标准不保证任何情况。它不能保证像堆栈这样的东西甚至存在。但是,我所知道的所有编译器都是这样做的。不过,对于某些嵌入式平台,malloc() 是一个非常简单的函数,它每次分配某个内容时都会增加一个指针,而free() 则什么都不做。

【讨论】:

  • 如果我在编译时不知道对象的大小,不应该使用 malloc 吗?在此示例中,obj 大小固定为指针和 int 大小。还是我错过了什么?
  • 非常感谢!你的解释现在已经很清楚了。
【解决方案2】:

在初始化()中

static void Init( world *w ) {
    obj pc;
    w->o = &pc;           //(1)
    w->o->val = 10;
    w->o->f.Doit = Read;
    w->o->f.Doit();
}

obj 电脑;是函数 Init() 中的自动变量。

从 Init() 重新调用后,它指向的内存将无效。 所以在 init() 之后的 main() w->o 将是无效的。

你已经使用了 malloc

w->o = (obj *) malloc(sizeof(obj)); 

您可以在 Init() 或 main() 中执行此操作。

注意:你必须使用 free() 释放它,否则会发生内存泄漏。

【讨论】:

  • “必须”仅当您要在 Init 返回后使用 w-&gt;o 时。在问题的代码中,它不是。如果问题代码是一些真实代码的简化版本,我们无法判断它是否发生在该代码中。
  • 那么,只要我在 Init() 之后不使用 w->o 就可以了吗?如果我在 main() 上声明“obj pc”并在那里使用引用会发生什么?这样可以吗?
  • 你可以移动obj pc; w->o = &pc;到 main() 的行,那么它应该可以工作
  • +1 感谢您的帮助。看完mice_e的解释我明白了。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-08-18
  • 2011-02-04
  • 2013-06-25
  • 1970-01-01
  • 2020-04-03
  • 1970-01-01
相关资源
最近更新 更多