为什么我不能退出这个while循环?

4

我正在尝试制作一个小程序来练习计算标准偏差。我的问题似乎在主函数中的第一个while语句。

我有点生疏,无法弄清楚为什么用户按下回车后我不能离开 while 语句。

请原谅我的新手状态。我还在学习中。

#include <stdio.h>
#include <math.h>
#define arraySize 100
double standardDeviation(int, double*);
int main(void){
    double array[arraySize];
    double result;
    int i=0;
    int count=0;
    printf("Enter up to %d data separated by spaces then hit enter:\n\n",arraySize);
    while(i<arraySize && array[i]!='\n'){
        scanf("%lf",&array[i]);
            i++;
            count++;
            }

    result=standardDeviation(count, array);

    printf("The standard deviation of your data is: %lf",result);

    return 0;
}

double standardDeviation(int count, double* firstDatum){
    int i=0,j=0;
    double standDev=0;
    double standDevArray[arraySize];
    double sum=0,sum2=0;
    double mean=0,variance=0;
    while(i<count){
        sum=sum + firstDatum[i]; 
        //printf("%lf", sum);
        i++;
    }
    mean=sum/count;
    //printf("The mean is: %lf", mean);

    while(j<count){
           standDevArray[j] = (mean  - firstDatum[j]) * (mean - firstDatum[j]);
           sum2=sum2+standDevArray[j];
           j++;
    }   
    variance=sum2/count;
    standDev=sqrt(variance);
    return standDev;
 }

查看 i 的值,因此在与换行符比较时查看 array[i] 的值。 - ilmiacs
2
array[i] 是一个 double。虽然将其与 '\n' 进行比较并不是错误的,但这很奇怪和不寻常 - 最好写 13.0 而不是 '\n',也许(但更有可能的是,这个测试是多余的)。此外,i 的值超出了 array 中最后一个初始化条目的范围 - 你的代码存在 未定义行为 (UB)。你也应该检查 scanf() 调用的结果。 - Jonathan Leffler
4
用户按下回车键并不会终止您的循环,因为 %lf 格式会跳过空白字符,包括换行符。如果以 13 作为值输入,您可能会(很可能)退出循环。在 scanf() 中检测换行符很困难。您必须使用字符输入(%c%s%[…])将换行符传递给程序数据。 - Jonathan Leffler
while(i<arraySize && scanf("%lf",&array[i]) == 1) { ... } - David C. Rankin
1
@JonathanLeffler '\n' 通常是10,而不是13。('\r' 通常是13。) - Keith Thompson
@KeithThompson — 我同意。深夜脑抽。 - Jonathan Leffler
3个回答

1
没有理由询问用户输入条目数。你应该要求用户输入一行内容,使用fgets(或 POSIX getline)将其读入缓冲区,然后通过扫描缓冲区将输入的数字转换为double值,使用strtod(首选),或使用sscanf和偏移量读取下一个值。
你已经很好地保护了数组边界,使用了while (i < arraySize ...,你需要添加第二个条件在有效的转换执行时。例如:
#define MAXC 2048
...
int main(void) {

    char buf[MAXC];                     /* buffer to hold line of input */
    double array[arraySize], result;    /* your array and result */
    int i = 0, offset = 0, used;        /* i, offset in buf, chars used */

    printf ("Enter up to %d data separated by spaces then hit enter:\n\n",
            arraySize);
    if (!fgets (buf, MAXC, stdin)) {    /* read/validate line of input */
        fputs ("(user canceled input)\n", stderr);
        return 1;
    }

    while (i < arraySize && /* while in bounds && valid conversion */
            sscanf (buf + offset, "%lf%n", &array[i], &used) == 1) {
        offset += used;     /* update offset with chars used in conversion */
        i++;                /* increment index */
    }

    result = standardDeviation (i, array);  /* compute/display result */

    printf ("The standard deviation of your data is: %lf\n",result);

    return 0;
}

现在你只需显示提示符,用户输入最多100个值,然后将这些值读入数组并进行计算。这里2K缓冲区大小允许每个值约19个字符。如果不足,请使用4K缓冲区并为每个值提供约40个字符。
此外,在standardDeviation()函数中,没有必要声明stdDevArray或单独的循环计数器变量。自C99以来,您可以将循环变量声明为循环定义的一部分。使用stdDevArray没有问题,它只是不是必需的。通过一致地排版代码也有助于提高可读性。如果消除单独的循环变量和不需要的stdDevArray,则可以调整函数如下:
double standardDeviation (int count, double *firstDatum)
{
    double  standDev = 0,
            sum = 0, sum2 = 0,
            mean= 0, variance = 0;

    for (int i = 0; i < count; i++)
        sum += firstDatum[i]; 

    mean = sum / count;

    for (int i = 0; i < count; i++)
        sum2 += (mean  - firstDatum[i]) * (mean - firstDatum[i]);

    variance = sum2 / count;
    standDev = sqrt(variance);

    return standDev;
}

请注意,+= 运算符只是一个简便操作符。你可以用 sum += firstDatum[i]; 来代替 sum = sum + firstDatum[i];。请告诉我如果您有进一步的问题。

0

这段代码中有两个错误,其中一个导致了未定义行为

首先,您将double(array [i])与char('\ n')进行比较。

第二个错误是索引“i”。在while循环中,您首先执行scanf并分配数组的第i个元素,然后使用操作i ++增加i,然后在下一次迭代中将未初始化的第i个元素与某些内容进行比较。 请注意,在比较时,您的索引已经递增,并且没有为该元素分配任何内容,这会导致未定义的行为


将“double”与“char”进行比较不会导致未定义的行为;因为“char”是整数类型,它被转换为“double”。 - ad absurdum
1
是的,我表达得很糟糕。我的意思是程序总体上由于第二个错误导致了未定义的行为。 - Zig Razor
那么对于第一个错误,虽然不是未定义的,但是'\n'从scanf中没有被读入到双精度数组中,因此我需要找到另一种方法来从用户输入中退出循环? 至于第二个错误,您是说我应该将i++改为++i吗? 为什么呢?而且我确实已经将i初始化为0了。请看上面几行。 - sethlearn
@exnihilo 一个小问题:'\n'的类型是int,而不是char。(这不会影响其行为。) - Keith Thompson
@KeithThompson -- 当然;我太粗心了 ;) - ad absurdum

0

我对主函数进行了以下调整。我根据用户输入预先确定了数据条目的数量,它运行得很好。 知道我的函数在第一次调用时就能给出正确的标准差值,这让我感到安心。

但是,我不得不询问用户输入的条目数量,这有点令人不舒服。 我认为如果我可以输入数据并在列表末尾终止,那么程序会更加用户友好。特别是对于一个人不知道要输入多少数据的较大数据集。使用数组并带有超出范围的值来终止有点棘手,因为限制条目的值也限制了函数的功能。如果有人想制作这样的程序,您认为链表更适合吗?

#include <stdio.h>
#include <math.h>
#define arraySize 100
double standardDeviation(int, double);
int main(void){
    double array[arraySize];
    double result;
    int userSize;
    int i=0;
    int count=0;
    printf("Enter how many data will be entered up to a maximum of %d\n\n",arraySize);
    scanf("%d",&userSize);
    printf("Enter your data:\n\n");
        while(i<userSize && i<arraySize){
        scanf("%lf",&array[i]);
            i++;
            count++;
            }


    result=standardDeviation(count, array);

    printf("The standard deviation of your data is: %lf",result);


    return 0;
}

    double standardDeviation(int count, double* firstDatum){
        int i=0,j=0;
        double standDev=0;
        double standDevArray[arraySize];
        double sum=0,sum2=0;
        double mean=0,variance=0;
        while(i<count){
            sum=sum + firstDatum[i]; 
            //printf("%lf", sum);
            i++;
        }
        mean=sum/count;
        //printf("The mean is: %lf", mean);

        while(j<count){
               standDevArray[j] = (mean  - firstDatum[j]) * (mean - firstDatum[j]);
               sum2=sum2+standDevArray[j];
               j++;
        }   
        variance=sum2/count;
        standDev=sqrt(variance);
        return standDev;
     }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接