zoukankan      html  css  js  c++  java
  • C语言初学者代码中的常见错误与瑕疵(7)

    问题:

    矩形的个数

      在一个3*2的矩形中,可以找到6个1*1的矩形,4个2*1的矩形3个1*2的矩形,2个2*2的矩形,2个3*1的矩形和1个3*2的矩形,总共18个矩形。给出A,B,计算可以从中找到多少个矩形


    输入:
      本题有多组输入数据(<10000),你必须处理到EOF为止

      输入2个整数A,B(1<=A,B<=1000)


    输出:

      输出找到的矩形数。


    样例:

    输入:

    1 2
    3 2


    输出:
    3
    18

     原代码-1:

    #include <stdio.h>
    int rectangle(int x,int y)
    {
        int i,j,sum,temp;
    
        for(i=1,sum=0,temp=0;i<=x;i++)
        {
            for(j=1;j<=y;j++)
            {
                temp = (x-i+1)*(y-j+1);
                sum += temp;
            }
        }
    
        return sum;
    }
    
    int main()
    {
        long int A[1000],B[1000],i;
        int rectangle(int x,int y);
        //printf("input A&B and end with 0
    ");
    
        for(i=0;i<1000;i++)
        {
            scanf("%d",&A[i]);
            
            if (A[i]==0)
            {
                break;
            }
    
            scanf("%d",&B[i]);
        }
    
        for(i=0;A[i]!=0;i++)
        {
            printf("%d
    ",rectangle(A[i],B[i]));
        }
    
        return 0;
    }

     评析:

       写完之后这位小朋友坦率地承认:

    露珠不知道如何以EOF结尾..只好用0了...哪位指点下..

       我告诉他,

    没必要用数组
    while( scanf("%d%d", &A,&B)!=EOF )
    {
         //计算输出矩形个数
    }

      于是他很快给出了新代码:

     原代码-2:

    #include <stdio.h>
    int rectangle(int x,int y)
    {
        int i,j,sum,temp;
    
        for(i=1,sum=0,temp=0;i<=x;i++)
        {
            for(j=1;j<=y;j++)
            {
                temp = (x-i+1)*(y-j+1);
                sum += temp;
            }
        }
    
        return sum;
    }
    
    int main()
    {
        long int A,B;
        int rectangle(int x,int y);
        printf("input A&B and end with EOF
    ");
    
        while(scanf("%d%d",&A,&B)!= EOF)
        {
            printf("%d
    ",rectangle(A,B));
        }
    
        return 0;
    }

    评析:

      这次好多了。不过他又有了新的困惑:

    我修改成这样之后,比如我先输入 2 3 跳出来 18,输入eof 就一直跳 18 停止不了了...
    总之就是一直跳最后一次出来的结果 ..

      看来他不清楚EOF是怎么回事,还以为是在键盘上键入eof三个字符呢。

      我告诉他:

    EOF不是三个字符
    而是一个符号常量
    如果你用的是WIN系统
    在行首输入Control-Z试试

      实际上EOF是在stdio.h中定义的一个宏,通常是这样的 

    #define    EOF    (-1)

      不过C语言并没说EOF一定为-1。

      在键盘上是没有这个EOF的,但在输入流中遇到特殊的字符,scanf()函数的返回值可以是EOF。那么,这句话究竟是什么意思呢?

      通常scanf()的返回值是一个非负整数。比如

    int i;
    scanf("%d", &i ) ;

      如果你在键盘上键入的是123(1、2、3与%d相匹配),scanf("%d", &i )的值就为1,因为为1个变量赋了值;如果你在键盘上键的是abc(a与%d不匹配),scanf("%d", &i )的值就为0,因为scanf无法把"a"视为十进制整数(%d),也无法对它进行转换,更无法为变量 i 赋值,也就是说scanf这种情况下没有为任何变量赋值,所以返回值为0。如果scanf()在输入流中遇到的是某个特殊的字符(具体是哪个字符与环境有关),则返回值为EOF。

      另外我告诉他:

    把函数类型声明
    int rectangle(int x,int y);
    写在main()函数之内很傻

    (应该把函数类型声明)写在函数外面

    另外那个temp多余

    temp赋值为零就更多余

    (因为可以直接)

    sum += (x-i+1)*(y-j+1);    

      就这样,他再次进行了修改:

    原代码-3

    #include <stdio.h>
    int rectangle(int x,int y)
    {
        int i,j,sum,temp;
    
        for(i=1,sum=0;i<=x;i++)
        {
            for(j=1;j<=y;j++)
            {
                sum += (x-i+1)*(y-j+1);;
            }
        }
    
        return sum;
    }
    
    int rectangle(int x,int y);
    int main()
    {
        long int A,B;
        printf("input A&B and end with EOF
    ");
    
        while(scanf("%d%d",&A,&B)!= EOF)
        {
            printf("%d
    ",rectangle(A,B));
        }
    
        return 0;
    }

     评析:

       现在毛病少多了,不过还是有一些。

      首先, rectangle()函数定义的位置不妥,写在main()定义的后面为好。

      其次,数据类型有问题,这个问题比较严重。 

        long int A,B;

      这里绝对没有必要把A、B定义为long类型,int类型足矣。由于不当地把A、B定义成了long类型,所以代码中的

    scanf("%d%d",&A,&B)

    rectangle(A,B)

    这两次调用都是错的。虽然没有产生错误的结果(估计在那个系统中long和int类型大小一样),但其实是瞎猫碰到死耗子,侥幸而已。

      此外的两处小瑕疵就是rectangle()函数定义中的temp变量忘记删除了,循环体内删除语句时不干净,有一“;”也忘记删除了。

      再有就是 

    sum += (x-i+1)*(y-j+1);

       这个算法我没看懂,不知道是否正确。

    重构:

      其实原来代码已经改得差不多了,只剩下少许小错和瑕疵。我在这里只说一下我的算法:

      我的算法是,穷举出两个点(P1,P2)的所有组合情况,只要P1可以是某个矩形的左上角,P2可以是某个矩形的右下角(P1_X<P2_X,P1_Y<P2_Y),则构成了一个矩形。

     1 /*
     2 矩形的个数 
     3 在一个3*2的矩形中,可以找到6个1*1的矩形,4个2*1的矩形3个1*2的矩形,
     4 2个2*2的矩形,2个3*1的矩形和1个3*2的矩形,总共18个矩形。 
     5 给出A,B,计算可以从中找到多少个矩形。 
     6 
     7 输入: 
     8 本题有多组输入数据(<10000),你必须处理到EOF为止 
     9 输入2个整数A,B(1<=A,B<=1000) 
    10 
    11 输出: 
    12 输出找到的矩形数。 
    13 
    14 样例:
    15 
    16 输入: 
    17 1 2 
    18 3 2 
    19 
    20 输出: 
    21 3 
    22 18
    23 
    24 作者:薛非
    25 出处:http://www.cnblogs.com/pmer/   “C语言初学者代码中的常见错误与瑕疵”系列博文 
    26 
    27 */
    28 
    29 #include <stdio.h>
    30 
    31 int count( int , int ); 
    32 
    33 int main( void )
    34 {
    35   int A , B ;
    36   
    37   while ( printf( "输入2个整数A,B(1<=A,B<=1000)" ),
    38           scanf( "%d%d" , &A , &B )!= EOF    
    39         )
    40   {
    41      printf( "%d
    " , count( A , B ) );
    42   }
    43   
    44   return 0;
    45 }
    46 
    47 int count( int A , int B )
    48 {
    49    int x1 , y1 ;//第一个点的坐标 
    50    int x2 , y2 ;//第二个点的坐标
    51    int num = 0 ;
    52    
    53    for ( x1 = 0 ; x1 <= B ; x1 ++ )
    54       for ( y1 = 0 ; y1 <= A ; y1 ++ )//穷举第一个点的各种可能 
    55          for ( x2 = 0 ; x2 <= B ; x2 ++ )
    56             for ( y2 = 0 ; y2 <= A ; y2 ++ )//穷举第二个点的各种可能 
    57             {
    58                if ( x1 < x2 && y1 < y2 )
    59                   num ++ ;
    60             }
    61 
    62    return num ;
    63 }

     BUG

      这个代码的不足之处在于没有认真思考答案是否在int类型的表示范围之内。在 飞鸟_Asuka 网友提出是否“时间复杂度比较大”的问题后,我一并考虑了这两个问题。结论是:1.这个问题用数学的办法很容易解决,不过采用这种方案对学习编程是不利的,因为求解太容易了;2.答案确实有可能超过int类型的表示范围(假如int最大能表示到231-1的话)。这应该算是重构代码中存在的一个BUG。我将在以后的博文中给出修正及数学解答。

      在此感谢飞鸟_Asuka 网友的提醒。

  • 相关阅读:
    javascript js 内存泄露
    javascript js 内存泄露工具使用
    逻辑运算符及按位或与非比较难理解先放着87
    //测试全局变量及局部变量的输出结果的异同
    域名被墙原因
    编写转义字符:78页
    1.1-java创建包和类的方法
    浏览器方法及代码打包成APP的
    一个简单的jsp+servlet实例,实现简单的登录
    LeetCode.985-查询后偶数的总和(Sum of Even Numbers After Queries)
  • 原文地址:https://www.cnblogs.com/pmer/p/3470760.html
Copyright © 2011-2022 走看看