美文网首页C/C++程序员
C++入门系列博客二 C++ 控制流

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

作者: AceTan | 来源:发表于2016-06-29 00:01 被阅读511次

    C++ 控制流


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


    控制流

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

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


    while语句

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

    while (condition)
        statement
    

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

    #include <iostream>
    
    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 <iostream>
    
    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 <iostream>
    
    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 <iostream>
    
    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 <iostream>
    #include <vector>
    
    using namespace std;
    
    int main()
    {
        vector<int> v = { 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 };
    
        // 范围for语句。如果想要改写范围变量,则需要把它写成引用形式。
        for (auto& val : v)
        {
            val = val * val;
        }
    
        // 普通的for语句
        for (vector<int>::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 <iostream>
    
    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 <iostream>
    
    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++ 控制流

        本文链接:https://www.haomeiwen.com/subject/tjotjttx.html