You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C语言中含char*的结构体数组读写二进制文件后指针设置求助

解决带指针的结构体数组二进制读写异常问题

嘿,我一眼就看出你问题的症结了——你写入二进制文件的时候,完全漏掉了每个point结构体里的namelength字段!

你当前的写入逻辑只把所有name的字符数据写到了文件里,但没有保存每个point对应的namelength值。这就导致读取程序里的POINTS[i].namelength都是malloc后未初始化的垃圾值,你的指针移动逻辑完全基于错误的长度,自然会出现“第一个非空name打印所有内容”的问题——因为那个垃圾长度可能远大于实际的单个name长度,把后面的所有name都包含进去了。

下面是修正后的完整读写流程和代码:

一、正确的写入流程

我们需要把结构体的关键元数据(总点数N、每个point的namelength)和name内容一起写入文件,步骤如下:

  1. 用wb模式打开二进制文件
  2. 先写入总点数N,让读取程序知道要分配多少个point结构体
  3. 逐个写入每个point的namelength字段
  4. 最后写入所有name的字符内容

修正后的写入代码:

// 打开二进制输出文件
if ((fin = fopen(name, "wb")) == NULL){ 
    printf("the output binary data file cannot be open\n"); 
    exit(0); 
}

// 写入总点数N
if (fwrite(&N, sizeof(N), 1, fin) != 1) {
    printf("Failed to write total points count to file\n");
    exit(0);
}

// 逐个写入每个point的namelength
for (int i = 0; i < N; i++) {
    if (fwrite(&POINTS[i].namelength, sizeof(unsigned char), 1, fin) != 1) {
        printf("Failed to write namelength for point %d\n", i);
        exit(0);
    }
}

// 写入所有name的字符内容(保留你原来的逻辑)
for (int i=0; i < N; i++){
    if (POINTS[i].namelength){
        if( fwrite(POINTS[i].name, sizeof(char), POINTS[i].namelength, fin) != POINTS[i].namelength){
            printf("when writing names to the output binary data file\n"); 
            exit(0);
        }
    }
}

fclose(fin); // 别忘了关闭文件,确保数据写入磁盘

二、正确的读取流程

读取时要严格对应写入的顺序,并且注意用rb模式打开文件(Windows下文本模式会破坏二进制数据):

  1. 用rb模式打开二进制文件
  2. 读取总点数N
  3. 分配point数组内存
  4. 逐个读取每个point的namelength,同时计算总字符数namestot
  5. 分配所有name的内存
  6. 读取所有name内容,再根据正确的namelength设置name指针

修正后的读取代码:

// 打开二进制输入文件,必须用rb模式!
if ((fin = fopen (argv[1], "rb")) == NULL){ 
    printf("the data file does not exist or cannot be opened\n"); 
    exit(0); 
}

// 读取总点数N
if (fread(&N, sizeof(N), 1, fin) != 1) {
    printf("Failed to read total points count from file\n");
    exit(0);
}

// 分配point数组内存
if((POINTS = (point *) malloc (N * sizeof(point))) == NULL){ 
    printf("when allocating memory for the points vector\n"); 
    exit(0); 
}

// 读取每个namelength并计算总字符数namestot
size_t namestot = 0;
for (int i = 0; i < N; i++) {
    if (fread(&POINTS[i].namelength, sizeof(unsigned char), 1, fin) != 1) {
        printf("Failed to read namelength for point %d\n", i);
        exit(0);
    }
    namestot += POINTS[i].namelength;
}

// 分配所有name的内存
char *allnames = NULL;
if((allnames = (char *) malloc (namestot * sizeof(char))) == NULL){ 
    printf("when allocating memory for the names vector"); 
    exit(0); 
}

// 读取所有name内容
if (fread(allnames, sizeof(char), namestot, fin) != namestot){ 
    printf("when reading names from the binary data file\n"); 
    exit(0); 
}

// 设置name指针,用临时指针避免破坏allnames的起始地址
char *current_ptr = allnames;
for (int i = 0; i < N; i++){
    if(POINTS[i].namelength > 0){
        POINTS[i].name = current_ptr;
        current_ptr += POINTS[i].namelength;
        // 如果你需要用printf("%s")打印,这里可以手动加'\0'(注意要提前多分配内存)
        // *current_ptr = '\0';
        // current_ptr++;
    } else {
        POINTS[i].name = NULL; // 空name设置为NULL,避免野指针
    }
}

fclose(fin); // 关闭文件

// 后续使用完记得释放内存,避免泄漏
// free(POINTS);
// free(allnames);

三、额外注意事项

  • 二进制模式必须用对:写入用wb,读取用rb,Windows系统中文本模式会自动转换换行符,导致二进制数据错位。
  • 字符串结尾问题:如果原结构体的name是C风格字符串(带'\0'),写入时要把'\0'也计入namelength并写入;如果没有'\0',打印时要指定长度,比如printf("%.*s", POINTS[i].namelength, POINTS[i].name),避免越界访问。
  • 内存释放:程序结束前记得释放POINTS和allnames的内存,避免内存泄漏。

内容的提问来源于stack exchange,提问作者Arduino

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 08:38:05