[讨论] 俺的C习题(4)--给3来一个重写

辛昕   2012-1-3 23:32 楼主
这些天上班,写程序,自己没有养成良好的习惯 造成的 弊端 越来越暴露出致命的缺陷。
甚至让自己都怀疑自己是不是不会写程序了。

晚上不想加班了,累累的,回到家,静静想着调整思路。想到这个 一天一个C代码。
在哪里跌倒哪里爬起,哪里狗屎哪里完善。。
做生不如做熟,于是我决定把3改好。从小,重新培养好的习惯吧。

这次,我不再急于写代码,大概也是没压力(想想,上班,就是因为有压力,但越是这样我越是在没完全分析的情况下就先动手,结果,还不如不写。现在想想,我真的应该尽量自己掌握自己的节奏才行啊!)

我先写了一个简单的分析,PS:因为这个程序毕竟比较容易,而且我写过。

引用: /*Practise 8.1 */
//定义一个函数,给函数传送任意多个浮点数,计算出这些数的平均值。
//从键盘输入任意个值,并输出平均值,以说明这个函数的执行过程

首先明确这个题目:
给这个函数传送任意多个浮点数,而不是 多次调用——原来做的就是这样曲解了。
其次,这并不是要用 参数个数可变 的函数——参考答案中并未使用。

由此,这只能是按指针传递,而且,由于个数不定,需要动态为该指针分配内存。

由于是任意多个浮点数,所以需要一个输入来判断是否继续输入。
采用do-while循环,因为至少会进行一次(判断)。
但在实际操作中,我发现,do-while并不是好的选择——我得承认这可能和我残留的记忆有关系。

除第一次以外,判断应先于输入——其实都不是必须的,因为第一次有 是否需要(本次输入)的判断

鉴于上次的失误,对于判断是否继续输入的考虑。
由于scanf函数对输入的控制并不完善,但并非不可用。
我们只需判断’n’即可——如果用’y’判断,由于容易被参与数据影响,容易出现误判,而’n’,相比而言,被影响的可能性小很多——除非倒霉到家,残余一个’n’。
——当然,这种情形,则需另外考虑解决方法——不过,要分清楚是自己输入的和残余的数据中的’n’,也许只能用空格符分隔scanf的输入。

由此,我们可以写出这个函数的接口(声明)
double Average(double *buffer,unsigned char length);
因为传递的是指针,函数无法知道个数,所以需要传入长度。

这个程序另一个要解决的问题是 为指针动态分配内存。
需要两个数据块,一个用于存储,一个用于在动态分配时转存数据。

*value *buffer
使用时,value用于存储
buffer用于在value重新分配内存时,备份


下面是我的代码:
  1. /*参考
    原 型:void *calloc(unsigned n,unsigned size);
    功 能: 在内存的动态存储区中分配n个长度为size的连续空间,函数返回一个指向分配起始地址的指针;如果分配不成功,返回NULL。
    */

    #include<stdio.h>
    #include<string.h>
    #include<ctype.h>
    #include<stdlib.h>

    #define LENGTH 20
    #define INCR 5

    double Average(double *buffer,unsigned char length);

    int main(void)
    {
    unsigned char count = 0,length = LENGTH;
    char answer = 'n';
    double *buffer = NULL;
    double *list = NULL;
    char i;

    buffer = (double *)calloc(length,sizeof(double));
    if(buffer == NULL)
    {
    printf("Allocation failed!\n");
    exit(-1);
    }

    if(list == NULL)
    list = buffer;

    while(1)
    {
    /*检查空间是否足够,不够重新分配*/
    if(count >= length)
    {
    length += INCR; //扩展缓冲深度

    buffer = (double *)calloc(length,sizeof(double));
    if(buffer == NULL)
    {
    printf("Allocation failed!\n");
    exit(-1);
    }

    for(i = 0;i < count;i++)
    buffer[i] = list[i];

    free(list);
    list = buffer;
    }

    printf("Do you want a%s press?\n",(!count)?" ":"another");
    scanf(" %c",&answer);
    if(tolower(answer) == 'n')
    break;

    scanf(" %lf",list+count++);
    }

    printf("the average is:%.3lf\n",Average(list,count));

    return 1;
    }

    double Average(double *buffer,unsigned char length)
    {
    char i;
    double sum = 0.00;

    for(i = 0;i < length;i++)
    sum += buffer[i];

    return sum / length;
    }

强者为尊,弱者,死无葬身之地

回复评论 (4)

下面是参考答案

  1. /* Exercise 8.1 A function to calculate an average */
    #include <stdio.h>
    #include <stdlib.h>
    #include <ctype.h> //业务不熟啊,对于什么头文件包含什么声明,我并不十分清楚

    double average(double data[], int count)
    {
    double sum = 0.0;
    for(int i = 0 ; i<count ; sum += data[i++]) //这个写法还真让我开眼了。
    ;
    return sum/count;
    }

    #define CAPACITY_INCREMENT 6 /* Increment in the capacity for data values */

    int main(void)
    {
    double *data = NULL; /* Pointer to array of data values */
    double *newdata = NULL; /* Pointer to new array of data values */
    double *averages = NULL; /* Pointer to array of averages */
    int count = 0; /* Number of data values */
    int capacity = 0; /* Number of data values that can be stored */
    char answer = 'n';

    //原来如此,他的考虑和我不一样,他认为必然有一个输入,因此总是从输入数字开始
    //而我认为,我的考虑首先不违背题目,而且可能比他更完善——没人说我一定会输入
    do
    {
    if(count == capacity)
    {
    capacity += CAPACITY_INCREMENT;
    /* Create new array of pointers */
    newdata = (double*)malloc(capacity*sizeof(double));
    /* Create an array of values of type double for each new day and store the address */
    if(data) //先确定分配正确再操作,这个思路,比起常见的判非NULL,是更好的方法。
    {
    /* Copy the existing values to the new array */
    for(int i = 0 ; i<count ; i++)
    newdata[i] = data[i];
    free(data); /* Free memory for the old array of pointers */
    }
    data = newdata; /* copy the address of the new array of pointers */
    newdata = NULL; /* Reset the pointer */
    }

    printf("Enter a data value: ");
    scanf(" %lf", data+count++);
    printf("Do you want to enter another (y or n)? ");
    scanf(" %c", &answer);
    } while(tolower(answer) != 'n');

    printf("\nThe average of thew values you entered is %10.2lf\n", average(data, count));
    free(data); //我写的,疏忽,忘了释放。
    return 0; //关于正常和异常返回值,这里其实颇有说道。这一部分内容在下面说吧。
    }


强者为尊,弱者,死无葬身之地
点赞  2012-1-3 23:33

关于返回值

在参考答案最后一行代码的注释中,我说道,关于返回值颇有点说道的。下面就来说说我实际编程中看代码和自己写代码过程中总结出来的一点点看法;
别人的代码一般都这么写:
正常返回0或者要求的数值,错误则返回-1.

而我习惯性的是正常返回1,错误返回0,只有在带要求返回某个数值时,我才会采用上述的方法。

一直相安无事,但是有时还是会遇到麻烦。
我在一个自称完全符合ansi的编译器上有过这样一次“奇遇”:

char X(void)
{
。。。。。return -1;
}

结果呢,就出了警报,具体细节我记不得了,只是警告我有一个unsigned int和char的转换。
我马上意识到问题出在这个-1上。
后来我改成int 为返回类型,事情就解决了。

这事暂时在这说,因为我至今没去考究到底怎么回事。

这里提到这个事情,和上面说的错误返回-1或者0.
一般来说,我之所以返回0和1是为了直接在主调函数中用返回值作为条件判断。如果我换成-1,事情就麻烦点了。

而且注意到一个差别,很多很多的函数其返回值都是标准的int类型——对于int这个类型,我并没深入理解,但我就目前所知,它可能是一个编译器的数据类型中的 代表——因为它的字长就是 机器长度!而且都选他做返回值。

但是,像我们等在单片机上写程序的,大多数时候用char就够了。
所以,这个-1的问题究竟如何解决,有待研究研究。

不过,还有另一套方法可以兼顾返回值和主调函数判断的。
那就是宏定义 true false的数值,植标志和判断都用这个宏就可以了。
强者为尊,弱者,死无葬身之地
点赞  2012-1-3 23:45

继续鸡蛋里挑骨头哈
if(buffer == NULL)
{
printf(\"Allocation failed!\n\");
exit(-1);
}

if(list == NULL)
list = buffer;
这个地方没有必要做 list 判空的操作
直接写成

list = buffer;

即可
顺带提一句,按我的C语言设计风格,上述这段话,我会这样写:

if((list = buffer) == NULL)
{
printf(\"Allocation failed!\n\");
exit(-1);
}

 

[ 本帖最后由 能圈就圈 于 2012-1-4 14:02 编辑 ]
能力越大,责任越大;知道越多,未知更多
点赞  2012-1-4 13:58

回复 4楼 能圈就圈 的帖子

额,圈哥,你这个风格,就有点象 参考答案里那一句话了。

把循环三个要素和执行的写到一起了。
强者为尊,弱者,死无葬身之地
点赞  2012-1-4 16:04
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 京公网安备 11010802033920号
    写回复