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