第2天:用c语言打印星号组成的字符图案

题目描述:

        通过c语言,打印字符星号( * ),并使之呈现出下面的“小飞机”图案。

题目分析:

        图案可以看作由 6x12 的格子矩阵组成。

        考虑图案是由单一的星号重复构成,因此第一想法想到了利用双层循环,外层循环负责变换行数,内层循环负责变换列数。

        但是!又考虑到图案只有6行,因此也可以抛弃循环,采用暴力的硬输出:直接用 printf() 函数打印每一行需要的星号。

解法一:

        直接用 printf() 打印每一行需要的星号,代码如下:

#include <stdio.h>
int main()
{
    printf( "    **      \n" );
    printf( "    **      \n" );
    printf( "************\n" );
    printf( "************\n" );
    printf( "   *  *     \n" );
    printf( "   *  *     \n" );

    return 0;
}

解法二:

        我第一反应想到循环来完成,建立外层 i 循环负责变化行数,内层 j 循环负责变化列数,循环遍历第 i 行第 j 列,并利用 if 判断在需要打印 * 的位置调用 printf() 函数。

        近日网络上对于多层 if 嵌套造成的“屎山代码”讨论比较激烈,考虑非工作性以学习为目的,还是建议大家采用简洁的代码,尽量避免太多的 if 嵌套,因此代码考虑用switch-case语句处理外层循环,if语句处理内层循环。

#include <stdio.h>
int main()
{
    int i,j;
    for ( i = 0 ; i<6 ; i++ )
    {
        switch ( i )
        {
            case 0 :
            case 1 :
                for ( j = 0 ; j < 12 ; j++ )
                {
                    if ( j == 5 || j == 6)
                    {
                        printf ( "*" );
                    }
                    else
                    {
                        printf ( " " );
                    }
                }
                printf ( "\n" );break;

            case 2:
            case 3:
               for ( j = 0 ; j < 12 ; j++ )
                {
                    printf ( "*" );
                }
                printf ( "\n" );break;

            case 4 :
            case 5:
               for ( j = 0 ; j < 12 ; j++ )
                {
                    if ( j == 4 || j == 7)
                    {
                        printf ( "*" );
                    }
                    else
                    {
                        printf ( " " );
                    }
                }
                printf ( "\n" );break;
        }
    }

    return 0;
}

        注意,关于代码中出现

case 0 :

case 1 :

        的原因是,12行代码工作内容重复,因此可以利用 case 语句匹配成功后,如果未遇到 break 会继续执行的特点,让其能在 i=0 时匹配成功,又能在 i=1 时再次匹配此段代码,节省代码长度。

        上述代码还可以简化,运用冒号运算符弱化if条件判断,代码如下:

#include <stdio.h>
int main()
{
    int i,j;
    for ( i = 0 ; i<6 ; i++ )
    {
        switch ( i )
        {
            case 0 :
            case 1 :
                for ( j = 0 ; j < 12 ; j++ )
                {
                    printf( "%c", ( j==5 || j==6 ? '*' : ' ' ) );
                }
                printf ( "\n" );break;

            case 2:
            case 3:
               for ( j = 0 ; j < 12 ; j++ )
                {
                    printf ( "*" );
                }
                printf ( "\n" );break;

            case 4 :
            case 5:
               for ( j = 0 ; j < 12 ; j++ )
                {
                    printf( "%c", ( j==4 || j==7 ? '*' : ' ' ) );
                }
                printf ( "\n" );break;
        }
    }
    return 0;
}

解法三:

        我最喜欢的解法,采用了单片机编程中的位运算技巧。因为每一行的每一个格子只存在两种情况:要嘛是空格,要嘛是星号。因此我们可以采用 数字0代表空格,数字1代表星号。

        例如,第一行第6个和第7个格子星号,其余为空白,则可以表示为二进制代码 000001100000 ,将二进制转化为十六进制为 0x060 ,即第一二行可利用 0x060 确定星号位置。第三四行可利用 0xfff 确定,第五六行可利用 0x090 确定。将产生的十六进制数取名为:星号位置编码。

        将相应的星号位置编码存入数组,利用外层 i 循环变化行数,在对应行数取出数组对应值。这里需要明白一个关键点,那就是如何在对应的行中,在星号位置编码中数字1对应的位置打印 * 。思路是采用移位,内层 j 循环移位读取二进制数值,先看代码如下:

#include <stdio.h>
int main()
{
    int xinghao[6] = {0x060, 0x060, 0xfff, 0xfff, 0x090, 0x090};
    int i,j;
    for ( i = 0; i <= 5; i++)
    {
        for ( j = 0; j <= 11; j++)
        {
            if (((xinghao[i] >> j) & 0x001) == 0x001)
            //先将数组中的星号位置编码读出,再将编码向右依次移位,
            //从而使对应某列的格子需要的0或1,正好对应移位后编码的最右边一位的数值
            //再通过位与运算,移位后编码与0x001按位与。因为 0&任何为0,1&任何不变
            //因此移位后编码与最后一位和1与运算不变,其余为和0与运算变成0
            //最后再看看是否等于0x001,如果是则表示对应的格子应该填 *
                printf("*");
            else
                printf(" ");
        }
        printf("\n");
    }
    return 0;
}

        难点在于 移位运算 和 位与运算,需要着重理解一下。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值