c - 为什么我的打印功能会用以下代码中的最后一个条目覆盖以前的条目?
问题描述
这是将用户条目存储在哈希表中的简单程序的开始。它仍然非常基本(尚未添加链表来处理冲突,哈希函数非常简单,并且是更强大的临时占位符,我还没有处理释放所有 malloc 的内存,等),但我想在继续之前先弄清楚基本功能。
在这个版本中,我的最新条目似乎覆盖了以前的条目。看起来条目仍然在正确的位置(由散列函数决定),但不知何故,最后一个是放置在任何地方的那个。
抱歉,我知道代码很多,但我不确定如何创建此代码的较小版本来隔离问题:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define BUF_SIZE_WORDS 4096
#define BUF_SIZE_NUMBERS 256
#define MAX_WORDS 10
typedef struct tablestruct
{
int val;
char* text;
struct tablestruct *next;
}
table;
// declare the hashing function that takes in the string to be hashed and the length of it
unsigned int hash(char *str, int length);
// // declare a linked list creation function
// lnode *createnode(char *str, htnode *hashtable);
// declare a hash table printing function
void print_ht(table *array[MAX_WORDS]);
int number_input();
int word_input(int num_words, table *array[MAX_WORDS]);
void empty_stdin();
int main(void)
{
// call number_input() to ask user for number of words they'd like to store, save that in word_count
int word_count = number_input();
// create hash table
table *array[MAX_WORDS];
for (int j = 0; j < MAX_WORDS; j++)
{
array[j] = malloc(sizeof(table));
array[j]->val = j;
}
// add some kind of a memory check?
// PUT word_input inside the if statement to make sure it worked?
// call word_input() and store the result in success
int success = word_input(word_count, array);
// if not successful:
if (!success)
{
fputs ("some kind of problem with word_input\n", stderr);
return 1;
}
// printf("\ncurrent address of the hash table: %p\n", &array[0]);
printf("printing hash table: \n");
print_ht(array);
// REMEMBER TO FREE WHATEVER'S MALLOC'ED
}
int number_input(void)
{
// a bunch of code is borrowed from David's answer here: https://stackoverflow.com/questions/52920852/why-is-the-following-code-not-allowing-me-to-get-user-input-with-fgets-yet-works?noredirect=1#comment92940817_52920852
int num_words = 0, /* number of words to enter */
word_count_check = 0; /* word count */
char buffer[BUF_SIZE_NUMBERS] = ""; /* buffer of sufficient size for input */
for (;;) /* loop continually until valid input of NUMBER OF WORDS USER WANTS TO ENTER or user cancels */
{
printf ("how many words would you like to enter? [1-%d]: ", MAX_WORDS);
// check for cancellation of input
if (!fgets (buffer, BUF_SIZE_NUMBERS, stdin))
{
fputs ("user canceled input\n", stderr);
return 1;
}
// check if user simply hit enter w/o typing anything
if(buffer[0] == '\n')
{
printf("please enter a value\n");
continue;
}
size_t inlength = strlen(buffer);
if (inlength && buffer[inlength - 1] == '\n')
{
// printf("hurray!\n");
buffer[--inlength] = 0;
}
else if (inlength == BUF_SIZE_NUMBERS - 1) /* the line was too long */
{
printf("you've entered too many characters... please stick to a maximum of %i\n", BUF_SIZE_NUMBERS);
empty_stdin();
// continue;
}
// make sure user actually entered a proper int
if (sscanf (buffer, "%d", &num_words) != 1) /* sscanf is used for conversion */
{
fputs ("invalid conversion to int; please provide valid input\n", stderr);
continue;
}
// check if the number entered is out of range
if (num_words < 1 || num_words > MAX_WORDS)
fprintf (stderr, "%2d out of valid range.\n", num_words);
else
break; /*if the input has been validated, we can now break out of the for loop */
}
return(num_words);
}
int word_input(int num_words, table *array[MAX_WORDS])
{
int word_count = 0;
for(;;) /* loop until word_count == num_words is achieved */
{
// declare an array for storing input string
char buffer[BUF_SIZE_WORDS];
char valid_input[BUF_SIZE_WORDS];
// prompt user for input
printf("\nplease enter a string: ");
// get input and check for CTRL+D
if (!fgets(buffer, BUF_SIZE_WORDS, stdin))
{
fputs ("user canceled input\n", stderr);
exit(1);
}
// check if user simply hit enter w/o typing anything
if(buffer[0] == '\n')
{
printf("please enter a word that's more than 0 characters\n");
// empty_stdin();
continue;
}
size_t inlength = strlen(buffer);
if (inlength && buffer[inlength - 1] == '\n')
{
buffer[--inlength] = 0;
// get rid of trailing spaces using sscanf
sscanf(buffer, "%s", valid_input);
inlength = strlen(valid_input);
printf("string length: %zu\n", inlength);
// call the hash function to get the hash code
int result = hash(&valid_input[0], inlength);
table *newnode = malloc(sizeof(table));
// store the current string in the newnode->text
newnode->text = valid_input;
// strcpy(newnode->text, valid_input); ??
// newnode->val = inlength;
// confirm string has been stored
printf("you've entered: %s\n", newnode->text);
// attach the node to correct slot in the hash table -- ADD LINKED LIST FUNCTIONALITY HERE TO DEAL WITH COLLISIONS!
array[result]->next = newnode;
// printf("address of the current HT entry is: %p\n", newnode);
// increment word count
word_count++;
printf("word_count = %i\n", word_count);
if (word_count == num_words)
{
printf("\nDONE!\n\n");
return word_count;
}
}
// check if the user entered too many characters
else if (inlength == BUF_SIZE_WORDS - 1) /* the line was too long */
{
printf("you've entered too many characters... please stick to a maximum of %i\n", BUF_SIZE_WORDS);
empty_stdin();
}
}
// return word_count;
}
/* helper function to remove any chars left in input buffer */
void empty_stdin()
{
int c = getchar();
while (c != '\n' && c != EOF)
c = getchar();
}
// THIS HASH FUNCTION IS TOO BASIC AND NEEDS TO BE REPLACED WITH SOMETHING BETTER
unsigned int hash(char *str, int length)
{
int sum = 0;
for (int j = 0; j < length; j++)
{
sum += str[j];
}
printf("here's what the hashing function is returning: %i\n", (sum % MAX_WORDS));
return sum % MAX_WORDS;
}
void print_ht(table *array[MAX_WORDS])
{
// printf("address of the hash table inside print function: %p\n\n", array);
table *cursor = malloc(sizeof(table));
// add memory check
for (int i = 0; i < MAX_WORDS; i++)
{
printf("[%i] -> ", i);
cursor = array[i];
if (cursor->next)
{
table *temp = malloc(sizeof(table));
temp = cursor->next;
printf("%s\n", temp->text);
free(temp);
}
else
{
printf("empty\n");
}
}
free(cursor);
}
我确信这段代码还有其他问题,我感谢任何人愿意在这里提供的任何其他建议。例如,我总是想知道代码的哪些部分需要是它们自己的函数,哪些应该/可以组合在一起。似乎这些东西中的一些应该被分离出来,但我在弄清楚如何正确地将信息从一个函数传递到另一个函数时遇到了很多麻烦。
更新#1(在一些评论之后):
是的,这现在有效!
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define BUF_SIZE_WORDS 4096
#define BUF_SIZE_NUMBERS 256
#define MAX_WORDS 10
typedef struct tablestruct
{
int val;
char* text;
struct tablestruct *next;
}
table;
// declare the hashing function that takes in the string to be hashed and the length of it
unsigned int hash(char *str, int length);
// // declare a linked list creation function
// lnode *createnode(char *str, htnode *hashtable);
// declare a hash table printing function
void print_ht(table *array[MAX_WORDS]);
int number_input();
int word_input(int num_words, table *array[MAX_WORDS]);
void empty_stdin();
int main(void)
{
// call number_input() to ask user for number of words they'd like to store, save that in word_count
int word_count = number_input();
// create hash table
table *array[MAX_WORDS];
for (int j = 0; j < MAX_WORDS; j++)
{
array[j] = malloc(sizeof(table));
array[j]->val = j;
}
// add some kind of a memory check?
// PUT word_input inside the if statement to make sure it worked?
// call word_input() and store the result in success
int success = word_input(word_count, array);
// if not successful:
if (!success)
{
fputs ("some kind of problem with word_input\n", stderr);
return 1;
}
// printf("\ncurrent address of the hash table: %p\n", &array[0]);
printf("printing hash table: \n");
print_ht(array);
// REMEMBER TO FREE WHATEVER'S MALLOC'ED
}
int number_input(void)
{
// a bunch of code is borrowed from David's answer here: https://stackoverflow.com/questions/52920852/why-is-the-following-code-not-allowing-me-to-get-user-input-with-fgets-yet-works?noredirect=1#comment92940817_52920852
int num_words = 0, /* number of words to enter */
word_count_check = 0; /* word count */
char buffer[BUF_SIZE_NUMBERS] = ""; /* buffer of sufficient size for input */
for (;;) /* loop continually until valid input of NUMBER OF WORDS USER WANTS TO ENTER or user cancels */
{
printf ("how many words would you like to enter? [1-%d]: ", MAX_WORDS);
// check for cancellation of input
if (!fgets (buffer, BUF_SIZE_NUMBERS, stdin))
{
fputs ("user canceled input\n", stderr);
return 1;
}
// check if user simply hit enter w/o typing anything
if(buffer[0] == '\n')
{
printf("please enter a value\n");
continue;
}
size_t inlength = strlen(buffer);
if (inlength && buffer[inlength - 1] == '\n')
{
// printf("hurray!\n");
buffer[--inlength] = 0;
}
else if (inlength == BUF_SIZE_NUMBERS - 1) /* the line was too long */
{
printf("you've entered too many characters... please stick to a maximum of %i\n", BUF_SIZE_NUMBERS);
empty_stdin();
// continue;
}
// make sure user actually entered a proper int
if (sscanf (buffer, "%d", &num_words) != 1) /* sscanf is used for conversion */
{
fputs ("invalid conversion to int; please provide valid input\n", stderr);
continue;
}
// check if the number entered is out of range
if (num_words < 1 || num_words > MAX_WORDS)
fprintf (stderr, "%2d out of valid range.\n", num_words);
else
break; /*if the input has been validated, we can now break out of the for loop */
}
return(num_words);
}
int word_input(int num_words, table *array[MAX_WORDS])
{
int word_count = 0;
for(;;) /* loop until word_count == num_words is achieved */
{
// declare an array for storing input string
char buffer[BUF_SIZE_WORDS];
char valid_input[BUF_SIZE_WORDS];
// prompt user for input
printf("\nplease enter a string: ");
// get input and check for CTRL+D
if (!fgets(buffer, BUF_SIZE_WORDS, stdin))
{
fputs ("user canceled input\n", stderr);
exit(1);
}
// check if user simply hit enter w/o typing anything
if(buffer[0] == '\n')
{
printf("please enter a word that's more than 0 characters\n");
// empty_stdin();
continue;
}
size_t inlength = strlen(buffer);
if (inlength && buffer[inlength - 1] == '\n')
{
buffer[--inlength] = 0;
// get rid of trailing spaces using sscanf
sscanf(buffer, "%s", valid_input);
inlength = strlen(valid_input);
printf("string length: %zu\n", inlength);
// call the hash function to get the hash code
int result = hash(&valid_input[0], inlength);
table *newnode = malloc(sizeof(table));
newnode->text = malloc(strlen(valid_input)+1);
strcpy(newnode->text, valid_input);
// confirm string has been stored
printf("you've entered: %s\n", newnode->text);
// attach the node to correct slot in the hash table -- ADD LINKED LIST FUNCTIONALITY HERE TO DEAL WITH COLLISIONS!
array[result]->next = newnode;
// printf("address of the current HT entry is: %p\n", newnode);
// increment word count
word_count++;
printf("word_count = %i\n", word_count);
if (word_count == num_words)
{
printf("\nDONE!\n\n");
return word_count;
}
}
// check if the user entered too many characters
else if (inlength == BUF_SIZE_WORDS - 1) /* the line was too long */
{
printf("you've entered too many characters... please stick to a maximum of %i\n", BUF_SIZE_WORDS);
empty_stdin();
}
}
// return word_count;
}
/* helper function to remove any chars left in input buffer */
void empty_stdin()
{
int c = getchar();
while (c != '\n' && c != EOF)
c = getchar();
}
// THIS HASH FUNCTION IS TOO BASIC AND NEEDS TO BE REPLACED WITH SOMETHING BETTER
unsigned int hash(char *str, int length)
{
int sum = 0;
for (int j = 0; j < length; j++)
{
sum += str[j];
}
printf("here's what the hashing function is returning: %i\n", (sum % MAX_WORDS));
return sum % MAX_WORDS;
}
void print_ht(table *array[MAX_WORDS])
{
// printf("address of the hash table inside print function: %p\n\n", array);
table *cursor; // = malloc(sizeof(table));
// add memory check
for (int i = 0; i < MAX_WORDS; i++)
{
printf("[%i] -> ", i);
cursor = array[i];
if (cursor->next)
{
table *temp; //= malloc(sizeof(table));
temp = cursor->next;
printf("%s\n", temp->text);
// free(temp);
}
else
{
printf("empty\n");
}
}
// free(cursor);
}
解决方案
您正在newnode->text = valid_input;
为每个节点进行设置。但是valid_input
是在 for 循环范围内声明的本地缓冲区word_input
。所以这不是有效的代码 - 不保证缓冲区存在于该函数之外。
实际上,valid_input
它包含一些堆栈内存的地址,每次进入此函数时都会重复使用该地址,因此所有node->text
指针最终都指向包含最后输入的值的同一内存。
您应该能够通过打印所有节点的文本指针的值来验证这种情况。
您需要做的是为要保存的每个条目创建一个新缓冲区。
推荐阅读
- html - 容器底部的元素不覆盖容器中的文本
- python - 为什么在尝试导入装饰器时出现此错误?
- android - 通过在运动布局中使用 touchAnchorId 按钮不起作用
- android - 在 Android 中,Volley 中,如何传递单个字符串参数并接收字符串结果?
- excel - VBA遍历列中的值,将值放在影响公式的单元格中,并将结果公式复制并粘贴到相邻列
- php - 如何更改nova中的数据源?
- python - Dask:将自定义函数应用于 DataFrame 会出错
- css - 在 CSS 中的两种背景颜色之间叠加图像
- gatsby - GraphiQL 排序导致错误“无法读取未定义的属性 'map'”
- android - Android 相当于 iOS 的 AVMIDIPlayer?