微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

链接列表的插入函数有错误吗? WORD_LIST.HWORD_LIST.C

如何解决链接列表的插入函数有错误吗? WORD_LIST.HWORD_LIST.C

WORD_LIST.H

我只想创建一个链接列表,我逐行从文件获取数据并存储在链接列表中,但在列表末尾我调用显示函数,因此它不打印单词而是打印单个单词CNAA 一次又一次

 struct word_node {
      struct word_node * next;
      const char * word;
    };
    struct word_list {
      struct word_node * head;
      long num_words;
    };

    void reading_words(const char * filename);
    void insert(struct word_list *,const char*);
    void display(struct word_list *p);

WORD_LIST.C

 void reading_words(const char * filename){
      struct word_list *p ;
      FILE* file = fopen(filename,"r");
      char *line = NULL;
      size_t len = 0;
      ssize_t read;
      p = (struct word_list*)malloc(sizeof(struct word_list));
      p->head  =NULL;
      p->num_words =0;
      while ((read = getline(&line,&len,file)) != -1) {
        line[strlen(line)-1] = '\0';
        p->num_words++;
        insert(p,line);
       }
      printf("Size : %ld\n",p->num_words);
      display(p);
      return ;
    }
    void insert(struct word_list *p,const char* word){
      struct word_node *test ;
      test = (struct word_node *)malloc(sizeof(struct word_node));
      if(test == NULL){
        printf("Out of Memory!");
       }
      else{
        test->word = word;
        if(p->head == NULL){
           p->head = test
           p->head->next = NULL;
          }
        else{
           test->next = p->head;
           p->head = test;
          }
       }
    }
    void display(struct word_list *p){
      struct word_node *temp;
      temp = p->head;
      while(temp!=NULL){
        printf("%s\n",temp->word);
        temp=temp->next;
       }
    }

根据 Word 文件,单词是 极客 手提袋 欧维 黄色 MTA 一位警察 再见 舞蹈 BSRT CNAA

但是在插入功能之后它只打印CNAA 10次

解决方法

当你插入单词时,你就是在调用

insert(p,line);

并将局部变量 line(指向 char 的指针)作为参数传递。稍后在插入功能中你有

test->word = word;

这只是复制指针,所以 test->word 将指向与 line 函数中的局部变量 reading_words 相同的地址。因此,列表的每个元素都指向相同的地址。 相反,您应该使用 strcpy 来复制字符串,但在此之前,您必须为字符串分配内存(稍后将其释放到某处)。类似的东西:

test->word = malloc(strlen(word)+1);
strcpy(test-word,word);
,

让我们尝试更新 reading_words 函数如下:

...
while ((read = getline(&line,&len,file)) != -1) {
    line[strlen(line)-1] = '\0';
    p->num_words++;
    insert(p,line);
    line = NULL;
    len = 0;
}
...

根据 getline(3) (link) 的文档:

如果在调用前 *lineptr 设置为 NULL 并且 *n 设置为 0,则 getline() 将分配一个缓冲区来存储该行。

,

正如其他人所解释的,您正在向每个单词插入相同的指针,而 getline() 只会更改指针指向的缓冲区的内容。

我建议使用 C99 灵活数组成员而不是指针:

struct word {
    struct word *next;
    char         word[];
};

struct word *new_word(const char *);
void         insert_word(struct word **,struct word *);

struct word *read_words(FILE *);
void         free_words(struct word *);
void         show_words(struct word *,FILE *);

new_word() 函数从给定的字符串创建一个新的 struct word,通过复制数据到新的动态分配结构:

struct word *new_word(const char *src)
{
    const size_t  srclen = (src) ? strlen(src) : 0;
    struct word  *w;

    w = malloc(sizeof (struct word) + srclen + 1);
    if (!w) {
        fprintf(stderr,"Out of memory.\n");
        exit(EXIT_FAILURE);
    }
    w->next = NULL;

    if (srclen > 0)
        memcpy(w->word,src,srclen);

    w->word[srclen] = '\0';

    return w;
}

即使在 src == NULL 时,上述方法也有效(创建一个空词)。由于 strlen(NULL) 不安全,我们使用 (expression) ? (if-true) : (if-false) 三元表达式来计算数据的长度。同样,如果没有数据,我们也不想使用 memcpy() 来复制数据。

另一个新函数 free_words() 可用于释放整个单词列表,获取指向列表中第一个单词的指针:

void free_words(struct word *list)
{
    while (list) {
        struct word *curr = list;

        list = list->next;

        /* Poison the word,so that if we accidentally
           use a word after it has been freed,we'll
           surely notice.  This is purely a measure to
           help with debugging programming bugs. */
        curr->next = NULL;
        curr->word[0] = '\0';

        /* Free this word. */
        free(curr);
    }
}

因为 insert_word() 现在需要一个指向指针的指针(指向列表变量的指针)和指向要插入的结构的指针,所以它变得微不足道:

void insert_word(struct word **listptr,struct word *w)
{
    /* Only do this if neither pointer is NULL. */
   if (listptr && w) {
       w->next = *listptr;
       *listptr = w;
   }
}

我们甚至可以实现insert_words(),它在另一个列表前面插入一个列表:

void insert_words(struct word **list,struct word *first)
{
    if (list && first) {
        struct word *last = first;

        /* Find last word in first-list */
        while (last->next)
            last = last->next;

        last->next = *list;
        *list = first;
    }
}

show_words() 几乎没有变化,除了我更喜欢它将它应该打印到的流作为第二个参数。通常,它只会是 stdout

void show_words(struct word *list,FILE *out)
{
    if (list && out) {
        while (list) {
            fputs(list->word,out);
            fputs("\n",out);
            list = list->next;
        }
    }
}

请注意,由于 C 按值传递参数,因此修改 list 变量仅在函数本身内可见。调用者用来传递给 show_words() 的任何变量都不会被修改,即使我们修改了 list 中的 show_words()

这留下了 read_words()。同样,我更喜欢将流句柄而不是文件名传递给函数。通过这种方式,您也可以从标准输入读取输入,只需传递 stdin。该函数返回读取的单词列表,以相反的顺序,因为我们将读取的每个单词添加到列表的开头。

struct word *read_words(FILE *in)
{
    struct word *list = NULL;
    struct word *curr;
    char        *line = NULL;
    size_t       size = 0;
    ssize_t      len;

    if (!in)
        return NULL;

    while (1) {
        len = getline(&line,&size,in);
        if (len == -1)
            break;

        unsigned char *ptr = (unsigned char *)line;
        unsigned char *end = (unsigned char *)line + len;

        /* Skip leading whitespace (including newline). */
        while (ptr < end && isspace(*ptr))
            ptr++;

        /* Trim trailing whitespace (including newline). */
        while (end > ptr && isspace(end[-1]))
            end--;
        *end = '\0';

        /* Create the word structure. */
        curr = new_word(ptr);

        /* Prepend to the list. */
        insert_word(&list,curr);
    }

    /* Line buffer is no longer needed.  Note,free(NULL) is safe to do. */
    free(line);

    return list;
}

unsigned char *ptrunsigned char *end 的原因是 isspace() 将字符代码转换为无符号字符。如果我们只使用 char *,我们必须使用 isspace((unsigned char)(*ptr))isspace((unsigned char)(end[-1])) 来使 isspace() 在所有情况下都能正常工作。

您还应该包含 <locale.h><ctype.h>,并在 main() 的开头告诉 C 库使用运行程序的用户的当前语言环境,通过>

    setlocale(LC_ALL,"");

根据使用的字符集(它是语言环境的一部分),isspace() 可能会将不同的代码视为空格。特别是,如果使用 ISO Latin 1、ISO Latin 15 或 Windows-1252(8 位西欧),则代码 160(不间断空格)也被视为空格。 (如果你在 Linux 中使用这样的语言环境,你通常通过 AltGr+Space 输入它。) 添加这只是一件小事,并使您的代码在许多不同情况下都能按用户预期工作。

如果您想要原始文件顺序中的单词列表,则需要将其反转。幸运的是,这很容易做到:

struct word *reverse_word_order(struct word *list)
{
    struct word *newlist = NULL;

    while (list) {
        struct word *curr = list;

        /* Advance original list pointer */
        list = list->next;

        /* Prepend word to newlist */
        curr->next = newlist;
        newlist = curr;
    }

    return newlist;
}

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。