编写struct + write(buf)指向未初始化的字节

时间:2015-04-25 20:26:32

标签: c

我在变量n0中为struct节点分配空间。我使用fwrite将此结构保存到文件中,但是当我运行valgrind时,我收到此错误。我的代码如下,你能帮帮我吗?

==1412== Syscall param write(buf) points to uninitialised byte(s)
==1412==    at 0x4F22870: __write_nocancel (syscall-template.S:81)
==1412==    by 0x4EB0002: _IO_file_write@@GLIBC_2.2.5 (fileops.c:1261)
==1412==    by 0x4EB14DB: _IO_do_write@@GLIBC_2.2.5 (fileops.c:538)
==1412==    by 0x4EB0D5F: _IO_file_close_it@@GLIBC_2.2.5 (fileops.c:165)
==1412==    by 0x4EA4B0F: fclose@@GLIBC_2.2.5 (iofclose.c:59)
==1412==    by 0x400793: main (in /home/grados-sanchez/git/merkle-codigos-C/test_file)
==1412==  Address 0x402500c is not stack'd, malloc'd or (recently) free'd
==1412==  Uninitialised value was created by a stack allocation
==1412==    at 0x40073F: main (in /home/grados-sanchez/git/merkle-codigos-C/test_file)

typedef struct {
    unsigned char * ustr;
    int height;
}node;

void node_init(node * n, int r) {

    int i;
    n->ustr = malloc((r + 1) * sizeof(unsigned char));
    for (i = 0; i < r; i++) {
        (n->ustr)[i] = random() & 0xff;
    }
    (n->ustr)[r] = 0;
    n->height = -1;
}
void node_destroy(node * n) {
    free(n->ustr);
    n->height = -1;
}

int main() {
    FILE* file_ptr = fopen("file1", "w+");
    node n0;
    node_init(&n0,2);
    fwrite(&n0, sizeof(node), 1, file_ptr);
    fclose(file_ptr);
    node_destroy(&n0);
    return 0;
}

1 个答案:

答案 0 :(得分:8)

这是因为编译器正在填充结构,并且您正在编写额外的填充字节但未初始化它们。您可以通过首先运行程序,然后检查出来的内容来看到这一点:

$ od -x file1
0000000 92c0 04c2 0000 0000 ffff ffff 0000 0000
0000020

前8个字节(92c0 04c2 0000 0000)是你的指针值ustr(注意它正在写指针本身的值,而不是它指向的值,这可能不是你想要的,但这是一个单独的问题。)

接下来的四个字节(ffff ffff)是您设置为-1的int height

然后还有四个字节设置为0.这些是编译器插入的填充,您没有初始化。您可以通过稍微修改程序以明确填充来证明这种情况:

typedef struct {
    unsigned char * ustr;
    int height;
    int pad;
}node;

void node_init(node * n, int r) {

    int i;
    n->ustr = malloc((r + 1) * sizeof(unsigned char));
    for (i = 0; i < r; i++) {
        (n->ustr)[i] = random() & 0xff;
    }
    (n->ustr)[r] = 0;
    n->height = -1;
    n->pad = 0xdeadbeef;
}

如果你现在运行程序,首先valgrind警告消失,第二个文件内容显示:

$ od -x file1
0000000 92c0 04c2 0000 0000 ffff ffff beef dead
0000020

pad变量的值现在显示为代替之前的零。

这一切都发生了,因为编译器试图使结构的大小甚至是机器字长的多倍,在你的情况下似乎是8字节(64位)。

您可能不希望包含多余的pad变量,因此禁止警告的另一个选项是从一开始就清除整个结构:

typedef struct {
    unsigned char * ustr;
    int height;
}node;

void node_init(node * n, int r) {

    int i;

    /* Clear node struct to suppress valgrind warnings */
    memset(n, 0, sizeof(node));
    n->ustr = malloc((r + 1) * sizeof(unsigned char));
    for (i = 0; i < r; i++) {
        (n->ustr)[i] = random() & 0xff;
    }
    (n->ustr)[r] = 0;
    n->height = -1;
}

这也会抑制valgrind警告,因为现在你正在初始化填充字节,并且文件中额外字节的内容会回到0,除非现在你明确地将它们设置为,而不是依赖于默认初始化:

$ od -x file1
0000000 92c0 04c2 0000 0000 ffff ffff 0000 0000
0000020

编辑:

另一个明确的实验。将以下内容添加到main()并再次运行:

printf("sizeof unsigned char *: %d\n", sizeof(unsigned char *));
printf("sizeof int: %d\n", sizeof(int));
printf("sizeof node: %d\n", sizeof(node));

在64位Intel Linux上,我看到:

$ ./writer 
sizeof unsigned char *: 8
sizeof int: 4
sizeof node: 16

结构大于其各部分的总和,你正在写整个东西,但只是初始化部分。

编辑2:

在回答下面有关修复指针正在写入的问题而不是指向的问题时,您可以通过单独编写结构的字段而不是编写整个结构本身来解决这个问题。顺便说一句,这也将以不同的方式修复原始的valgrind问题,因为您将不再编写填充字节。所以你的主要人物最终看起来像这样:

int main() {
    FILE* file_ptr = fopen("file1", "w+");
    node n0;
    node_init(&n0,2);
    fwrite(n0.ustr, strlen(n0.ustr), 1, file_ptr);
    fwrite(&n0.height, sizeof(n0.height), 1, file_ptr);
    fclose(file_ptr);
    node_destroy(&n0);

    return 0;
}

如果你运行它并查看文件,它不再包含你的8字节指针,而只包含它指向的两个字节的数据:

$ od -x file1 
0000000 c667 ffff ffff
0000006