语句基础
-
语句的常见类别
表达式语句:表达式后加分号,对表达式求值后丢弃,可能产生副作用
空语句:仅包含一个分号的语句,可能与循环一起工作
-
复合语句(语句体):由大括号组成,无需在结尾加分号,形成独立的域(语句域)
——>更好的控制对象的生命周期
int main() { // 注意下面实际上是两条语句:复合语句和空语句 {cc // ... }; }
-
顺序语句与非顺序语句
- 顺序语句
- 从语义上安装先后顺序执行
- 实际的执行顺序可能产生变化(编译器优化、硬件乱序执行)
- 与硬件流水线紧密结合,执行效率较高
- 非顺序执行
- 在执行过程中引入跳转,从而产生复杂的变化
- 分支预测错误可能导致执行性能降低
- 顺序语句
-
最基本的非顺序语句:
goto
#include <iostream> int main() { int x = 3; if (x) goto label; x = x + 1; label: return 0; }
- 通过标签指定跳转到的位置
- 具有若干限制
- 不能跨函数跳转
- 向前跳转时不能越过对象初始化语句
- 向后跳转可能会导致对象销毁与重新初始化
-
goto
本质上对应了汇编语言中的跳转指令- 缺乏结构性的含义
- 容易造成逻辑混乱
- 除特殊情况外,应避免使用
分支语句
if
使用语句块表示复杂的分支逻辑
-
从
if
到if-else
- 实现多重分支
-
else
会与最近的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
- 注意:在
while
的条件部分不包含额外的初始化内容
do-while
- 语法:https://zh.cppreference.com/w/cpp/language/do
- 注意结尾处要有分号,表示一条语句的结束
- 处理逻辑:
- 执行循环体
- 断条件是否满足,如果不满足则跳出循环
- 如果条件满足则转向步骤1
- 注:
do-while
中条件处不支持带花括号或等号初始化器的单个变量的声明。
// 错误的语句
do
{
// ...
} while (int x = 0);
for
-
处理逻辑:
- 初始化语句会被首先执行
- 条件部分会被执行,执行结果如果为
false
,则终止循环 - 否则执行循环体
- 迭代表达式会被求值,之后转向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
、范围for
、while
或do-while
循环或switch
语句终止 -
continue
:用于跳过整个for
、while
或do-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