Valgrind 对结构 + 联合的行为

Valgrind's behaviour for structs + unions

我有一个很大的 C 项目,多亏了 valgrind,我清理了内存管理中的一些混乱情况。除了一件事,我清理了所有东西,经过一周的分析,我开始认为那是 valgrind 对我的代码的误解,而不是我的错误。该程序永远是 运行,但这没有任何意义(我已经看到程序运行了好几周然后因为 int 中的第 31 个翻转位而卡住的情况)。

我的代码使用了下一个想法:有一个存储(就我的项目而言 "warehouse"),它包含我想要构建的所有类型的结构。我使用 Xlib 中的技巧来使它在内存中尽可能小:

 typedef struct
 {
     // data
 } TypeA

 typedef struct
 {
     // data
 } TypeB

 typedef struct
 {
     // data
 } TypeC

 typedef union
 {
    TypeA typea;
    TypeB typeb;
    TypeC typec;
 } UniType;

 typedef struct
 {
     int type;
     UniType data;
 } Element;

然后我创建一个元素:

SmlErrors SmlWhsAdd(SmlElement element, SmlIndex * index)
{
    SML_CHECKPTR(index);

    SmlElement * ptrold = warehouse.elem;

    warehouse.elem      = realloc(warehouse.elem,
                                  (++warehouse.elemcount) * sizeof(SmlElement));
    if (!(warehouse.elem))
    {
        warehouse.elem = ptrold;
        *index         = 0;
        warehouse.elemcount--;
        return SML_ERR_BADALLOC;
    }

    warehouse.elem[warehouse.elemcount - 1] = element;

    *index = (warehouse.elemcount - 1);

    return SML_ERR_SUCCESS;
}

对于那些认为 ptr = realloc(ptr... 不好的人 - 仔细看看,我保存旧的并在之后恢复它。我打算用myalloc 使程序崩溃而不是继续工作

这段代码清晰,valgrind无语。除了一种情况。我的一个 "TypeX" 结构(准确地说,是 TypeX 的子结构)包含一个数组:

SmlIndex     sprite[SML_THEMEBLOCK_SIZE];

每个精灵也是来自仓库的索引,所以它是移动中的移动,因为该数组是该仓库(在杰克建造的房子里)的元素之一的一部分。

我使用上述函数在其中一个精灵中写入值:

SML_CHECKLOC(SmlImageCreate(&(widget->sprite[i]),
                            widget->geometry.size));
// Which calls `WhsAdd` with `&(widget->sprite[i]` as `index`-parameter.

每次我这样称呼它时,valgrind 都会抱怨 Invalid write of size 4。之后每次我尝试使用 sprite[x] 中的值 - Invalid read of size 4。准确地说,它抱怨的是以下一行:

*index = (warehouse.elemcount - 1);

我的系统是32位的,SmlIndex是uint32_t

请告诉我在哪里挖掘。经过一周的研究,我没有主意了。这就是为什么我开始认为这可能是 valgrind 的错误——我还听说它在使用联合和结构时工作起来很奇怪。

还有一件事。

widget->sprite[i] = 0; // No complainings.
SmlImageCreate(&(widget->sprite[i], ...) // Complainings.

有人可以帮帮我吗?我快淹死在那个沼泽里了。关于在哪里看的任何建议。随便。

UPD: MCVE:http://pastebin.com/r5T5ZBPC

此致, 亚历克斯

(编辑历史记录:MCVE最初使用未初始化的变量,但是在初始化所有这些之后,问题仍然存在)

在 MCVE 中,问题来自:

SmlWhsAdd(sprite, &(warehouse.elem[window].data.wdg.sprite[0]));

第二个参数是指向 space 的指针,该 space 由之前对 realloc 的调用分配。

然而,在 SmlWhsAdd 函数中,realloc 被调用到此 space,它分配一个新块并释放旧块。这使得第二个参数指向释放 space.


要解决此问题,我的建议是审查 SmlWhsAdd 的所有用途并避免传递 warehouse.elem 下的指针。

一个选项可能是使用一个临时变量,然后在调用之后分配索引;另一种选择可能是传递一些其他信息,允许 SmlWhsAdd 函数在执行 realloc 之后计算写入索引的位置;或者如果索引总是在末尾,你甚至根本不需要那个参数,因为调用者可以在 warehouse.elemcount-1 after.