0基础学习C++做贪吃蛇,边玩边学习!(五)for循环和作用域——可长可短的蛇

点击链接回顾前几篇:
(一)标准输出cout——一条安静的蛇
(二)代码详解和Sleep()——蛇之闪现
(三)SetConsoleCursorPosition光标移动效果——一条前进的蛇
(四)预定义和函数调用——妄图得分的蛇
在前几篇中,我们学习了如何用cout()输出字符串到屏幕,如何用Sleep()实现闪现,如何用SetConsoleCursorPosition()让蛇前进,以及关于预处理命令#define函数的相关知识。

接下来,我们将学习在循环中快捷地打印蛇身图案。

    for (int i = 0;i < 5;i++) {
        SetConsoleCursorPosition(handle, { i,i });
        cout << BodySymbol;
    }

for是C++关键字,用来定义一个循环

可以看到,for的()中有三个表达式,用分号隔开,我们称之为表达式一,表达式二,表达式三。大括号{}内的部分叫循环体

如图:

for (语句一;语句二;语句三){
    循环体
}

我们知道,程序一般是顺序执行的,那么for循环是如何执行的呢?

Created with Raphaël 2.3.0 开始 语句一 语句二 循环体 语句三 结束 yes no
可以看到,当程序运行到for循环时,先执行语句一(预处理语句),然后判断语句二是否成立(非0即为成立),若成立,进入循环,循环体执行一遍过后执行语句三(通常是控制条件语句),然后再判断语句二是否成立,若成立者继续循环,若不成立则结束循环。

也就是说,每次循环在判断语句二为成立后开始,在语句三执行完毕后结束。
程序按照语句二——循环体——语句三的顺序多次执行直到语句二不成立,循环结束。而语句一仅执行一次
现在我们可以直观地了解上面那个for循环的意义:

Created with Raphaël 2.3.0 开始 定义i=0 i<5吗? 在{i,i}处打印 i增加1 结束 yes no

注:i++相当于i=i+1

可以想到:当i依次是0,1,2,3,4时,在屏幕上{0,0} {1,1} {2,2} {3,3} {4,4},五个点处依次打印出了BodySymbol,蛇的身体。
在主函数中写入这个for循环:

int main() {
    handle = GetStdHandle(STD_OUTPUT_HANDLE);
    for (int i = 0;i < 5;i++) {
        SetConsoleCursorPosition(handle, { i,i });
        cout << BodySymbol;
    }
    getch();
    return 0;
}

编译运行:
在这里插入图片描述

注:getch()函数等待一个字符输入(敲键盘),可以让程序暂停下来,需要#include<conio.h>

接下来:改LENGTH 的值为5

#define LENGTH 5

将for循环移入ready()函数,并做一些修改

void ready() {
    for (int i = 0;i < LENGTH;i++) {
        SetConsoleCursorPosition(handle, { i,10});
        cout << BodySymbol;
    }
    cout << HeadSymbol;//最后打印一个蛇头
}

我们将y坐标改成“恒为10”,使得输出将在从左到右的一行(第十行)

注:之所以不设为0是为了体现SetConsoleCursorPosition()的作用,因为光标本身就在第0行(Y=0),从左向右移动(x++)

将5改为LENGTH,则循环LENGTH次(即按照长度打印蛇)

注:Windows坐标系方向为:从左上角水平向右为X轴正方向。从左上角竖直向下为Y轴正方向。

此时输出:
在这里插入图片描述

如果我们将LENGTH改成30,则输出:
在这里插入图片描述

30个蛇身,一个蛇头。
此时程序全部代码为:

#include <iostream>
#include<conio.h>
#include <windows.h>
//初始长度
#define LENGTH 30
//输出符号
#define NoSymbol ' '
#define BodySymbol 'o'
#define HeadSymbol 'O'
using namespace std;
//窗口句柄
HANDLE handle;
//蛇体坐标数组
void ready() {
    for (int i = 0;i < LENGTH;i++) {
        SetConsoleCursorPosition(handle, { i,10});
        cout << BodySymbol;
    }
    cout << HeadSymbol;//最后打印一个蛇头
}
int main() {
    ready();
    getch();
    return 0;
}

不知道大家有没有发现,自从ready()出现以来,HANDLE handle这句语句就移到了主函数外面,这是为什么呢?
因为每个变量都有自己的作用域,只能在作用域范围内使用。
当程序执行到变量定义语句时,就在内存为此变量分配储存空间(变量被定义),当程序离开子程序(代码块,函数等,一个子程序的特征是用大括号{}括起来。)时,所有在这个子程序中被定义的变量被释放,如果下一次执行此子程序,所有变量都会被重新定义。

所以变量的生命是从定义语句开始,到它所在的大括号结束处(右括号)结束。

因此:
变量 i 的作用域是for循环内部
变量 handle 的作用域则是全局
所谓的全局变量就是没有定义在大括号内的变量,他在全局一直存在,直到程序结束。

如果handle还在main()内部,我们就无法在ready()中使用他,可以试试将handle的定义移回main(),编译不会通过。

“handle” was not declared in this scope.
“handle” 未被定义在此作用域。

其实我们还可以使用参数传递的方法:
将main()中ready()那一行改为:

    ready(handle);

这个语句就是我们上一篇说的函数调用,其中handle是实际参数,简称实参。(具有值)
将ready()的函数头改为:

void ready(HANDLE handle) //函数头就是函数类型、名称、参数表所在那一行

其中handle是形式参数,又叫形参(没有值,接受实参的值)

实参形参傻傻分不清?
要知道main()中的handle和ready()中的handle是两个变量(变量在作用域内不许同名,在作用域外则无妨
可以将ready()中的handle改为h,这个时候,handle是实参,h是形参,参数传递的实质就是:
h = handle;

即将实参的值赋给形参。

关于形参和实参的联系是:
函数调用—赋值前: 实参有值,形参无值。
函数调用——赋值时: 实参和形参值相等。
函数调用时: 形参可以发生改变,不对实参形成影响。
函数返回后: 形参被释放,无碍于实参。

还记得上一篇提到的数组和COORD类型吗?我们将在下一篇具体讲解数组,实现真正的移动!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值