将用户输入添加到未知大小的数组

Posted

技术标签:

【中文标题】将用户输入添加到未知大小的数组【英文标题】:Add user input to array of unknown size 【发布时间】:2021-05-04 21:51:51 【问题描述】:

我对编程还是很陌生,在 C 之前我唯一的经验是 javascript。我正在做 CS50 Introduction to Computer Science 并且在其中一堂课中有一个示例代码可以计算一些用户输入的平均值。它看起来像这样:

#include <cs50.h>
#include <stdio.h>

const int TOTAL = 3;

float average(int length, int array[])
int main(void)

    int scores[TOTAL];
    for (int i = 0; i < TOTAL; i++)
    
      scores[i] = get_int("Score: ");
    

    printf("Average: %f\n", average(TOTAL, scores);


float average(int length, int array[])

    int sum = 0;
    for (int i = 0; i < length; i++)
    
        sum += array[i];
    
    return sum / (float) length;

我要添加的功能是根据用户输入动态存储数组的大小,而不是只有一个变量(在本例中为 TOTAL)。例如:我需要有一个循环,它总是向用户询问分数(而不是像上面的代码那样只有 3 次),当用户键入零(0)时,循环中断并且数组的大小是由用户输入分数的次数来定义。

这就是我所做的:

int main(void)

    int score;
    // start count for size of array
    int count = - 1;

    do
    
        score = get_int("Score: ");
        // add one to the count for each score
        count++;
    
    while (score != 0);
    
    // now the size of the array is defined by how many times the user has typed.
    int scores[count];

    for (int i = 0; i < count; i++)
    
        // how do I add each score to the array???
    

我的问题是如何将用户键入的每个分数添加到数组中。提前谢谢!!!

【问题讨论】:

关于:float average(int length, int array[]) int main(void) 第一条语句后缺少分号;,因此无法编译 关于:printf("Average: %f\n", average(TOTAL, scores); 这在半色 ; 之前缺少一个右括号,因此无法编译! 关于:return sum / (float) length; 这会导致编译器输出警告消息:untitled1.c:30:16: 警告:从 'int' 到 'float' 的转换可能会改变值 [- Wconversion] 编译时,始终启用警告,然后修复这些警告。 (对于gcc,至少使用选项:-Wall -Wextra -Wconversion -pedantic -std=gnu11)注意:其他编译器使用不同的选项来产生相同的结果 注意,只计算平均值,只需要保留输入值的总和和输入值的个数;您不需要具有已保存值的数组。然后,您可以简单地读取数字直到 EOF(或输入错误,例如字母或标点字符),然后通过计算 (double)sum / count 打印平均值。 【参考方案1】:

您需要一个可以根据需要扩展的“动态数据结构”。这是两种方式:

    当空间不足时,使用mallocrealloc 分配一个初始大小的数组。

    使用链表

(有更多方法,但这些方法对于这个问题来说很常见)

【讨论】:

【参考方案2】:

您需要有数据结构来跟踪大小并存储数据。

这里有一个简单的实现:

typedef struct

    size_t size;
    int result[];
SCORES_t;

SCORES_t *addScore(SCORES_t *scores, int score)

    size_t newsize = scores ? scores -> size + 1 : 1;
    scores = realloc(scores, newsize * sizeof(scores -> result[0]) + sizeof(*scores));
    if(scores)
    
        scores -> size = newsize;
        scores -> result[scores -> size - 1] = score;
    
    return scores;


double getAverage(const SCORES_t *scores)

    double average = 0;
    if(scores)
    
        for(size_t index = 0; index < scores -> size; average += scores -> result[index], index++);
        average /= scores -> size;
    
    return average;


int main(void)

    int x;
    SCORES_t *scores = NULL;

    while(scanf("%d", &x) == 1 && x >= 0)
    
        SCORES_t *temp = addScore(scores, x);
        if(temp)
        
            scores = temp;
        
        else
        
            printf("Memery allocation error\n");
            free(scores);
        
    
    if(scores) printf("Number of results: %zu Average %f\n", scores -> size, getAverage(scores));
    free(scores);

https://godbolt.org/z/5oPesn

【讨论】:

不要将realloc() 的返回值直接赋值给目标指针。相反,检查它是否有 (!=NULL)。如果为NULL,则处理错误,否则,更新目标指针 关于:scores = realloc(scores, newsize * sizeof(scores -&gt; result[0]) + sizeof(*scores)); 这将为每次调用 struct SCORES_t 添加另一个实例。这是一个错误 @user3629249 你的两个 cmets 都没有任何意义,表明你不知道这个 0 行程序做了什么。 1、你真的以为我不会用realloc吗?阅读代码并告诉我在哪里您看到了潜在的泄漏?检查是在“调用者”级别完成的。 2. 它用指向新分配区域的指针分配局部变量 scores。这个指针返回给调用者。来电者既有新的,也有旧的。然后它决定 new 是否不为 NULL。我认为您应该在评论之前花一些精力阅读代码。【参考方案3】:

关于:

int main(void)

    int score;
    // start count for size of array
    int count = - 1;

    do
    
        score = get_int("Score: ");
        // add one to the count for each score
        count++;
    
    while (score != 0);
    
    // now the size of the array is defined by how many times the user has typed.
    int scores[count];

    for (int i = 0; i < count; i++)
    
        // how do I add each score to the array???
    

这不会编译并包含几个逻辑错误

它缺少以下语句:#include &lt;cs50.h&gt;#include &lt;stdio.h&gt;

关于:

    int score;
    // start count for size of array
    int count = - 1;

    do
    
        score = get_int("Score: ");
        // add one to the count for each score
        count++;
    
    while (score != 0);

这仅定义了一个变量:score,并且每次通过循环时都会覆盖单个变量。此外,第一次循环时,计数器:count 将递增到 0,而不是 1

在接下来的每一次循环中,变量score 将被覆盖(即用户输入的所有先前值都将丢失)

建议使用动态内存。注意:要使用动态内存,需要头文件:stdlib.h 原型:malloc()free()。建议:

#include <cs50.h>
#include <stdio.h>
#include <stdlib.h>
    
int main( void )

    // pointer to array of scores
    int * score = NULL;

    // start count for size of array
    int count = 0;

    while( 1 )
    
        int score = get_int("Score: ");

        if( score != 0 )
          // then user has entered another score to put into array
            count++;
            int * temp = realloc( scores, count * sizeof( int ) )
            if( ! temp )
             // realloc failed
                // output error info to `stderr`
                // note: `perror()` from `stdio.h`
                perror( "realloc failed" );

                // cleanup
                free( scores );

                // `exit()` and `EXIT_FAILURE` from `stdlib.h`
                exit( EXIT_FAILURE );
            

            // implied else, 'realloc()' successful, so update the target pointer
            scores = temp;

            // insert the new score into the array
            scores[ count ] = score;
        

        else
         // user entered 0 so exit the loop
            break;
        
    

注意:在退出程序之前,将scores 传递给free(),这样就不会发生内存泄漏。

【讨论】:

谢谢,这个解决方案对初学者来说更有意义。在 CS50 课程中仍然有一些我不知道或看到的关于 C 的东西,比如指针和 malloc、realloc 等的使用。关于“缺少的参数”,我没有把它放在评论中,因为它是'不相关,但我的代码中有这些。而 count 等于 ` - 1 ` 是因为当我输入 0 退出循环时,0 仍会添加到计数中,然后例如 3 个分数而不是 3 个计数,3 个分数将是 4 个。那么平均数就会是错误的,因为除以错误的数字。反正我的逻辑是错的

以上是关于将用户输入添加到未知大小的数组的主要内容,如果未能解决你的问题,请参考以下文章

数组和指针+错误

c++中用new给未知大小的数组分配空间怎么弄?

c程序打印未知大小的字符串数组的元素

如何在 html 表中为未知数量的输入命名?

创建未知类型的数组。 C#

如何在 C# 中将用户输入添加到二维数组