2010-05-04 73 views
4

我有如下定義的結構:閱讀文件和填充結構

typedef struct myStruct{ 
    int a; 
    char* c; 
    int f; 
} OBJECT; 

我能夠填充這個對象,並將其寫入文件。不過,我無法讀取其中的char * c值...嘗試閱讀它時,它給我一個分段錯誤錯誤。這有什麼錯我的代碼:

//writensave.c 

#include "mystruct.h" 
#include <stdio.h> 
#include <string.h> 


#define p(x) printf(x) 

int main() 
{ 
    p("Creating file to write...\n"); 
    FILE* file = fopen("struct.dat", "w"); 
    if(file == NULL) 
    { 
     printf("Error opening file\n"); 
     return -1; 
    } 

    p("creating structure\n"); 
    OBJECT* myObj = (OBJECT*)malloc(sizeof(OBJECT)); 
    myObj->a = 20; 
    myObj->f = 45; 
    myObj->c = (char*)calloc(30, sizeof(char)); 
    strcpy(myObj->c, 
     "This is a test"); 
    p("Writing object to file...\n"); 
    fwrite(myObj, sizeof(OBJECT), 1, file); 
    p("Close file\n"); 
    fclose(file); 
    p("End of program\n"); 
    return 0;  
} 

這裏是我想讀它:

//readnprint.c 
#include "mystruct.h" 
#include <stdio.h> 
#define p(x) printf(x) 
int main() 
{ 
    FILE* file = fopen("struct.dat", "r"); 
    char* buffer; 
    buffer = (char*) malloc(sizeof(OBJECT)); 
    if(file == NULL) 
    { 
     p("Error opening file"); 
     return -1; 
    } 

    fread((void *)buffer, sizeof(OBJECT), 1, file); 
    OBJECT* obj = (OBJECT*)buffer; 
    printf("obj->a = %d\nobj->f = %d \nobj->c = %s", 
     obj->a, 
     obj->f, 
     obj->c); 
    fclose(file); 
    return 0; 
} 
+0

需要寫入的數據指針指向的是它所包含的地址而不是指向的地址。 – Learner 2010-05-04 18:03:12

回答

1

您正在保存一個指向字符,而不是字符串本身。當您嘗試重新加載在不同地址空間的新進程中運行的文件時,該指針不再有效。您需要改爲按值保存字符串。

2

當您編寫對象時,您正在將指針值寫入文件而不是指向的信息。

你需要做的是而不是只是fwrite/fread你的整個結構,而是一次做一個字段。像對待對象一樣用a和f來寫a,然後你需要對字符串做一些特殊的處理。嘗試fwrite/fread的長度(沒有在你的數據結構中表示,這很好),然後fwrite/fread字符緩衝區。閱讀時,你需要分配這些,當然。

2

你的第一個代碼示例似乎假定字符串將不會超過30個字符。如果是這樣的話,那麼最簡單的解決方法是可能重新定義你的結構是這樣的:

typedef struct myStruct{ 
    int a; 
    char c[30]; 
    int f; 
} OBJECT; 

否則,你只是存儲一個指向動態分配的內存將被毀壞了,當你的程序退出(所以當你稍後檢索這個指針時,這個地址是毫無價值的,並且很可能是非法訪問的)。

+0

結構對齊如何? – Learner 2010-05-04 17:59:45

+0

結構對齊如何?只要數據是由寫它的同一個應用程序讀取的,它應該不是問題。任何填充數據在寫入時都是未初始化的數據,讀取時可能會發生變化,但您並不指望它處於任何特定的狀態...... – 2010-05-04 19:19:23

+0

如果您擔心結構對齊問題,那麼編譯器很可能有選項來「打包」你的結構(即不使用填充字節)。在gcc中,你可以在'struct'定義的大括號後面加上__attribute __((__ packed __))。然而,正如破折號爆炸指出的那樣,如果您正在讀取和寫入來自同一應用程序的數據,對齊應該不成問題。 – bta 2010-05-06 16:15:32

1

我想添加一個關於潛在可移植性問題的說明,根據數據文件的計劃使用情況,可能存在也可能不存在。

如果數據文件要在不同端的計算機之間共享,則需要爲非字符類型(int,short,long,long)配置文件到主機和主機到文件轉換器長, ...)。此外,可以謹慎使用stdint.h(int16_t,int32_t,...)中的類型來保證所需的大小。

但是,如果數據文件不會在任何地方移動,則忽略這兩點。

1

您的結構的char *字段被稱爲可變長度字段。當你寫這個字段時,你需要一個確定文本長度的方法。兩種流行的方法是:
1.書寫尺寸的第一
2.寫終端字符

書寫尺寸的第一
在該方法中,文本數據的大小首先被寫入,由數據緊接着。
優點:文本可以通過塊讀取更快加載。
缺點:需要兩次讀取,長度數據需要額外的空間。
實施例的代碼片段:

struct My_Struct 
{ 
    char * text_field; 
}; 

void Write_Text_Field(struct My_Struct * p_struct, FILE * output) 
{ 
    size_t text_length = strlen(p_struct->text_field); 
    fprintf(output, "%d\n", text_length); 
    fprintf(output, "%s", p_struct->text_field); 
    return; 
} 

void Read_Text_Field(struct My_STruct * p_struct, FILE * input) 
{ 
    size_t text_length = 0; 
    char * p_text = NULL; 
    fscanf(input, "%d", &text_length); 
    p_text = (char *) malloc(text_length + sizeof('\0')); 
    if (p_text) 
    { 
    fread(p_text, 1, text_length, input); 
    p_text[text_length] = '\0'; 
    } 
} 

寫終端字符 在該方法中的文本數據被寫入之後是「終端」字符。非常類似於C語言字符串。 優點:比Size First需要更少的空間。
缺點:文本必須一次讀取一個字節,因此不會錯過終端字符。

固定尺寸字段
代替使用char*作爲成員的,使用char [N],其中N是場的最大尺寸。 優點:固定大小的記錄可以讀取爲塊。 使文件中的隨機訪問更容易。缺點:如果沒有使用所有的場地空間,會浪費空間。 字段太小時的問題。

將數據結構寫入文件時,應該考慮使用數據庫。有一些小的如SQLite和較大的如MySQL。不要浪費時間編寫並調試永久存儲例程,當您的數據已經寫入並測試時。

+0

我想看看你提到的前兩種方法的一些代碼... – deostroll 2010-05-05 10:26:22

+0

@deostroll:我爲第一種方法添加了代碼。第二種方法可以將標準庫函數用於字符串I/O。 – 2010-05-06 16:20:10

+0

像你之前提到過的,我保留了一個存儲字符串長度的字段(obj-> a)...我將所有這些寫入文件。但在嘗試閱讀> http://pastebin.com/UuDGVQxZ時,我仍然不斷收到段錯誤 – deostroll 2010-05-06 18:16:06