在结构中包含char *指针的二进制文件读/写错误

蓝鸟

我有一个奇怪的问题。我不知道为什么会这样。我尝试了各种方式。可能是因为我仍然是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;

这有效。

任何意见 ?

巴西勒·斯塔林凯维奇

不要叫你的函数readwrite(这些名字是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或像MariaDbMongoDB这样的真实数据库

本文收集自互联网,转载请注明来源。

如有侵权,请联系[email protected] 删除。

编辑于
0

我来说两句

0条评论
登录后参与评论

相关文章

来自分类Dev

C#:在二进制文件中读写列表

来自分类Dev

在Android中读写二进制文件

来自分类Dev

C#:在二进制文件中读写列表

来自分类Dev

C无效二进制文件中的错误消息

来自分类Dev

从具有指向内存的指针的二进制文件中读取结构

来自分类Dev

Word文件的二进制结构

来自分类Dev

从二进制文件读取char *

来自分类Dev

Java从二进制结构化文件中读取错误的值

来自分类Dev

从C ++中的二进制文件中删除结构

来自分类Dev

从二进制文件中读取

来自分类Dev

在C ++ 11中定义二进制文件的结构

来自分类Dev

如何更新二进制文件中的结构项

来自分类Dev

无法从二进制文件中读取结构

来自分类Dev

在python包中包含外部二进制文件

来自分类Dev

在DEB包中包含二进制文件

来自分类Dev

在DEB包中包含二进制文件

来自分类Dev

为什么“二进制文件”不在原始二进制文件中?

来自分类Dev

WebView中的“ android.view.InflateException:二进制XML文件错误夸大类”错误

来自分类Dev

在二进制文件中找不到二进制文本

来自分类Dev

在OS X(C ++)中设置Eclipse错误:启动失败,找不到二进制文件?

来自分类Dev

解决Postgres.app中的“找不到二进制文件”错误

来自分类Dev

在OS X(C ++)中设置Eclipse错误:启动失败,找不到二进制文件?

来自分类Dev

在 runc 容器中执行二进制文件时出现分段错误

来自分类Dev

如何从二进制文件读取多个结构

来自分类Dev

二进制文件写入顺序如何结构?

来自分类Dev

读取结构化二进制文件c ++

来自分类Dev

用二进制文件填充结构

来自分类Dev

从二进制文件到屏幕的printf结构

来自分类Dev

如何从二进制文件读取多个结构

Related 相关文章

热门标签

归档