【问题标题】:Writing char pointer as struct member to file issue将 char 指针作为结构成员写入文件问题
【发布时间】:2017-04-23 20:57:36
【问题描述】:

我有一个结构成员作为 char * 并将其分配给结构初始化时的字符串文字“John”,如下所示。使用 printf 可以很好地打印字符串。

但是,如果我使用 fwrite 将此字符串写入文件,我会在文件中读回垃圾。

如果我使用 char 数组而不是 char *(如图所示在结构中注释掉)并将其写入文件,我可以按预期读回文件中的字符串。我无法理解这一点。 fwrite 在这两种情况下都不应该获取指针并将正确的字符串写入文件吗?

此外,如果我声明一个单独的 char * 并将其指向一个字符串文字并将其写入文件,我可以按预期将其读回。非常感谢您提供任何解释帮助。 (我在 Windows 上使用带有 Mingw 编译器的 codeblocks IDE)。

更新:包含实际使用的代码

int main()
{
    struct person
    {
        //char name[20];
        char* name;
        int age;
    };

    struct person p1 = {"John", 25};

    printf("%s\n", p1.name);
    FILE* fp = fopen("test.txt", "w");
    fwrite(&p1, 1, 10, fp);
    fclose(fp);
    return 0;
}

【问题讨论】:

  • 如何你如何写和读字符串来得到“垃圾”?您是否在单个 fwrite 调用中编写了整个结构?请尝试为 failing 程序创建一个Minimal, Complete, and Verifiable Example 并向我们展示。
  • 当你这样做时,你应该知道指针是特定于单个进程的,如果你在对fwrite的一次调用中编写完整的结构,你就编写了指针 而不是它所指向的。 fwrite 函数对它写入的数据一无所知,它所拥有的只是一系列匿名字节。
  • 我希望得到更优雅的回复。这是一个学习和帮助他人的地方,而不是傲慢自大。
  • 表现得很傲慢。要求 minimal reproducible example 的评论只是 SO 规则的应用。给一个,你会得到答案,解释什么是错误的以及如何解决它。但是没有它,我们只能尝试猜测,目前我不知道您的问题可能出在哪里。
  • 我可以告诉你,写 10 个字节的 p1 是没有意义的;该结构可能大于 10 个字节。您如何确定 10 是要写入的正确字节数?

标签: c pointers struct char fwrite


【解决方案1】:

有了这个结构定义:

struct person
{
    char* name;
    int age;
};

以下代码不会达到您的预期:

struct person p1 = {"John", 25};

printf("%s\n", p1.name);
FILE* fp = fopen("test.txt", "w");
fwrite(&p1, 1, 10, fp);

原因是变量p1: 的字节实际上并不包含字符串“John”,但它包含一个指向字符串“John”的指针,即内存字符串“John”的地址。现在fwrite(&p1, 1, 10, fp);将从p1的内存地址开始写入10个字节,即(假设是32位系统):4个字节表示指针,它只是一些内存地址,4个字节表示1(年龄)和内存中不属于p1变量的2个字节。

现在如果结构是这样定义的:

struct person
{
    char name[20];
    int age;
};

情况完全不同。这次变量p1 实际上确实包含字符串“John”。前 4 个字节包含字符 John,然后是 NUL 字符,它是字符串终止符,然后是 15 个字节的未确定值。然后跟随代表age的4个字节。

在这种情况下,fwrite(&p1, 1, 10, fp); 将再次从p1 的内存地址开始写入 10 个字节,但如上段所述,前 4 个字节包含“John”,第 5 个字节包含 NUL 终止符和剩余 5 个字节的值不确定。

【讨论】:

  • 当我们终于看到代码时,你坚持这一点并提供正确答案,你得到了我的投票!
  • 谢谢。我的困惑是/是数组总是被视为与指针相同。当我们将数组传递给函数时,传递的是指针。在这种情况下,我认为 fwrite 会看到 char* 名称指向并写入它。
  • @Engineer999 fwrite“看到”的唯一东西是指向一堆字节和长度的指针。 fwrite 不知道它在写什么。
【解决方案2】:

评论者说您提供的代码没有提供足够的信息来解决您的问题。看起来错误在您的代码中的其他地方。我可以像这样将您的代码转换为MCVE

#include <stdio.h>

struct person
{
    //char name[20];
    char* name;
    int age;
};

int main(void)
{
    struct person p1 = {"John", 25};

    printf("%s\n", p1.name);

    FILE *fp = fopen("test.txt", "w");
    fwrite(p1.name, 1, 10, fp);
    fclose(fp);

    char buffer[20];
    fp = fopen("test.txt", "r");
    fread(buffer, 1, 10, fp);
    fclose(fp);

    printf("The stored name is: %s\n", buffer);

    return 0;
}

但我确信你所拥有的是不同的,因为这段代码有效:

John
The stored name is: John

更新

从您提供的新代码中,我可以看出您的问题是您正在将 struct 的内容写入文件,而不是像原始代码示例中那样将字符串写入文件。

使用的原始代码:

fwrite(p1.name, 1, 10, fp);

并将字符串 p1.name 的 10 个字节写入文件 "test.txt"(吹过字符串 "John" 的终止符 NUL 并保存打印字符串时看不到的垃圾值)。

新代码使用:

fwrite(&p1, 1, 10, fp);

structp1的前10个字节保存到文件"test.txt"中。

struct 包含char name[20]; 时,前10 个字节是chars 存储在字符数组name 中,这就是您的代码在这种情况下似乎可以工作的原因。

struct 包含char *name; 时,保存的前几个字节属于指针name(在我的系统上为8 个字节),而不是字符串文字"John"。接下来的几个字节属于intage(我的系统上有4个字节)。在这种情况下,保存的是这些值,而不是字符串文字 "John"chars。

注意,指针和ints的大小在不同的系统上可能不同,完全可以有4个字节的指针和4个字节的int,剩下的2个字节被保存不是struct 的一部分。此外,虽然这不会影响您,但struct 的第一个成员之后可能会有填充。

【讨论】:

  • 我确定您的代码与 OP 的代码完全不同。我不明白他为什么不发布 MCVE。
猜你喜欢
  • 1970-01-01
  • 2021-12-09
  • 1970-01-01
  • 2018-05-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-01-26
  • 1970-01-01
相关资源
最近更新 更多