c - 无法将 header 设置为指向链表中的新节点

标签 c linked-list

所以我试图插入一些东西作为链接列表的新头(我为它创建了一个函数以及所有这些) - 但由于某种原因,当我运行代码时,我收到了段错误 - 我已经缩小了范围节点的原因在哪里,但我不确定为什么它会导致问题?

most_freq.h

#ifndef MOST_FREQ_H_
#define MOST_FREQ_H_

#include <stdio.h>

//used to hold each "word" in the list
typedef struct word_node
{
char *word;
unsigned int freq; //frequency of word 
struct word_node *next;
} word_node;

struct node *readStringList(FILE *infile);

int ReadLine(FILE *infile, char * line_buffer);

struct node *GetMostFrequent(struct word_node *head, unsigned int num_to_select);

void PrintStringList(struct word_node *head);

void FreeStringList(struct word_node *head);

void SortedInsert(char * word, word_node *head);

void PushNewHead(node_t ** head, char * word);

struct word_node* CreateNode(char * string);

char *strip_copy(const char *s); //removes any new line characters from strings

#endif

most_freq.c

#include "most_freq.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

char* str_buffer = NULL;

struct word_node* CreateNode(char * string) {
    word_node* new_node = malloc(sizeof(word_node));
    new_node->word = string;
    new_node->freq = 1;
    new_node->next = NULL;

    return new_node;
}

void PushNewHead(word_node ** head, char * word) {
    word_node * new_node;
    new_node = malloc(sizeof(word_node));

    new_node->word = word;
    new_node->next = *head;
    printf("*Head word is: %s\n", (new_node->next)->word);
    *head = new_node;
    return;
}

void SortedInsert(char * word, word_node *head) {
    //first check if head node is empty
    if(head->word == NULL) { //if head->word is null, then list is empty
        //setup head node
        head->word = word;
        head->freq = 1;
        head->next = NULL;
        return;
    }
    else { //otherwise, list isn't empty and we need to traverse it
        word_node * current = head; //set current to head parameter
        word_node * prev = NULL; //set previous to NULL (to track previous node)
        printf("Attempting to insert: %s\n",word);
        while(current != NULL) { //while current isn't null
            char* currentNodeWord = current->word;
            if(strcmp(word,currentNodeWord) == 0) { //word matches current node's word
                printf("%s is already in the list, updating the frequency counter\n",word);
                current->freq++; //increase node's frequency
                break;
            }
            else if(strcmp(word,currentNodeWord) > 0) { //word comes after current node's word alphabetically
                prev = current;
                current = current->next; //move current node pointer
            }
            else if(strcmp(word,currentNodeWord) < 0) { //word comes before current node's word alphabetically
                //prepare node for insertion
                if(current = head) { //if current = head, then we're at the first item in the list
                    printf("%s is the new head node.\n",word);
                    PushNewHead(&head,word);
                }
                struct word_node * new_node = malloc(sizeof(word_node));
                new_node = CreateNode(word);
                prev->next = new_node;
                new_node->next = current;
            }
        }
        //if current node is null, we're at the end of the list, so insert the new node

    }
}

void PrintStringList(struct word_node *head) {
    word_node * current = head;
    printf("List of Strings (and Frequencies)\n\n");
    while(current != NULL) {
        printf("%s (Frequency: %d)\n", current->word, current->freq);
        current = current->next;
    }
}

int ReadLine(FILE *infile, char * line_buffer) {
   fscanf(infile, "%s", line_buffer);
   str_buffer = strdup(line_buffer);
   if(str_buffer[0] != '\0' || strcmp(str_buffer, "") != 0) {
    return EXIT_SUCCESS; //return success code
   }
   else {
    return EXIT_FAILURE; //return failure code
   }
}

struct node *readStringList(FILE *infile) {

    //setup empty linked list
    word_node * root = NULL;
    root = malloc(sizeof(word_node));
    if(root == NULL) { //check if root was successfully allocated
        printf("Not enough memory to create linked list.\n");
        exit(EXIT_FAILURE);
    }

    char* temp_buffer = malloc (sizeof(char) * 255); //buffer for 255 chars
    while(!feof(infile) && ReadLine(infile, temp_buffer) == EXIT_SUCCESS) { //while there is still something to be read from the file
        SortedInsert(str_buffer, root); //pass in root node to str_buffer
    }
    printf("Preparing to print list.\n");
    PrintStringList(root);
}

int main(int argc, char *argv[])
{
    if (argc == 2) // no arguments were passed
    {
        FILE *file = fopen(argv[1], "r"); /* "r" = open for reading, the first command is stored in argv[1] */
        if ( file == 0 )
        {
            printf( "Could not open file.\n" );
        }
        else
        {
            printf("Starting program.\n\n");
            readStringList(file);
        }
    }
    else if (argc < 3) {
        printf("You didn't pass the proper arguments! The necessary arguments are: <number of most frequent words to print> <file to read>\n");
    }
}

文本文件(从终端执行时作为参数读入)

bat
bat
bat
ant

我认为问题出在 *head = new_node 行,但我无法弄清楚为什么这会导致问题?

最佳答案

从根本上来说,问题在于 SortedInsert() 的接口(interface)。你有:

void SortedInsert(char *word, word_node *head);

这可以防止 SortedInsert() 报告新头。它无法更改调用代码中的head。有两种可能的设计(两者都有效):

word_node *SortedInsert(char *word, word_node *head);  // V1
void SortedInsert(char *word, word_node **head);       // V2

显然,它们的使用方式不同:

head = SortInserted(new_word, head);  // V1
SortInserted(new_word, &head);        // V2

您的代码还存在许多其他问题。我记得修复过的问题包括:

  • 只有在尝试使用最后一个条目两次之后,您才能可靠地检测到 EOF。
  • 您有一个奇怪的组织,其中包含 str_buffer 全局变量和 temp_buffer 变量。
  • 您在函数 ReadLine() 中读取单词,而不是行。
  • 当您可能指的是 word_node(或 struct word_node)时,您可以在各种函数原型(prototype)中使用 struct node。 (练习:你知道为什么编译器没有提示吗?)
  • 您声明了未实现的函数(因此您的代码不是 MCVE ( Minimal, Complete, Verfiable Example )。
  • 您的测试代码不会在每次添加元素时打印列表。这是一个常见的错误。你需要一个打印功能;您需要在空列表、一个元素列表和两个元素列表等上练习该函数。这样您可以更快地发现问题。 (早期版本的代码很高兴添加了bat,但是添加了cat失败,但是添加了ant成功了,但没有其他任何东西。它添加每个条目后打印列表时很容易发现。)
  • 您在多个位置创建了单词节点,而不是始终使用单个函数。
  • (未修复)您没有明确定义的包外部接口(interface)。各种内部管理函数(PushNewHead()CreateNode())在本应是静态的时被公开为全局函数。并且您不会将 main() 和测试代码与“生产”代码分开。
  • 由于在增加现有单词的计数而不是添加单词时未释放 str_buffer,导致内存泄漏。

以下代码可以解决上述大多数问题,并且可能还可以解决许多其他问题。它存储在单个文件(ll19.c)中,但 header 可以轻松分离。该版本使用“V2”接口(interface)。

#ifndef MOST_FREQ_H_
#define MOST_FREQ_H_

#include <stdio.h>

typedef struct word_node
{
    char *word;
    unsigned int freq;
    struct word_node *next;
} word_node;

word_node *readStringList(FILE *infile);
char *ReadWord(FILE *infile);
void PrintStringList(struct word_node *head);
void FreeStringList(struct word_node *head);
void SortedInsert(char *word, word_node **head);
void PushNewHead(word_node **head, char *word);
word_node *CreateNode(char *string);

#endif

#include <assert.h>
#include <stdlib.h>
#include <string.h>

struct word_node *CreateNode(char *string)
{
    word_node *new_node = malloc(sizeof(word_node));
    new_node->word = string;
    new_node->freq = 1;
    new_node->next = NULL;

    return new_node;
}

void PushNewHead(word_node **head, char *word)
{
    word_node *new_node = CreateNode(word);
    new_node->next = *head;
    printf("*Head word is: %s\n", (new_node->next)->word);
    *head = new_node;
}

void SortedInsert(char *word, word_node **head)
{
    assert(head != 0);
    if (*head == NULL)
    {
        printf("New head word: [%s]\n", word);
        *head = CreateNode(word);
    }
    else
    {
        word_node *current = *head;
        word_node *prev = NULL;
        printf("Attempting to insert: %s\n", word);
        while (current != NULL)
        {
            char *currentNodeWord = current->word;
            if (strcmp(word, currentNodeWord) == 0)
            {
                printf("%s is already in the list, updating the frequency counter\n", word);
                current->freq++;
                free(word);
                return;
            }
            else if (strcmp(word, currentNodeWord) > 0)
            {
                printf("[%s] comes after [%s]\n", word, currentNodeWord);
                prev = current;
                current = current->next;
            }
            else 
            {
                assert(strcmp(word, currentNodeWord) < 0);
                if (current == *head)
                {
                    printf("[%s] is the new head node.\n", word);
                    PushNewHead(head, word);
                    return;
                }
                break;
            }
        }
        printf("Regular word: [%s] after [%s]\n", word, prev->word);
        struct word_node *new_node = CreateNode(word);
        prev->next = new_node;
        new_node->next = current;
    }
}

void PrintStringList(struct word_node *head)
{
    word_node *current = head;
    printf("List of Strings (and Frequencies):\n");
    while (current != NULL)
    {
        printf("%s (Frequency: %d)\n", current->word, current->freq);
        current = current->next;
    }
    putchar('\n');
}

char *ReadWord(FILE *infile)
{
    char line_buffer[255];
    if (fscanf(infile, "%254s", line_buffer) == 1)
    {
        char *str_buffer = strdup(line_buffer);
        if (str_buffer != 0 && strcmp(str_buffer, "") != 0)
            return str_buffer;
        free(str_buffer);
    }
    return 0;
}

struct word_node *readStringList(FILE *infile)
{
    word_node *root = NULL;
    char *str_buffer;

    while ((str_buffer = ReadWord(infile)) != NULL)
    {
        printf("Line: [%s]\n", str_buffer);
        SortedInsert(str_buffer, &root);
        PrintStringList(root);
    }
    return root;
}

void FreeStringList(word_node *node)
{
    while (node != NULL)
    {
        free(node->word);
        word_node *next = node->next;
        free(node);
        node = next;
    }
}

int main(int argc, char *argv[])
{
    if (argc != 2)
    {
        fprintf(stderr, "Usage: %s file\n", argv[0]);
        return EXIT_FAILURE;
    }
    FILE *file = fopen(argv[1], "r");
    if (file == 0)
    {
        fprintf(stderr, "%s: could not open file %s for reading.\n", argv[0], argv[1]);
        return EXIT_FAILURE;
    }
    printf("Starting program - reading %s.\n", argv[1]);
    word_node *root = readStringList(file);
    printf("Read complete.\n");
    PrintStringList(root);
    FreeStringList(root);
    fclose(file);
    return EXIT_SUCCESS;
}

示例文件data.1:

bat
bat
bat
ant

data.1 上运行的输出:

Starting program - reading data.
Line: [bat]
New head word: [bat]
List of Strings (and Frequencies):
bat (Frequency: 1)

Line: [bat]
Attempting to insert: bat
bat is already in the list, updating the frequency counter
List of Strings (and Frequencies):
bat (Frequency: 2)

Line: [bat]
Attempting to insert: bat
bat is already in the list, updating the frequency counter
List of Strings (and Frequencies):
bat (Frequency: 3)

Line: [ant]
Attempting to insert: ant
[ant] is the new head node.
*Head word is: bat
List of Strings (and Frequencies):
ant (Frequency: 1)
bat (Frequency: 3)

Read complete.
List of Strings (and Frequencies):
ant (Frequency: 1)
bat (Frequency: 3)

示例文件data.2:

bat
bat
cat
bat
auk
dog
ant
bee
ant
cow
bat
pig
hen

data.2 上运行的输出:

Starting program - reading data.2.
Line: [bat]
New head word: [bat]
List of Strings (and Frequencies):
bat (Frequency: 1)

Line: [bat]
Attempting to insert: bat
bat is already in the list, updating the frequency counter
List of Strings (and Frequencies):
bat (Frequency: 2)

Line: [cat]
Attempting to insert: cat
[cat] comes after [bat]
Regular word: [cat] after [bat]
List of Strings (and Frequencies):
bat (Frequency: 2)
cat (Frequency: 1)

Line: [bat]
Attempting to insert: bat
bat is already in the list, updating the frequency counter
List of Strings (and Frequencies):
bat (Frequency: 3)
cat (Frequency: 1)

Line: [auk]
Attempting to insert: auk
[auk] is the new head node.
*Head word is: bat
List of Strings (and Frequencies):
auk (Frequency: 1)
bat (Frequency: 3)
cat (Frequency: 1)

Line: [dog]
Attempting to insert: dog
[dog] comes after [auk]
[dog] comes after [bat]
[dog] comes after [cat]
Regular word: [dog] after [cat]
List of Strings (and Frequencies):
auk (Frequency: 1)
bat (Frequency: 3)
cat (Frequency: 1)
dog (Frequency: 1)

Line: [ant]
Attempting to insert: ant
[ant] is the new head node.
*Head word is: auk
List of Strings (and Frequencies):
ant (Frequency: 1)
auk (Frequency: 1)
bat (Frequency: 3)
cat (Frequency: 1)
dog (Frequency: 1)

Line: [bee]
Attempting to insert: bee
[bee] comes after [ant]
[bee] comes after [auk]
[bee] comes after [bat]
Regular word: [bee] after [bat]
List of Strings (and Frequencies):
ant (Frequency: 1)
auk (Frequency: 1)
bat (Frequency: 3)
bee (Frequency: 1)
cat (Frequency: 1)
dog (Frequency: 1)

Line: [ant]
Attempting to insert: ant
ant is already in the list, updating the frequency counter
List of Strings (and Frequencies):
ant (Frequency: 2)
auk (Frequency: 1)
bat (Frequency: 3)
bee (Frequency: 1)
cat (Frequency: 1)
dog (Frequency: 1)

Line: [cow]
Attempting to insert: cow
[cow] comes after [ant]
[cow] comes after [auk]
[cow] comes after [bat]
[cow] comes after [bee]
[cow] comes after [cat]
Regular word: [cow] after [cat]
List of Strings (and Frequencies):
ant (Frequency: 2)
auk (Frequency: 1)
bat (Frequency: 3)
bee (Frequency: 1)
cat (Frequency: 1)
cow (Frequency: 1)
dog (Frequency: 1)

Line: [bat]
Attempting to insert: bat
[bat] comes after [ant]
[bat] comes after [auk]
bat is already in the list, updating the frequency counter
List of Strings (and Frequencies):
ant (Frequency: 2)
auk (Frequency: 1)
bat (Frequency: 4)
bee (Frequency: 1)
cat (Frequency: 1)
cow (Frequency: 1)
dog (Frequency: 1)

Line: [pig]
Attempting to insert: pig
[pig] comes after [ant]
[pig] comes after [auk]
[pig] comes after [bat]
[pig] comes after [bee]
[pig] comes after [cat]
[pig] comes after [cow]
[pig] comes after [dog]
Regular word: [pig] after [dog]
List of Strings (and Frequencies):
ant (Frequency: 2)
auk (Frequency: 1)
bat (Frequency: 4)
bee (Frequency: 1)
cat (Frequency: 1)
cow (Frequency: 1)
dog (Frequency: 1)
pig (Frequency: 1)

Line: [hen]
Attempting to insert: hen
[hen] comes after [ant]
[hen] comes after [auk]
[hen] comes after [bat]
[hen] comes after [bee]
[hen] comes after [cat]
[hen] comes after [cow]
[hen] comes after [dog]
Regular word: [hen] after [dog]
List of Strings (and Frequencies):
ant (Frequency: 2)
auk (Frequency: 1)
bat (Frequency: 4)
bee (Frequency: 1)
cat (Frequency: 1)
cow (Frequency: 1)
dog (Frequency: 1)
hen (Frequency: 1)
pig (Frequency: 1)

Read complete.
List of Strings (and Frequencies):
ant (Frequency: 2)
auk (Frequency: 1)
bat (Frequency: 4)
bee (Frequency: 1)
cat (Frequency: 1)
cow (Frequency: 1)
dog (Frequency: 1)
hen (Frequency: 1)
pig (Frequency: 1)

现在代码完善了吗?不,远非如此。但它已经在 valgrind 下运行并且具有良好的健康状况——显示的两个测试文件中的代码没有内存泄漏。 (我不会声称没有其他测试文件可以发现泄漏,但我持适度乐观的态度。)

关于c - 无法将 header 设置为指向链表中的新节点,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39432844/

相关文章:

java - 可迭代是如何工作的?

java - 链表程序java的意外输出

java - LinkedList 对象 add() 函数不更新值 - java

c - 将 size_t 变量添加到指针

c - 从字符串数组中删除字符串 (C)

c - 状态机教程

C - 使用 {} 进行视觉上高效的指针数组初始化

ios - 是否可以将旧的 xcode 代码更新为最新的 xcode?

c - 在 C 中添加到链表的前面

c++ - 用于通过快速迭代按值从任何位置删除的容器