【问题标题】:how to pass strings from a file to linked list如何将字符串从文件传递到链表
【发布时间】:2012-08-16 01:19:45
【问题描述】:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
struct ll
{
    char data[50];
    struct ll *next;
    struct ll *prev;
};
typedef struct ll node;

main()
{
    node *head;node *temp1;node *temp2;
    head = (node *)malloc(sizeof(node));
    temp1 = head;
    FILE *p;
    int n;
    char s[50];
    p = fopen("studentRecords.txt","r");
    while((fscanf(p, "%s", s)) != EOF)
    {
        strcpy(head->data, s);
        head->next = (node *)malloc(sizeof(node));
        head->next = head->next->prev;
        head = head->next;
    }
    head = NULL;
    fclose(p);
    for(temp2 = temp1; temp2->next != NULL; temp2 = temp2->next)
        printf("%s\n", temp2->data);
}

当我运行上述代码时,输​​出是分段错误。我该如何纠正?我在studentRecords.txt 文件中将学生的记录作为字符串保存。

【问题讨论】:

  • 与流行的看法相反,在源文件中可以有空格..
  • 当您尝试调试段错误时,我强烈建议您通过Valgrind 运行您的可执行文件,这是处理此类内存泄漏和错误的绝佳工具。
  • 您可能希望使用 strncpy 来防止缓冲区溢出错误/攻击。例如strcpy(head-&gt;data, s, 49); head-&gt;data[49] = 0;

标签: c file linked-list segmentation-fault


【解决方案1】:

您在初始化之前使用next

head->next = (node *)malloc(sizeof(node));
head->next = head->next->prev;             /* head->next->prev is garbage. */
head = head->next;                         /* Now `head` points nowhere. */

稍后您最终会取消引用该垃圾值。此外,您正在覆盖刚刚从malloc 获得的内存。

【讨论】:

    【解决方案2】:

    你必须首先初始化头指针:

    head = (node *)malloc(sizeof(node));
    head->prev = NULL;
    head->next = NULL;
    

    要阅读我建议使用 fgets() 的文件:

    p = fopen("studentRecords.txt","r");
    while(!feof(p))
    {
        fgets(head->data, 50, p);
    

    通常你分配一个更大的缓冲区(比如 1K,内存通常很便宜),将一行读入缓冲区并检查它是否有效且长度合适。如果不是,您可以发出信息性错误(“第 XXX 行,值 'yyy' 太长”)并中止,或忽略该行并继续。您还可以使用 strdup() 来完全避免该问题,使用带有 *data 的结构而不是 data[50]。您实际上可能会以这种方式最终节省内存。

    使用具有较短缓冲区的 fget 可能会导致 52 个字符的行被读取为两个错误记录而没有任何警告。

    最后这里的代码太紧凑了——而且你再次没有初始化指针:

        head->next = (node *)malloc(sizeof(node));
        head->next->prev = NULL;
        head->next->next = NULL;
    

    一旦你将你的行放在临时缓冲区中,你应该做的是创建一个新节点,完全初始化它,然后将它作为新的头:

        newNode = (node *)malloc(sizeof(node));
        newNode->prev = NULL;
        newNode->next = head;
        head->prev    = newNode;
        // A 
        strncpy(head->data, temp, 50);
        head->data[49] = 0x0;
        // B
        // head->data = strdup(temp);
        head = newNode;
    

    all-on-one-line head->next = (node*)... 可能看起来很漂亮,但读回、维护和调试很快就会变得很糟糕。而且编译器通常会在很短的时间内输出更优化的代码,这是您梦寐以求的。

    【讨论】:

    • 您提供给fgets 的大小包括字符串终止符所需的空间,因此50 实际上是正确的大小。 fgets 也将零终止字符串,因此您的 head-&gt;data[49] = 0; 也是不必要的。
    • 是的。我有时过于谨慎,我忘记了 fgets 会处理这些问题。
    【解决方案3】:
    head->next = head->next->prev;
    

    这是不正确的! head-&gt;next-&gt;prev 指向 'god-even-knows' 内存块,然后你将它分配给 head-&gt;next

    它会破坏head-&gt;next中有用的消息,并将其替换为垃圾。

    【讨论】:

      【解决方案4】:

      这里的主要问题是声明: head-&gt;next = head-&gt;next-&gt;prev;head-&gt;next-&gt;prev 尚未初始化,因此当您在下一条语句中将其分配给 head-&gt;next 时,它不会指向任何有用的东西。当您稍后尝试取消引用此值时,您的程序就会崩溃。你的意思可能是head-&gt;next-&gt;prev = head;

      也就是说,您的代码中还有许多其他值得评论的地方:

      1. 使用int main(void) 而不是main()
      2. 不要在 C 中将返回值强制转换为 malloc,这是不必要的,并且可以隐藏错误。
      3. 检查mallocfopen 的返回值。
      4. 如果您使用fscanf 读取数据,请注意%s 说明符只会读取直到遇到空白(跳过初始空白之后),这可能是也可能不是您想要的。此外,为防止缓冲区溢出,您需要确保不会使用%s 读取太多数据,因此请始终使用最大字段宽度,即%49sfgetssscanf 通常是更好的选择。
      5. 别忘了free()动态分配内存。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-05-05
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2022-01-21
        相关资源
        最近更新 更多