2010-03-04 101 views
1

重要編輯:閱讀和寫作結構[C]

對不起,大家,我犯了一個大錯誤的結構。 char * name;意思是在結構之外,寫在結構之後的文件中。 這樣,你讀取結構,找出名字的大小,然後讀入字符串。也解釋了爲什麼不需要空終止符。 但是,我覺得某處,我的實際問題已得到解答。如果有人想編輯他們的回覆,所以我可以選擇一個最合適的,我會很感激。

同樣,我所問的問題是「如果你閱讀了一個結構體,你還閱讀它所保存的數據,或者你需要以其他方式訪問它」。

很抱歉的混亂

對於分配,我一直在負責其寫入和讀取結構的盤(使用的fread和fwrite)中的程序。

我很難理解這個概念。 比方說我們有這樣的結構:

typedef struct { 
    short nameLength; 
    char* name; 
}attendenceList; 

attendenceList names; 

現在假設我們給它這樣的數據:

names.name = "John Doe\0"; 
names.nameLength = strlen(names.name); /*potentially -1?*/ 

,然後我們使用fwrite的...給出一個文件指針fp。

fwrite(&names,sizeof(names),1,fp); 

現在我們關閉文件,稍後打開它來閱讀結構。 問題是這樣的:當我們在結構中讀取時,我們是否還讀取了它存儲的變量?

我們能那麼現在這樣做:

if(names.nameLength < 10) 
{ 
... 
} 

還是我們必須FREAD的東西,不只是結構,或以某種方式進行分配? 假設FREAD是:

fread(&names,sizeof(names),1,fp); 

而且假設我們定義了在我國目前的功能結構,如上所述。

感謝您的幫助!

+0

一對夫婦的評論:' 「李四」'已經有了'e'後0,這樣你就不需要寫' 「李四\ 0」'。其次,'strlen'返回'size_t',它是一個無符號類型。因此,它不能返回負值。當你把'strlen'的返回值賦給'short'時,如果這個值在'short'的範圍內,那麼一切都沒問題。否則,你會溢出。 – 2010-03-04 20:52:14

+0

你編輯的答案是,如果你用'fwrite'寫了一個'struct',然後用'fread'讀回,你會得到它,但是如果'struct'有指針,你會得到值的指針返回,這在閱讀時可能沒有意義。如果你的struct只有一個'short'元素,我不知道你爲什麼不能寫'short'本身,而不是將它包裝在'struct'中。也許你實際的'struct'有更多的數據呢?只要沒有指針參與'struct',你就沒問題。當然,編寫二進制數據不能跨不同的機器移植。 – 2010-03-04 21:02:00

+0

實際的結構體包含兩個字符串的大小(在結構之後,因此您可以正確使用fseek或知道如何讀取它們),並且該程序連續使用兩次。 – Blackbinary 2010-03-04 21:23:45

回答

0

我假設你的意思是char* name而不是char name。 另外sizeof(name)將返回4,因爲你得到的大小不是char數組的長度,而是char*。所以你應該在你的fwrite裏寫strlen(name)而不是sizeof(name)

在你的上面的例子中,我會建議存儲沒有null終止字符串的確切大小。您不需要存儲字符串長度,因爲您可以在後面獲取該字符串長度。

如果您正在讀取文件中的一個字符串,並且您沒有空終止符而寫下確切的大小。然後,在讀取數據後,需要手動將終止緩衝區的內容終止。 因此,請確保至少分配您正在讀取的數據的大小加上1.
然後,您可以將該數組的最後一個字節設置爲'\0'

如果你一次寫入整個結構到緩衝區,你應該小心,因爲填充。填充可能並不總是相同的。

當我們在結構中讀取的時候,我們是否還讀取它存儲的變量?

是的,你的問題是,正如我上面提到的,你將存儲指針char *(4字節)而不是實際的char數組。我會建議單獨存儲結構元素。

+0

呵呵?我不明白你的意思是'[我的]緩衝區'? 啊,得到它的感謝,將修復我的代碼。這只是一個例子,因爲我不想在實際任務附近使用任何東西。 獲得一個字符串的長度(它實際上來自一個文件,所以使用fread)我需要-1 strlen嗎? – Blackbinary 2010-03-04 20:23:41

+0

'strlen'返回不包含空終止符的字符串長度。 – 2010-03-04 20:30:32

+0

感謝您的澄清,但我的實際問題一直未得到答覆。 – Blackbinary 2010-03-04 20:32:05

3

您在這裏有一個問題:

fwrite(&names,sizeof(names),1,fp); 

由於attendenceList保存名稱作爲char *這將只是寫出來的指針,而不是實際的文本。當你讀回來時,指針所引用的內存很可能還有其他內容。

你有兩個選擇:

  1. 放入attendenceList字符數組(char names[MAXSIZE])。
  2. 不要寫入原始數據結構,而是寫入必要的字段。
1

您正在編寫包含其成員的結構的內存佈局。

如果您再次讀取結構,您將返回它們 - 至少如果您在同一平臺上執行此操作,並且程序使用相同的編譯器和編譯器設置進行編譯。

您的name成員聲明爲char,所以不能在其中存儲字符串。

如果name是這樣一個指針:

typedef struct { 
    short nameLength; 
    char *name; 
}attendenceList; 

你真的不應該讀/寫結構到一個文件。您將在存儲器中編寫結構,並且該結構包含name指針的值。

fwrite對結構中的指針一無所知,它不會遵循指針,也不會寫任何指向的指針。

當您再次讀取結構時,您會看到name指針中的地址,這可能不會指向任何明智的東西。

如果你聲明name爲一個數組,那麼你就可以,因爲數組及其內容是結構的一部分。

typedef struct { 
    short nameLength; 
    char name[32]; 
}attendenceList; 

與往常一樣,請確保您不嘗試複製一個字符串 - 包括其NUL terminator-到name這是大於32.當你回來讀一遍。設置yourstruct.name [31] = 0;所以你確定緩衝區是空的。

要寫入的結構,你會做

attendenceList my_list; 

//initialize my_list 
if(fwrite(&my_list,sizeof my_list,1,f) != 1) { 
//handle error 
} 

,並備份看了一遍:

attendenceList my_list; 

//initialize my_list 
if(fread(&my_list,sizeof my_list,1,f) != 1) { 
//handle error 
} 

}

0

你問:

現在我們關閉文件,稍後打開它來閱讀結構。問題是這樣的:當我們閱讀結構時,我們是否還在讀取它存儲的變量?

編號sizeof(names)是在編譯時定義的常量值。這將是一樣

sizeof(short) + sizeof(void*) + some_amount_of_padding_to_align_things 

它將包括什麼names.name點,這將只包括指針本身的尺寸大小。

因此,在將其寫入文件時存在兩個問題。

  1. 你實際上並沒有寫名字字符串文件
  2. 你正在寫一個指針值,當你讀回,將沒有任何意義的文件。

由於您的代碼是當前編寫的,當您讀回名稱時,names.name將指向某處,但不會指向「John Doe \ 0」。

您需要做的是編寫由names.name指向的字符串而不是指針值。

你需要做的事情有時被稱爲「扁平化」結構,你在內存中創建一個不包含指針,但保存與你想要使用的結構相同的數據的結構,然後將扁平結構寫入磁盤。這是做到這一點的一種方法。

typedef struct { 
    short nameLength; 
    char name[1]; // this will be variable sized at runtime. 
}attendenceListFlat; 

int cbFlat = sizeof(attendenceListFlat) + strlen(names.name); 
attendenceListFlat * pflat = malloc(cbFlat); 
pflat->nameLength = names.nameLength; 
strcpy(pflat->name, names.name); 

fwrite(pflat, cbFlat, 1, fp); 

的扁平結構與具有1的最小尺寸的陣列結束,但是當我們的malloc,我們添加的strlen(names.name),所以我們可以把這個作爲函數strlen(names.name)的陣列+1大小。

0

有幾件事。

結構只是內存的塊。它只需要一堆字節並在它們上繪製邊界。訪問結構元素只是將特定內存偏移量轉換爲特定類型數據的便捷方式

您正在嘗試將字符串分配給char類型。這不起作用。在C中,字符串是在它們結尾處具有NULL字節的字符數組。最簡單的方法是將名稱設置爲固定緩衝區。在創建結構時,必須將名稱複製到緩衝區中(非常小心不要寫入比緩衝區包含更多的字節)。然後您可以一步寫入/讀取文件中的緩衝區。

struct attendanceList { 
    int namelen; 
    char name[256]; //fixed size buffer for name 
} 

你可以做的另一種方式是由具有名字是指針爲字符串。這使得你想要做的更加複雜,因爲爲了將結構寫入/從文件讀取,必須考慮到名稱存儲在內存中的不同位置。這意味着兩次寫入和兩次讀取(取決於您的操作方式),並正確指定name指針指向讀取名稱數據的任何位置。

struct attendanceList { 
    int namelen; 
    char* name; //the * means "this is a pointer to a char somewhere else in memory" 
} 

有你可以做到這一點,使用一招用在一個結構的結束零長度陣列的動態調整結構第三種方式。一旦知道了名稱的長度,就可以分配正確的數量(sizeof(struct attendanceList)+字符串的長度)。然後你將它放在一個連續的緩衝區中。您只需要記住sizeof(struct attendanceList)不是您需要編寫/讀取的大小。這可能有點混亂作爲一個開始。這也是所有編譯器都不支持的破解方式。

struct attendanceList { 
    int namelen; 
    char name[0]; //this just allows easy access to the data following the struct. Be careful! 

}