2015-04-25 37 views
4

我正在爲變量n0中的struct節點分配空間。我使用fwrite將此結構保存到文件中,但是當我運行valgrind時,出現此錯誤。我的代碼在下面,你能幫我嗎?將struct + write(buf)指向未初始化的字節

==1412== Syscall param write(buf) points to uninitialised byte(s) 
==1412== at 0x4F22870: __write_nocancel (syscall-template.S:81) 
==1412== by 0x4EB0002: [email protected]@GLIBC_2.2.5 (fileops.c:1261) 
==1412== by 0x4EB14DB: [email protected]@GLIBC_2.2.5 (fileops.c:538) 
==1412== by 0x4EB0D5F: [email protected]@GLIBC_2.2.5 (fileops.c:165) 
==1412== by 0x4EA4B0F: [email protected]@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; 
} 
+0

這段代碼只會寫一個'unsigned char *'和一個int文件。不是節點字符串的內容。如果這就是你想要的,代碼應該沒問題。 –

+0

@BrianMcFarland通過您的評論,感謝我如何修復錯誤「(請注意,它正在寫入指針本身的值,而不是它指向的值,這可能不是您想要的,但這是一個單獨的問題)」 ? – Juan

回答

6

這是因爲編譯器填充你的結構和你寫的額外填充字節而不是初始化它們。你可以看到這第一個運行程序,然後檢查出來的東西:

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

前8個字節(92c0 04c2 0000 0000)是你的指針值,ustr(請注意,它的寫指針的值本身,而不是它指向的,這可能不是你想要的,但這是一個單獨的問題)。

接下來的四個字節(ffffffff)是您設置爲-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 
+0

感謝您的回答,我如何解決這個錯誤「(請注意,它是在寫指針本身的值,而不是它指向的值,這可能不是您想要的,但這是一個單獨的問題)」? – Juan

+0

最直接的方法是單獨編寫結構的字段,而不是一次寫入整個結構。我將編輯答案來展示這一點。 –