我有一个奇怪的问题。我不知道为什么会这样。我尝试了各种方式。可能是因为我仍然是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;
这有效。
任何意见 ?
不要叫你的函数read
和write
(这些名字是POSIX函数)。而且不要期望能再次读取由不同进程编写的指针。这是未定义的行为。
因此,在您write
(假设是64位x86系统,例如Linux系统)中,您正在写入12个字节(4 ie sizeof(int)
+ 8 ie sizeof(char*)
);后8个字节是某些malloc
-ed指针的数值。
在你的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
-edStudent
区域,这里是:
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这样的真实数据库。
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句