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.
我有一个很大的 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.