![](/img/trans.png)
[英]C : Write char array (containing '1' and '0') into as binary in a file
[英]binary file read/write error containing char* pointer within struct
我有一個奇怪的問題。 我不知道為什么會這樣。 我嘗試了各種方式。 可能是因為我仍然是C語言的新手。
請看下面的代碼。
它帶有2個參數。 --write
和--read
。
在我的write()
函數中,我寫入文件,然后調用read()
函數。 這會將數據寫入文件,並按預期正確打印3行值。
在我的read()
函數中,我讀取了文件。 當我單獨傳遞--read
參數時,程序會給出segmentation fault
錯誤消息。 盡管在下面的代碼中,如果我將靜態字符串值分配給char *name
此讀取功能將按預期工作。
以下是我創建的用於模擬問題的完整代碼。
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
typedef struct _student {
int id;
char *name;
} Student;
void write();
void read();
int main(int argc, char *argv[])
{
if (argc > 1) {
if (strcmp(argv[1], "--write") == 0) {
write();
read();
}
else if (strcmp(argv[1], "--read") == 0) {
read();
}
}
return 0;
}
void write()
{
printf("Write\n");
FILE *fp;
// write student
Student *std_writer = (Student *) malloc(sizeof(Student));
std_writer->id = 10;
//std_writer->name = "Alice"; // But if i remove the below 4 lines and uncommented this line, everything works as expected.
char *a = "Alice";
std_writer->name = malloc(20);
memset(std_writer->name, '\0', 20);
strncpy(std_writer->name, a, 5);
fp = fopen("Student.file", "wb");
fwrite(std_writer, sizeof(Student), 1, fp);
fwrite(std_writer, sizeof(Student), 1, fp);
fwrite(std_writer, sizeof(Student), 1, fp);
fclose(fp);
free(std_writer);
}
void read()
{
printf("Read\n");
FILE *fp;
// read student
Student *std_reader = (Student *) malloc(sizeof(Student));
fp = fopen("Student.file", "rb");
while(fread(std_reader, sizeof(Student), 1, fp) == 1) {
printf("ID %i \tName : %s\n", std_reader->id, std_reader->name);
}
fclose(fp);
free(std_reader);
}
請幫助我理解並解決此問題。
編輯
確定根據我所理解的以下答案,我按如下所示對struct Student進行了點綴。
typedef struct _student {
int id;
char name[20];
} Student;
這可行。
任何意見 ?
請注意,您並沒有寫上學生的姓名。 您僅將指針寫入該字符串。 當然,這不是您想要的。 當您讀取文件時,您正在讀取的指針不再有效。
要么將整個字符串放入結構(不是char指針,而是char數組)中,否則您應該將字符串分別寫入文件中。
不要叫你的函數read
和write
(這些名字是POSIX函數)。 並且不要期望能再次讀取由不同進程編寫的指針。 這是未定義的行為 。
因此,在您的write
中(假設使用64位x86系統,例如Linux),您將寫12個字節(4即sizeof(int)
+ 8即sizeof(char*)
); 后8個字節是某些malloc
指針的數值。
在read
您正在讀取這12個字節。 因此,您要將name
字段設置為在完成write
的過程中恰好有效的數字指針。 這通常不會起作用(例如,由於ASLR )。
通常,對指針執行I / O是非常糟糕的。 它只對同一過程有意義。
您要執行的操作稱為序列化 。 出於軟件工程方面的原因,我建議使用文本格式進行序列化(例如JSON ,也許使用Jansson庫)。 文本格式不那么脆弱,並且更容易調試。
假設您將以JSON格式編碼學生,例如
{ "id":123, "name":"John Doe" }
這是使用Jansson的可能的JSON編碼例程:
int encode_student (FILE*fil, const Student*stu) {
json_t* js = json_pack ("{siss}",
"id", stu->id,
"name", stu->name);
int fail = json_dumpf (js, fil, JSON_INDENT(1));
if (!fail) putc('\n', fil);
json_decref (js); // will free the JSON
return fail;
}
注意,您需要一個函數來釋放由malloc
分配的Student
區域,這里是:
void destroy_student(Student*st) {
if (!st) return;
free (st->name);
free (st);
}
而且您可能還需要宏
#define DESTROY_CLEAR_STUDENT(st) do \
{ destroy_student(st); st = NULL; } while(0)
現在,這是使用Jansson的JSON解碼例程; 它在堆中提供了一個Student
指針(稍后由調用者使用DESTROY_CLEAR_STUDENT
銷毀)。
Student* decode_student(FILE* fil) {
json_error_t jerr;
memset (&jerr, 0, sizeof(jerr));
json_t *js = json_loadf(fil, JSON_DISABLE_EOF_CHECK, &err);
if (!js) {
fprintf(stderr, "failed to decode student: %s\n", err.text);
return NULL;
}
char* namestr=NULL;
int idnum=0;
if (json_unpack(js, "{siss}",
"id", &idnum,
"name", &namestr)) {
fprintf(stderr, "failed to unpack student\n");
return NULL;
};
Student* res = malloc (sizeof(Student));
if (!res) { perror("malloc student"); return NULL; };
char *name = strdup(namestr);
if (!name) { perror("strdup name"); free (res); return NULL; };
memset(res, 9, sizeof(Student));
res->id = id;
res->name = name;
json_decref(js);
return res;
}
您還可以決定以某種二進制格式進行序列化(我不建議您這樣做)。 然后,您應該定義序列化格式並堅持使用。 很可能您必須對學生ID,其名稱的長度,其名稱進行編碼。
您還可以(在C99中)確定學生的name
是一個靈活的數組成員 ,即聲明
typedef struct _student {
int id;
char name[]; // flexible member array, conventionally \0 terminated
} Student;
您確實希望學生姓名的長度不同。 然后,您不能簡單地將不同長度的記錄放在簡單的FILE
。 您可以使用一些索引文件庫,例如GDBM (每個記錄可以在JSON中)。 您可能要使用Sqlite或像MariaDb或MongoDB這樣的真實數據庫。
在read()
,您永遠不會在Student
結構中為name
分配內存。 (在這方面,您的write()
函數的行為要好得多。)
在printf
語句中引用它時,將調用未定義的行為 。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.