5 语句 | Statement

Basic

语句通常是顺序执行的,而 控制流 flow-of-control 可以支持更复杂的执行路径。

简单语句

表达式语句 expresion statement

一个表达式末尾加上分号就是 表达式语句 。表达式语句的作用是执行表达式并丢弃掉求值结果

空语句 null statement

只有一个分号的语句,用于在语法上需要但逻辑上不需要语句时:

while(...) ;后面这个分号是空语句,代表while的循环体。注意while循环体内除非用花括号,否则以分号作为空语句代表循环体结束。

cout<<"Hello";;;后面两个分号是空语句。

复合语句 compound statement

指花括号括起来的语句,可以为空。又称做 块 block。一个块就是一个作用域。在块中引入的名字只能在块内部及其嵌套的子块内访问。通常名字在有限区域内可见,该区域从名字定义处开始到名字所在最内层块的结尾为止。

在某个地方语法上需要单条语句但逻辑上应使用多条语句时,如while和for的循环体内应使用复合语句。

块不以分号作为结束。没有任何内容的块称为空块,作用同空语句一致。如:while(...){} 没有分号但正确。

语句作用域 statement scope

https://en.cppreference.com/w/cpp/language/scope

条件语句

if语句

if condition
    statement
else if condition //嵌套if,可选
    statement
else              //else语句,可选
    statement

悬垂else (dangling else)

为消除多个if和随后else的二义性,C++规定else和最近的一个未匹配的if匹配。若需要匹配更远处的if。则需要使用花括号。

switch语句

Syntax
attr(optional) switch ( condition ) statement (until C++17)
attr(optional) switch ( init-statement(optional) condition ) statement (since C++17)
  • attr(C++11): any number of attributes
  • condition - any expression of integral or enumeration type, or of a class type contextually implicitly convertible to an integral or enumeration type, or a declaration of a single non-array variable of such type with a brace-or-equals initializer.
  • init-statement(C++17) - either
    • an expression statement (which may be a null statement ";")
    • a simple declaration, typically a declaration of a variable with initializer, but it may declare arbitrarily many variables or structured bindings
    • Note that any init-statement must end with a semicolon ;, which is why it is often described informally as an expression or a declaration followed by a semicolon.
  • statement - any statement (typically a compound statement).
  • case: and default: labels are permitted in statement and break; statement has special meaning.
attr(optional) case constant_expression : statement
attr(optional) default : statement
  • constant_expression - a constant expression of the same type as the type of condition after conversions and integral promotions

语句示例:统计分数段及人数

int main(){
    unsigned cnt=0, cntA=0, cntB=0, cntC=0, cntD=0, cntF=0;
    unsigned score;
    while (cin>>score) {
        char grade = score>=90 ? 'A':
                     score>=80 ? 'B':
                     score>=70 ? 'C':
                     score>=60 ? 'D': 'F';
        switch (grade) {
            case 'A': ++cntA; break;
            case 'B': ++cntB; break;
            case 'C': ++cntC; break;
            case 'D': ++cntD; break;
            case 'F': ++cntF; break;
        }
        ++cnt;
    }
    cout<<"Total number of students: "<<cnt<<'\n'
        <<"Grade A: "<<cntA<<" student\n"
        <<"Grade B: "<<cntB<<" student\n"
        <<"Grade C: "<<cntC<<" student\n"
        <<"Grade D: "<<cntD<<" student\n"
        <<"Grade F: "<<cntF<<" student\n";
}

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

对于case语句,仅在被循环语句包围时可以使用 continue。注意continue不仅会结束当前switch,还会结束当前循环语句,是直接作用于最近的一个循环语句而不是switch 。而break仅直接作用于case,因此加花括号块并在case块内break也不影响跳出switch。

注意:在 switch 语句中定义的 default 并不会强制执行,且定义在该块中的 非case语句unreachable

switch内部的控制流

若某个case标签匹配成功,则除非程序显式中断该过程,否则会一直到switch结尾才会停下来。即:可跳转入,不跳转出,除非中断。

default语句

用于接收未被case语句匹配的情况。可出现在switch任意位置,但出现在最后且没有case语句接着时,需要在后面添加空语句或空块。

switch和作用域

在switch中case外定义语句会使其unreachable;在case中定义语句有可能因为跳过而无法初始化,所以不能在case中定义变量的显式/隐式初始化(如空string、空vector)。

可以在case中或switch内case外声明,但声明必须写在使用之前,且声明语句所在的case可以unreachable。

int main(){
    bool i = 0;
    switch (i) {
        int c;        //unreachable,但有效
        case true:
            c = 1;
            int d;    //有效
            break;
        case false:
            c = 0; d = 0;
            cout<<c<<d;  //有效,但是未初始化的值
    }
}

如果在某处一个带初值的变量位于作用域外,而在另一处该变量在作用域内,则从前一位置跳到后一位置是非法行为。

要在单个case内定义变量,则只能初始化在该变量所在块内以确保其作用域不在其他的case语句中。

迭代语句

while语句

前置判断条件:普通while语句

attr(optional, since C++11) while ( condition ) statement

后置判断条件:do while语句

attr(optional, since C++11) do statement while ( expression ) ;

attr: https://en.cppreference.com/w/cpp/language/attributes

for语句

formal syntax:
attr(optional) for ( init-statement condition(optional) ; iteration_expression(optional) ) statement
informal syntax:
attr(optional) for ( declaration-or-expression(optional) ; declaration-or-expression(optional) ; expression(optional) ) statement
#include <iostream>
#include <vector>
 
int main()
{
    // typical loop with a single statement as the body
    for (int i = 0; i < 10; ++i)
        std::cout << i << ' ';
    std::cout << '\n';
 
    // init-statement can declare multiple names, as long as they
    // can use the same decl-specifier-seq
    for (int i = 0, *p = &i; i < 9; i += 2) {
        std::cout << i << ':' << *p << ' ';
    }
    std::cout << '\n';
 
    // condition may be a declaration
    char cstr[] = "Hello";
    for (int n = 0; char c = cstr[n]; ++n)
        std::cout << c;
    std::cout << '\n';
 
    // init-statement can use the auto type specifier
    std::vector<int> v = {3, 1, 4, 1, 5, 9};
    for (auto iter = v.begin(); iter != v.end(); ++iter) {
        std::cout << *iter << ' ';
    }
    std::cout << '\n';
 
   // init-statement can be an expression
    int n = 0;
    for (std::cout << "Loop start\n";
         std::cout << "Loop test\n";
         std::cout << "Iteration " << ++n << '\n')
        if(n > 1)
            break;
    std::cout << '\n';
}

output:

0 1 2 3 4 5 6 7 8 9 
0:0 2:2 4:4 6:6 8:8 
Hello
3 1 4 1 5 9 
Loop start
Loop test
Iteration 1
Loop test
Iteration 2
Loop test

range-based for(C++11)

Syntax
attr(optional) for ( init-statement(optional, since C++20) range_declaration : range_expression ) loop_statement

可以通过指针、引用、转发引用、const引用、值来进行access。

跳转语句

break

  • 负责终止最近的whiledo whileforswitch
  • 直接终止而不进行下一次迭代。

continue

  • 负责终止最近的whiledo whilefor
  • 只能出现在上述循环体内。对不在循环体内的switch无效
  • 仅终止当前的迭代,然后进行下一次。

goto

  • 从goto语句无条件跳转到同一函数内另一条语句。使用时需要用带标签语句labeled statement给要跳转的语句命名。
  • 尽量不在程序中使用。

try语句块和异常处理

C++中的异常处理包括:

  • throw expression - 异常检测部分使用throw表达式表示遇到了无法处理的问题。即:throw raised the expression(throw引发了异常)
  • try block - 异常处理部分使用try语句块处理异常。该语句块以关键字try开始并以一个或多个catch子句 - catch clause 结束。try语句块中代码抛出的异常通常会被某个子句处理,因此被称为异常处理代码(exception handler)
  • exception class 异常类,用于在throw表达式和相关的catch子句之间传递异常的具体信息。

throw 表达式

异常检测部分使用throw表达式引发一个异常。throw表达式包含关键字throw和紧随其后的一个表达式,表达式类型就是抛出的异常类型

throw表达式通常紧跟一个分号。

if(!condition_satisfied)
    throw runtime_error("Error text here");

try 语句块

try语句块的通用语法形式:

try {
    program-statements
} catch (exception-declaration) {
    handler-statements
} catch (exception-declaration) {
    handler-statements
} //.......

https://en.cppreference.com/w/cpp/language/try_catch

string data;
while (cin>>data){
    try{

    } catch (runtime_error err) {   //实例化error类型的err
        cout<<err.what()
            <<"\nTry again? (Y/N)"<<endl;
        char c;
        cin>>c;
        switch(c) {
            default:
            case 'N': continue;
            case 'Y': ;
        }
    }
}
#include <exception>
#include <iostream>

int throwError(int arg){
    switch (arg){
        case 1:
            throw std::runtime_error("Runtime error occurred!");
            break;
        case 2:
            throw std::exception("Exception occurred!");
            break;
    }
    return 0;
}

int main(){
    int a = 1;
    try{
        throwError(a);
    } catch (std::runtime_error err){
        std::cout<<err.what()<<std::endl;
    } catch (std::exception exc){
        std::cout<<exc.what()<<std::endl;
    }
}

C++标准异常

定义在<exception>中,需要以std::访问的异常类如下:
https://en.cppreference.com/w/cpp/error/exception

异常类 描述
exception 常见问题
runtime_error 运行时才能检测出的错误
range_error 运行时错误:结果超范围
overflow_error 运行时错误:计算上溢
underflow_error 运行时错误:计算下溢
logic_error 程序逻辑错误
domain_error 逻辑错误:参数对应结果值不存在
invalid_error 逻辑错误:无效参数
length_error 逻辑错误:视图创建超出类型最大长度的对象
out_of_range 逻辑错误:使用一个超出有效范围的值

上述类的对象只能默认初始化而不可以通过任何形式赋初值。它们只有一个成员函数 what() ,返回一个 const char* 提供关于该异常的文本信息。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,658评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,482评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,213评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,395评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,487评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,523评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,525评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,300评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,753评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,048评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,223评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,905评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,541评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,168评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,417评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,094评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,088评论 2 352

推荐阅读更多精彩内容