2011-12-31 64 views
6

這是一個測試程序,我爲我正在編寫的一個更大的項目編寫。它與使用fwrite()將結構數據寫入磁盤然後使用fread()讀取數據相關。該結構的一個成員是動態分配的。C fread()魔法讀取動態分配的結構成員,怎麼樣?

首先,這裏是我的代碼

#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 

#define STRING_LEN 128 

struct Person { 
    int age; 
    char *name; 
}; 

int main(int argc, const char *argv[]) 
{ 
    struct Person *person = calloc(1, sizeof(struct Person)); 
    person->age = 22; 
    person->name = calloc(STRING_LEN, sizeof(char)); 

    char *name = "Name that is really, really, really, really, really, really, long."; 
    strncpy(person->name, name, STRING_LEN); 

    FILE *out_file = fopen("rw.out", "w"); 
    fwrite(person, sizeof(struct Person), 1, out_file); 
    fclose(out_file); 

    FILE *in_file = fopen("rw.out", "r"); 
    struct Person *person_read = calloc(1, sizeof(struct Person)); 
    fread(person_read, sizeof(struct Person), 1, in_file); 
    fclose(in_file); 

    printf("%d %s\n", person_read->age, person_read->name); 

    free(person->name); 
    free(person); 
    free(person_read); 

    return 0; 
} 

而且outpout

22 Name that is really, really, really, really, really, really, long. 

我的問題是,爲什麼是這方面的工作?不應該fwrite()只寫入'name'包含的地址(即字符串的開始地址)?也就是說,我將sizeof(struct Person)傳遞給fwrite(),但它正在寫入'name'指向的字符串。

更令我困惑的是fread()的行爲。同樣,如果我傳遞sizeof(struct Person),那麼'name'的實際值如何被讀取?內存如何分配?

我以前對如何使用fwrite()+ fread()的理解是,我必須「手動」寫入'name'指向的數據,「手動」讀取數據,然後複製該字符串在爲結構和'名稱'成員分配內存之後。換句話說,我將不得不遍歷任何指針,寫入數據,然後以相同的順序讀取數據。

編輯:丹和其他人是正確的。我已經看過了輸出文件與XXD:

0000000: 1600 0000 0000 0000 30a0 d900 0000 0000 ........0....... 

如果我打印出「名」在寫之前包含地址和看完後是相同的(0xd9a030),從XXD輸出相匹配。

回答

9

您正在將數據寫入結構中,該數據是一個int,後跟一個指向字符串的指針。它就像其他任何數據一樣,你知道它有多長,因爲結構是固定的長度 - 一個int加上一個指針。您讀取與原始字符串相同的名稱字符串。名字本身既不寫也不讀。

+0

+1您可以通過printof sizeof(struct Person)和/或通過查看writen文件來檢查。 fread()正在讀取一個指針(一個內存地址),這恰好與當前的'person-> name'一致。 – leonbloy 2011-12-31 02:39:11

+0

唯一的保證是結構在相同的環境配置中具有相同的長度。它可能會改變你使用-m32,-m64,更改全局對齊參數等的時刻。 – 2011-12-31 05:03:37

1

*名稱指針在整個fwrite和fread調用中保持有效,這似乎是你的僥倖。如果你在printf之前釋放(person-> name),你會得到你期望的結果或錯誤。

5

person->nameperson_read->name指向相同的內存位置。由於您在重新讀入文件之前未取消分配person->name,因此person_read->name中的指針值仍然有效。

如果您已經釋放了person->name或從另一個程序中讀取文件,指針值將不再有效,嘗試引用它會引發未定義的行爲 - 您要麼打印出亂碼,要麼得到段錯誤。

+0

+1謝謝,在閱讀你的答案後,這顯然是明智的。 – tsliwkan 2011-12-31 02:55:51