为什么我的打印函数用下面代码中的最后一个条目覆盖前面的条目? [英] Why is my print function overwriting previous entries with the last entry in the following code?

查看:25
本文介绍了为什么我的打印函数用下面代码中的最后一个条目覆盖前面的条目?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这是一个简单程序的开始,该程序将在哈希表中存储用户条目。它仍然很基本(还没有添加处理冲突的链表,哈希函数非常简单,是更健壮的函数的临时占位符,我还没有处理释放所有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
", stderr);
        return 1;
    }

    // printf("
current address of the hash table: %p
", &array[0]);

    printf("printing hash table: 
");

    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
", stderr);
            return 1;
        }

        // check if user simply hit enter w/o typing anything
        if(buffer[0] == '
')
        {
            printf("please enter a value
");
            continue;
        }

        size_t inlength = strlen(buffer);

        if (inlength && buffer[inlength - 1] == '
')
        {
            // printf("hurray!
");
            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
", 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
", 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.
", 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("
please enter a string: ");

        // get input and check for CTRL+D
        if (!fgets(buffer, BUF_SIZE_WORDS, stdin))
        {
            fputs ("user canceled input
", stderr);
            exit(1);
        }

         // check if user simply hit enter w/o typing anything
        if(buffer[0] == '
')
        {
            printf("please enter a word that's more than 0 characters
");
            // empty_stdin();
            continue;
        }

        size_t inlength = strlen(buffer);

        if (inlength && buffer[inlength - 1] == '
')
        {
            buffer[--inlength] = 0;

            // get rid of trailing spaces using sscanf
            sscanf(buffer, "%s", valid_input);
            inlength = strlen(valid_input);

            printf("string length: %zu
", 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
", 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
", newnode);

            // increment word count
            word_count++;
            printf("word_count = %i
", word_count);

            if (word_count == num_words)
            {
                 printf("
DONE!

");
                 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
", 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 != '
' && 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
", (sum % MAX_WORDS));
    return sum % MAX_WORDS;
}

void print_ht(table *array[MAX_WORDS])
{
    // printf("address of the hash table inside print function: %p

", 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
", temp->text);
            free(temp);
        }
        else
        {
            printf("empty
");
        }
    }
    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
", stderr);
        return 1;
    }

    // printf("
current address of the hash table: %p
", &array[0]);

    printf("printing hash table: 
");

    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
", stderr);
            return 1;
        }

        // check if user simply hit enter w/o typing anything
        if(buffer[0] == '
')
        {
            printf("please enter a value
");
            continue;
        }

        size_t inlength = strlen(buffer);

        if (inlength && buffer[inlength - 1] == '
')
        {
            // printf("hurray!
");
            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
", 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
", 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.
", 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("
please enter a string: ");

        // get input and check for CTRL+D
        if (!fgets(buffer, BUF_SIZE_WORDS, stdin))
        {
            fputs ("user canceled input
", stderr);
            exit(1);
        }

         // check if user simply hit enter w/o typing anything
        if(buffer[0] == '
')
        {
            printf("please enter a word that's more than 0 characters
");
            // empty_stdin();
            continue;
        }

        size_t inlength = strlen(buffer);

        if (inlength && buffer[inlength - 1] == '
')
        {
            buffer[--inlength] = 0;

            // get rid of trailing spaces using sscanf
            sscanf(buffer, "%s", valid_input);
            inlength = strlen(valid_input);

            printf("string length: %zu
", 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
", 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
", newnode);

            // increment word count
            word_count++;
            printf("word_count = %i
", word_count);

            if (word_count == num_words)
            {
                 printf("
DONE!

");
                 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
", 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 != '
' && 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
", (sum % MAX_WORDS));
    return sum % MAX_WORDS;
}

void print_ht(table *array[MAX_WORDS])
{
    // printf("address of the hash table inside print function: %p

", 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
", temp->text);
            // free(temp);
        }
        else
        {
            printf("empty
");
        }
    }
    // free(cursor);
}

推荐答案

您正在为每个节点设置newnode->text = valid_input;。但是valid_input是在word_input中的for循环范围内声明的本地缓冲区。因此,这不是有效的代码--不能保证缓冲区存在于该函数之外。

实际上,valid_input包含一些堆栈内存的地址,每次您进入该函数时都会重复使用该地址,因此所有node->text指针最终都指向包含最后输入的值的同一内存。

您应该能够通过打印所有节点的文本指针的值来验证是否如此。

您需要做的是为要保存的每个条目创建一个新缓冲区。

这篇关于为什么我的打印函数用下面代码中的最后一个条目覆盖前面的条目?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆