第六章语句

语句基础

  • 语句的常见类别

    • 表达式语句:表达式后加分号,对表达式求值后丢弃,可能产生副作用

    • 空语句:仅包含一个分号的语句,可能与循环一起工作

    • 复合语句(语句体):由大括号组成,无需在结尾加分号,形成独立的域(语句域)

      ——>更好的控制对象的生命周期

    int main()
    {
        // 注意下面实际上是两条语句:复合语句和空语句
        {cc  
            // ...
        };
    }
    
  • 顺序语句与非顺序语句

    • 顺序语句
      • 从语义上安装先后顺序执行
      • 实际的执行顺序可能产生变化(编译器优化、硬件乱序执行)
      • 与硬件流水线紧密结合,执行效率较高
    • 非顺序执行
      • 在执行过程中引入跳转,从而产生复杂的变化
      • 分支预测错误可能导致执行性能降低
  • 最基本的非顺序语句:goto

    #include <iostream>
    int main()
    {
        int x = 3;
        if (x) goto label;
        x = x + 1;
    label:
        return 0;
    }
    
    • 通过标签指定跳转到的位置
    • 具有若干限制
      • 不能跨函数跳转
      • 向前跳转时不能越过对象初始化语句
    • 向后跳转可能会导致对象销毁与重新初始化
  • goto本质上对应了汇编语言中的跳转指令

    • 缺乏结构性的含义
    • 容易造成逻辑混乱
    • 除特殊情况外,应避免使用

分支语句

if

// 一个不合原意的应用
// grade > 80 --> Excellent
// grade <= 60 --> Bad

int grade = 65;
if (grade > 60)
    if (grade > 80)
        std::cout << "Excellent\n";
else
    std::cout << "Bad\n";
  • if V.S. constexpr if —— 运行期与编译器分支
constexpr int grade = 80;
if constexpr (grade < 60)
{
    //...
}
  • 带初始化的if
int x = 3;
// y的作用域为if-else语句范围内
if (int y = x * 3; y > 100)
{
    std::cout << y << '\n';
}
else
{
    std::cout << -y << '\n';
}

switch

  • 语法:https://zh.cppreference.com/w/cpp/language/switch
  • 条件部分应当能够隐式转换为整型或枚举类型,可以包含初始化的语句
  • case/default标签
    • case后面跟常量表达式(即在编译期可以求值的式子),用于匹配switch中的条件,匹配时执行后续的代码
    • 可以使用break跳出当前的switch执行
    • default用于定义缺省情况下的逻辑
    • case/default中定义对象要加大括号
int x;
switch (std::cin >> x; x)
{
    case 3:
        std::cout << "Hello\n";  // fall through
    case 4:
        std::cout << "World\n";
}
  • [[fallthrough]]属性
int x;
switch (std::cin >> x; x)
{
    case 3:
        std::cout << "Hello\n";
        [[fallthrough]];
    case 4:
        std::cout << "World\n";
}
  • if相比的优劣
    • 分支描述能力较弱
    • 在一些情况下能引入更好的优化

循环语句

while

  • 语法:https://zh.cppreference.com/w/cpp/language/while
  • 处理逻辑:
    1. 判断条件是否满足,如果不满足则跳出循环
    2. 如果条件满足则执行循环体
    3. 执行完循环体后转向步骤1
  • 注意:在while的条件部分不包含额外的初始化内容

do-while

  • 语法:https://zh.cppreference.com/w/cpp/language/do
    • 注意结尾处要有分号,表示一条语句的结束
  • 处理逻辑:
    1. 执行循环体
    2. 断条件是否满足,如果不满足则跳出循环
    3. 如果条件满足则转向步骤1
  • 注:do-while中条件处不支持带花括号或等号初始化器的单个变量的声明。
// 错误的语句
do
{
    // ...
} while (int x = 0);

for

  • 语法:https://zh.cppreference.com/w/cpp/language/for

  • 处理逻辑:

    1. 初始化语句会被首先执行
    2. 条件部分会被执行,执行结果如果为false,则终止循环
    3. 否则执行循环体
    4. 迭代表达式会被求值,之后转向2
  • 在初始化语句中声明多个名字

for (int i = 0, *p = &i; i < 9; i += 2)
{
    std::cout << i << ' : ' << *p << ' ';
}
std::cout << '\n';
  • 初始化语句、条件、迭代表达式可以为空
    • 条件若为空语句则系统默认为TRUE
  • for的更多示例

基于范围的for循环

  • 语法:https://zh.cppreference.com/w/cpp/language/range-for
  • 本质:语法糖,编译器会转换为for循环的调用方式
  • 转换形式的衍化:C++11/C++17/C++20
  • 使用常量左值引用读元素;使用“万能引用(universal reference)-> auto &&”修改元素
{
    // C++17标准
    auto && __range = 范围表达式;
    for (auto __begin = 首表达式, __end = 尾表达式; __begin != __end; ++__begin)
    {
        范围声明 = *__begin;
        // 循环语句...
    }
}
std::vector<std::string> arr{"h", "e", "l"};
for (const std::string & v : arr)
    std::cout << v << '\n';

break/continue

  • 含义(转自cpp reference)
    • break:导致外围的for、范围forwhiledo-while循环或switch语句终止
    • continue:用于跳过整个forwhiledo-while循环体的剩余部分
  • 注意这二者均不能用于多重嵌套循环,多重嵌套循环的跳转可以考虑goto语句

语句的综合应用——达夫设备

  • 使用循环展开提升系统性能
  • 处理无法整除的情形
    • 额外增加一个循环语句 ——> 循环展开
    • 将switch与循环结合——达夫设备
#include <iostream>
#include <vector>

int main (void)
{
    constexpr size_t buffer_count = 10000;
    std::vector<size_t> buffer(buffer_count);
    for (size_t i = 0; i < buffer_count; ++i)
    {
        buffer[i] = i;
    }

    size_t max_value = buffer[0];
    auto ptr = buffer.begin();
    switch (buffer_count % 8)
    {
        case 0 : max_value = (max_value > *ptr) ? max_value : *ptr; ++ptr; [[fallthrough]];
        case 7 : max_value = (max_value > *ptr) ? max_value : *ptr; ++ptr; [[fallthrough]];
        case 6 : max_value = (max_value > *ptr) ? max_value : *ptr; ++ptr; [[fallthrough]];
        case 5 : max_value = (max_value > *ptr) ? max_value : *ptr; ++ptr; [[fallthrough]];
        case 4 : max_value = (max_value > *ptr) ? max_value : *ptr; ++ptr; [[fallthrough]];
        case 3 : max_value = (max_value > *ptr) ? max_value : *ptr; ++ptr; [[fallthrough]];
        case 2 : max_value = (max_value > *ptr) ? max_value : *ptr; ++ptr; [[fallthrough]];
        case 1 : max_value = (max_value > *ptr) ? max_value : *ptr; ++ptr;
    };

    for (size_t i = 0; i < (buffer_count - 1) / 8; ++i)
    {
        max_value = (max_value > *ptr) ? max_value : *ptr; ++ptr;
        max_value = (max_value > *ptr) ? max_value : *ptr; ++ptr;
        max_value = (max_value > *ptr) ? max_value : *ptr; ++ptr;
        max_value = (max_value > *ptr) ? max_value : *ptr; ++ptr;
        max_value = (max_value > *ptr) ? max_value : *ptr; ++ptr;
        max_value = (max_value > *ptr) ? max_value : *ptr; ++ptr;
        max_value = (max_value > *ptr) ? max_value : *ptr; ++ptr;
        max_value = (max_value > *ptr) ? max_value : *ptr; ++ptr;
    }

    for (size_t i = buffer_count / 8 * 8; i < buffer_count; ++i)
    {
        max_value = (max_value > buffer[i])       ? max_value : buffer[i]      ;
    }
    std::cout << max_value << '\n';
}
#include <iostream>
#include <vector>

int main (void)
{
    constexpr size_t buffer_count = 10000;
    std::vector<size_t> buffer(buffer_count);
    for (size_t i = 0; i < buffer_count; ++i)
    {
        buffer[i] = i;
    }

    size_t max_value = buffer[0];
    auto ptr = buffer.begin();

    size_t i = 0;
    switch (buffer_count % 8) {
        for (; i < (buffer_count + 7) / 8; ++i) {
            case 0 :
                max_value = (max_value > *ptr) ? max_value : *ptr;
            ++ptr; [[fallthrough]];
            case 7 :
                max_value = (max_value > *ptr) ? max_value : *ptr;
            ++ptr; [[fallthrough]];
            case 6 :
                max_value = (max_value > *ptr) ? max_value : *ptr;
            ++ptr; [[fallthrough]];
            case 5 :
                max_value = (max_value > *ptr) ? max_value : *ptr;
            ++ptr; [[fallthrough]];
            case 4 :
                max_value = (max_value > *ptr) ? max_value : *ptr;
            ++ptr; [[fallthrough]];
            case 3 :
                max_value = (max_value > *ptr) ? max_value : *ptr;
            ++ptr; [[fallthrough]];
            case 2 :
                max_value = (max_value > *ptr) ? max_value : *ptr;
            ++ptr; [[fallthrough]];
            case 1 :
                max_value = (max_value > *ptr) ? max_value : *ptr;
            ++ptr;
        }
    }

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

推荐阅读更多精彩内容