C++入门系列博客二 C++ 控制流

C++ 控制流


作者:AceTan,转载请标明出处!


控制流

为什么会有控制流这玩意呢?那是因为程序不总是按顺序执行。这个很好理解,生活中也随处可见。例如:如果今天不下雨,那我就去打篮球,否则,宅在宿舍打LOL。这个就可以对应if else这种控制流结构了。控制流在大多数的语言中都是有的,而且也基本上都是 while for if这三剑客或者变种。

程序设计语言一般都会提供多种不同的控制流语句,允许我们执行更为复杂的执行路径。


while语句

while 语句 反复执行一段代码,直至给定的条件为假(false)为止。while语句的形式为:

while (condition)
    statement

让我们用while语句来完成一个小任务:求整数1到100的和。 知道大数学家高斯的人,想必都知道这个经典问题的出处。直接上代码:

#include 

using namespace std;

int main()
{
    int sum = 0;          // sum用于存储和
    int x = 1;            // 当前要加的值
    while (x <= 100)    // 判断当前值是否小于等于100
    {
        sum = sum + x;    // 和加上当前要加的值
        x = x + 1;        // 当前值加1,往后递推。
    }

    cout << "整数1到100的和为" << sum << endl;

    return 0;
}

注释比较详尽,就不一一解释了。使用while语句特别要注意的是条件以及条件的改变,不然容易造成死循环,使程序崩溃。例如上面的程序,粗心的人忘了写 x = x + 1 这一句,那么程序就会陷入死循环。

最经典的Windows消息机制也是用的while语句,像这样:

// 消息循环过程
MSG msg = {0};
while (msg.message != WM_QUIT)        // 如果没有退出消息
{
    if(PeekMessage(&msg, hwnd, 0, 0, PM_REMOVE))
    {
        TranslateMessage(&msg);        // 将虚拟按键装化为字符
        DispatchMessage(&msg);        // 分发消息给程序窗口
    }
}

还有一些输入不确定数量的数,也可以使用while语句解决。

问题:输入若干个整数,求其和。

#include 

using namespace std;

int main()
{
    int sum = 0;        // sum用于存储和
    int x = 0;            // 当前要输入的值
    int num = 0;        // 记录一共输入了几个数
    while (cin >> x)    // 读取数,一直遇到结束符为止
    {
        num = num + 1;
        sum = sum + x;    // 和加上当前要加的值
    }

    cout << "你一个输入了" << num << "个数," << "这些数的和为:" << sum << endl;

    return 0;
}

在Windows系统下,结束输入的组合键是 Ctrl + Z,然后回车。 Linux系统下是 Ctrl + D。 Max OS系统下是 Ctrl + D

另外,还有个和while有关的是do while结构的语句。这种结构会至少执行一次循环体,然后检查while中的条件是否成立。看示例:

#include 

using namespace std;

int main()
{
    int count = 0;        // 记录一共执行了几次循环体
    
    do
    {
        count = count + 1;
        // 一段你想执行的代码
    } while (false);
    
    cout << "语句块执行的次数为:" << count << endl;

    return 0;
}

输出结果为1,也就是说,这种结构至少执行一次 do 语句块中的代码。


for 语句

像while语句这样在循环条件中检测变量,在循环体中改变变量的模式非常频繁。于是一个专门的语句出现了,它就是 for 语句。 for语句的形式为:

for(init-statement; condition; expression)
    statement

每个for语句都包含两部分:循环头和循环体。循环头控制循环体的执行次数,它由三个部分组成:一个初始化语句(init-statement),一个循环条件(condition)以及一个表达式(expression)

for循环和while循环是可以相互转换的。
看之前的一个小任务:求整数1到100的和。 使用for语句完成如下:

#include 

using namespace std;

int main()
{
    int sum = 0;        // sum用于存储和
    
    for (int x = 1; x <= 100; ++x)
    {
        sum += x;        // 和加上当前要加的值
    }

    cout << "整数1到100的和为" << sum << endl;

    return 0;
}

简述一下for循环的总体执行流程

  1. 创建变量x,将其初始化为1
  2. 检测变量x是否小于等于100,若检测成功,执行for循环的循环体。若失败,退出循环。继续执行循环体之后的代码。
  3. 将x的值增加1
  4. 重复第2步中的检测条件,只要条件为真,就继续执行剩余步骤。

需要注意的是,for语句的初始化语句、循环条件、表达式都是可以省略的。例如for (; ;){},相当于while(true){}。 所以说,for语句使用不当,也会造成死循环。

范围for语句

C++ 11新标准引入的一种更简单的for语句。这种语句可以遍历容器或者其他序列的所有元素。

范围for语句(range for statement) 的语法形式如下:

for (declaration : expression)
    statement

declaration 定义了一个变量,序列中的每个元素都是能转换成该变量的类型。

expression 必须是一个序列。 例如可以是 数组、vector或者string等对象。

看代码:

#include 
#include 

using namespace std;

int main()
{
    vector v = { 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 };

    // 范围for语句。如果想要改写范围变量,则需要把它写成引用形式。
    for (auto& val : v)
    {
        val = val * val;
    }

    // 普通的for语句
    for (vector::iterator iter = v.begin(); iter != v.end(); ++iter)
    {
        cout << *iter << " ";
    }

    return 0;
}

输出结果为:81 64 49 36 25 16 9 4 1 0

可见容器内的值已经被改变。


if 语句

if语句也就是条件语句。大部分语言都支持了if语句。if语句的形式为:

if (condition)
    statement

另一种if语句带else分支,形式为:

if (condition)
    statement
else
    statement2

如果condition为真,则执行statement语句,否则执行statement2语句。

另外还有就是嵌套的if语句,像这样:

if (condition)
{
    // other code
    if (condition1)
    {
        // other code
    }
    else
    {
        // other code
    }
}
else
{
    // other code
}

建议大家像我一样,使用方括号和缩进,来控制执行路径,解决垂悬else问题,也使得代码更加可读。

看一个《C++ Prime》上的一个例子:统计输入中的每个值连续出现的次数。

#include 

using namespace std;

int main()
{
    // currentVal 是我们正要统计的数,我们将新的值存入val
    int currentVal = 0, val = 0;
    
    // 读取第一个数,并确保有数据可以处理
    if (cin >> currentVal)
    {
        int count = 1;                // 保存我们正在处理的当前值的个数
        while (cin >> val)
        {
            if (val == currentVal)    // 如果值相同
            {
                ++count;
            }
            else                    // 如果值不同,则打印出现的个数
            {
                cout << currentVal << "出现了" << count << "次" << endl;

                currentVal = val;    // 记住新出现的这个值
                count = 1;            // 重置计数器
            }
        }    // while循环到这里结束

        // 打印文件中最后一个值的个数
        cout << currentVal << "出现了" << count << "次" << endl;
    }        // 最外层if语句结束

    return 0;
}

最基本的流程控制实际就这三个,也是大多数语言所支持的。其他的一些控制流其实都可以用这三个来转换。下面介绍一下switch语句跳转语句,他们也是控制流程语句的一份子。


switch 语句

switch语句(switch statement) 提供了一条便利的途径使得我们在若干个固定选项中做出选择。switch语句形式如下:

switch (switch_on)
case x1:
    statement1
    break;
case x2:
    statement2
    break;
// other case 
default:
    statementn
    break;

case关键字和它对应的值一起被称为 case标签(case label)。 case标签必须是整型常量表达式

下面用一段代码来说明此问题:

问题: 统计一段小写文本中元音字母出现次数,以及非元音字母出现的次数。

代码:

#include 

using namespace std;

int main()
{
    // 五个元音分别统计
    int aCount = 0, eCount = 0, iCount = 0, oCount = 0, uCount = 0;
    // 非元音统计
    int otherCount = 0;

    char ch;
    while (cin >> ch)
    {
        switch (ch)
        {
        case 'a':
            ++aCount;
            break;
        case 'e':
            ++eCount;
            break;
        case 'i':
            ++iCount;
            break;
        case 'o': 
            ++oCount;
            break;
        case 'u':
            ++uCount;
            break;
        default:
            ++otherCount;
            break;
        }
    }

    cout << "元音字母a出现的次数为:" << aCount << endl;
    cout << "元音字母e出现的次数为:" << eCount << endl;
    cout << "元音字母i出现的次数为:" << iCount << endl;
    cout << "元音字母o出现的次数为:" << oCount << endl;
    cout << "元音字母u出现的次数为:" << uCount << endl;

    cout << "非元音字母出现的次数为:" << otherCount << endl;

    return 0;
}

输入:acetanlovesnail

输出:

元音字母a出现的次数为:3

元音字母e出现的次数为:2

元音字母i出现的次数为:1

元音字母o出现的次数为:1

元音字母u出现的次数为:0

非元音字母出现的次数为:8

这里要注意每个分支下的break,它代表结束当前的控制流,break下文会讲到。新手常犯的错误就是漏写break,漏写break最容易导致逻辑错误,漏写break后,程序会继续执行下面的代码。例如:上面的程序

case 'i':
    ++iCount;    // 漏写了break
case 'o': 
    ++oCount;    // 漏写了break
case 'u':
    ++uCount;

如果有一个元音字母i,那么iCount会加1,oCount也会加1,uCount也会加1。这一点千万要注意。

switch语句是可以转化为if语句的,这很容易看出来。


跳转语句

跳转语句是中断当前的执行过程。C++中提供了四种跳转语句:break, continue, goto, return.

break语句(break statement) 负责终止离它最近的while, do while, for 或者switch语句,并从这些语句之后的第一条语句开始执行。

break语句只能出现在迭代语句或者switch语句内部。例子可以参考上面switch的例子。

continue语句(continue statement) 终止离它最近的循环中的当前迭代并立即开始下一次迭代。

continue语句只能出现在for, while, do while循环的内部,或者嵌套在此类循环里的语句或者块的内部。

goto语句(goto statement) 的作用是无条件跳转到同一函数内的另一条语句。

不要在程序中使用goto语句,因为它使得程序既难理解又难修改。

goto语句是一个比较有意思的语句,虽然各种教材都说它很危险,不推荐使用。但它有时候也能起到奇效的作用。

goto语句的形式为:

goto label;

其中label是用于标示某条语句的标示符。 带标签语句(labeled statement) 是一种特殊语句,在它之前有一个标示符以及一个冒号。

end: return; // 带标签语句,可以作为goto的目标

goto语句一个优点就是它可以在很深的嵌套层次中直接跳到最外围,否则你只能break一层一层的跳。其实
Java也保留了这种用法,不过换了语法变成break label;

使用goto语法:

for (...) 
{
    for (...) 
    {
        for (...) 
        {
            for (...) 
            {
                for (...) 
                {
                    for (...) 
                    {
                        if (...) 
                            goto:outside;
                    }
                }
            }
        }
    }
}

outside:

goto只能在函数体内跳转,不能跳到函数体外的函数。即goto有局部作用域,需要在同一个栈内。

return 语句(return statement) 终止当前正在执行的函数并将控制权返回到调用该函数的地方。
return 语句有两种形式:

return;

return expression;

return 函数非常常见,每个函数都要使用它。其中,无返回值函数使用第一种形式。有返回值函数使用第二种形式。 需要注意的是:

在含有return语句的循环后也要有一条return语句,如果没有的话,那么该程序就是错误的。很多编译器都无法发现此类错误。

其实,比较新的编译器都会给出警告的。例如VS2015会给出这样的警告:

warning C4715: “”: 不是所有的控件路径都返回值

在Code::Blocks上会给出这样的警告:

warning: control reaches end of non-void function [-Wreturn-type]|

结束语

关于C++的控制流就介绍到这里,希望各位读者能熟练掌握和应用。

你可能感兴趣的:(C++入门系列博客二 C++ 控制流)