第五章:循环和关系表达式 | C++ Primer Plus 重点带看

顺序点

顺序点(sequence point,C++11 起改称顺序规则)是程序执行过程中的一个"检查点"。在顺序点处,之前所有副作用(赋值、递增、递减)必须全部完成,之后才执行下一步。

y = (4 + ++x) - (6 - ++x);   // ❌ 未定义行为!
// C++ 不保证 x 的值在计算子表达式 4 + x++ 后立刻增加 1。
// 因此,若 x 执行这条语句前为 1,
// 可能实际结果是 y = (4 + 2) - (6 - 2) = 2
// 也可能是 y = (4 + 2) - (6 - 3) = 3

// 所以,一条语句中不要对同一变量产生多个副作用。
  • C++ 中的顺序点:

顺序点

说明

; 分号

一条完整语句结束时

, 逗号运算符

逗号左侧表达式求值完成后

&&、||、?:

第一个操作数求值完成后(短路求值)

函数调用前

所有参数求值完成后、进入函数体之前

return 语句

return 表达式求值完成后

完整表达式结尾

if / while 的条件表达式、for的三个表达式之间

x++ (后置递增) 和 ++x (前置递增)

int x = 5;

int a = ++x;    // 先加 1,x 变成 6,然后返回 6 → a = 6, x = 6
int b = x++;    // 先返回 5,然后 x 加 1 → b = 5, x = 7

++x(前置):
  ① x = x + 1
  ② 返回 x
  → 没有临时变量,直接操作

x++(后置):
  ① temp = x        ← 临时变量保存旧值
  ② x = x + 1
  ③ 返回 temp
  → 多了一个临时变量

编译器能不能优化:
  int 等内置类型 → 能优化掉,最终代码一样
  结构体/类类型 → 不一定能优化,因为拷贝有成本

for 循环和 while 循环的区别

for 中的条件可以为空,空认为条件一直位真;while 不能省略条件。

C++ 创建类型别名的三种方式以及区别

1、#define new old:纯粹的文本替换,编译前由预处理器处理,不安全,不遵循作用域,尽量避免。

#define INT_PTR int*  // 不受函数作用域限制,宏定义从这行开始全局生效
INT_PTR a, b;    // 展开为 int* a, b → a 是 int*,b 是 int ⚠️。PTR a, b 被展开成 int* a, b,* 只修饰 a

#define PI 3.14
int x = PI;      // ✅ 编译通过,3.14 隐式截断为 3,但没有警告

2、typedef old new:安全,但不能定义模板别名。

typedef int* INT_PTR;
INT_PTR a, b;    // a 和 b 都是 int* ✅

3、using new = old:安全,支持模板别名,现代 C++ 首选

using INT_PTR = int*;
INT_PTR a, b;    // a 和 b 都是 int* ✅

三种方式都不能创建新类型,只能给已有类型创建别名,另外在声明指针变量的时候只能用 typedef,using。

using 是最好的方式(c++11 之后的方式),而且 using 可以用来定义模板别名,typedef 不行(只能定义具体化的模板别名)。

template<typename T>
using MyVector = std::vector<T>;
typedef std::vector<int> IntVector;

特性

#define

typedef

using

本质

预处理器文本替换

C++ 类型别名

C++ 类型别名

类型安全

❌ 不检查

✅ 检查

✅ 检查

作用域

❌ 不遵循

✅ 遵循

✅ 遵循

调试友好

❌ 展开后丢失

✅ 保留别名信息

✅ 保留别名信息

指针声明

⚠️ * 只修饰第一个变量

✅ 所有变量都是指针

✅ 所有变量都是指针

模板别名

❌(需包装)

多个别名同时声明

❌(每行一个)

C++ 版本

C

C/C++

C++11

基于范围的 for 循环

基于范围的 for 循环(也叫 range-for)是 C++11 引入的语法糖,用于简洁地遍历容器或数组的所有元素,无需手动管理迭代器或下标。

基本语法:for (声明 : 可遍历对象) { }

三种声明:
  auto x         → 拷贝,改不了原元素
  auto& x        → 引用,能改原元素
  const auto& x  → const 引用,只读不拷贝(最推荐)

适用对象:数组、STL 容器、string、初始化列表、自定义类型(有 begin/end)

注意事项:
  1、循环中不能增删容器元素。(range-for 底层依赖 begin() 和 end(),end() 在循环开始时就计算好了,修改容器会导致迭代器失效。)
  2、数组退化为指针后不能用。
    void print(int arr[], int size) {
       for (auto x : arr) { }    // ❌ 编译报错,arr 是指针,不知道长度
    }

    int arr[] = {1, 2, 3};
    for (auto x : arr) { }        // ✅ 编译器知道数组大小
  3、std::vector<bool> 按引用遍历有问题。(std::vector<bool> 是一个特殊实现,用位压缩存储,不满足标准容器的引用语义,是 C++ 的一个历史遗留问题。)
C++ Primer Plus 文章被收录于专栏

C++ Primer Plus 精读|从入门到面试,重点内容全程带看。 本专栏以《C++ Primer Plus》为蓝本,逐章提炼必考知识点、易错点、面试高频考点,跳过冗余示例,直击语法本质与工程实践,帮你高效吃透 C++ 基础,夯实底层开发必备能力。

全部评论
x初值是1吗
点赞 回复 分享
发布于 04-01 23:11 河北
欢迎订阅专栏《C++/嵌入式开发 秋招面经》:https://www.nowcoder.com/creation/manager/columnDetail/MKaoll
点赞 回复 分享
发布于 03-31 01:20 河北

相关推荐

鱼专:你没有问题,有问题的是java市场,我有实习经历都捞不到实习,走一步看一步吧
点赞 评论 收藏
分享
评论
5
2
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务