Valgrind memcheck 使用一堆启发式方法来避免误报“无害”使用未初始化的值,因为这种用法在正确和不正确但正常运行的代码中都很常见。
特别是,除非您以一种严肃的、也许是“不可逆”的方式实际使用这样的值,例如,根据它的值进行跳跃,否则它不会出错。
这意味着有时错误发生在离问题根源很远的地方,甚至无法确定涉及哪个值。是否有某种方法可以在运行时“检查”一个值,例如如果未初始化use(x)
,Valgrind 会在该位置发出错误?x
你可以让你的use(x)
宏使用ValgrindVALGRIND_CHECK_VALUE_IS_DEFINED
客户端请求来当场出错。
为此,包括valgrind/memcheck.h
并将您的宏定义为
#define use(x) VALGRIND_CHECK_VALUE_IS_DEFINED(x)
并确保始终传递一个左值。
您还可以使用 memcheck 运行--track-origins=yes
更重的跟踪,以显示未初始化数据的来源。
另请参阅有关未初始化值错误的 Valgrind 常见问题解答,它解释了这两者,以及为什么 Valgrind 不会抱怨复制未初始化的值。
通常,像这样的东西需要对代码进行检测(由工具自动完成,或者在源代码中手动插入)。
正如我的评论中所述,如果您可以use(x)
自己插入语句,您可以执行以下操作:
static FILE* dev_null = 0;
static void use_var(char* var_addr, size_t var_size)
{
if (dev_null == 0) /* make sure we only open FILE* dev_null once */
{
dev_null = fopen("/dev/null", "wb");
assert(dev_null != 0); /* opening /dev/null CAN actually fail */
}
size_t i;
for (i = 0; i < var_size; ++i)
{
fputc(var_addr[i], dev_null); /* read every byte in the variable, write to dev_null */
}
}
#define use(x) use_var((char*)&x, sizeof(x))
/* Example of usage */
int main()
{
long x = 80;
struct { double d; char c[123]; } y;
memset(&y, 0, sizeof(y) - 1); /* initialize all bytes in y, except the last */
double z[2] = {3.14, 42.0};
use(x);
use(y);
use(z);
return 0;
}
但是,使用struct
包含对齐填充的 s 存在问题。填充从不用于任何事情,因此这可能是传递未初始化数据的合法原因。在这种情况下,Valgrind 可能会导致关于未初始化读取的虚假错误。
这两个帖子专门讨论了这个问题: 无论如何,valgrind 消息“条件跳转或移动取决于未初始化的值”可能是所谓的“误报”