惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
B
Blog
Jina AI
Jina AI
N
Netflix TechBlog - Medium
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
博客园_首页
Hugging Face - Blog
Hugging Face - Blog
博客园 - 聂微东
美团技术团队
Google DeepMind News
Google DeepMind News
WordPress大学
WordPress大学
阮一峰的网络日志
阮一峰的网络日志
U
Unit 42
The Cloudflare Blog
V
V2EX
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
小众软件
小众软件
罗磊的独立博客
Microsoft Security Blog
Microsoft Security Blog
Apple Machine Learning Research
Apple Machine Learning Research
I
InfoQ
GbyAI
GbyAI
腾讯CDC
MongoDB | Blog
MongoDB | Blog

Shiroha白羽的博客

Golang 踩坑 —— interface 为参数的时候传 nil 指针 Codeforces Round 925 (Div. 3) Codeforces Round 924 (Div. 2) Codeforces Round 923 (Div. 3) Codeforces Round 922 (Div. 2) Codeforces Round 921 (Div. 2) Educational Codeforces Round 161 (Rated for Div. 2) Codeforces Round 920 (Div. 3) Codeforces Round 919 (Div. 2) Hello 2024 Good Bye 2023 Codeforces Round 918 (Div. 4) 个人备份的常用 macOS 清理命令 Codeforces Round 917 (Div. 2) Pinely Round 3 (Div. 1 + Div. 2) Educational Codeforces Round 160 (Rated for Div. 2) Codeforces Round 915 (Div. 2) Codeforces Round 914 (Div. 2) Codeforces Round 913 (Div. 3) Educational Codeforces Round 159 (Rated for Div. 2) Codeforces Round 912 (Div. 2) Codeforces Round 911 (Div. 2) CodeTON Round 7 (Div. 1 + Div. 2, Rated, Prizes!) Educational Codeforces Round 158 (Rated for Div. 2) Codeforces Round 910 (Div. 2) Codeforces Round 909 (Div. 3) Codeforces Round 908 (Div. 2) Educational Codeforces Round 157 (Rated for Div. 2) C++自定义的字面量 Codeforces Round 907 (Div. 2)
一段奇怪的 CPP 代码
Shiroha · 2023-12-03 · via Shiroha白羽的博客

最近发现了一个奇怪的代码,在 C++17 下。使用的 cmake 命令是

1
"~/Applications/CLion Nova.app/Contents/bin/cmake/mac/aarch64/bin/cmake" -DCMAKE_BUILD_TYPE=Debug "-DCMAKE_MAKE_PROGRAM=~/Applications/CLion Nova.app/Contents/bin/ninja/mac/aarch64/ninja" -G Ninja -S ~/Code/ClionProject -B ~/Code/ClionProject/cmake-build-debug

而这段代码则是

1
2
3
4
5
list<int> l;
for (int i = 0; i < 10; ++i) l.push_back(i);
auto iter = l.begin();
for (int i = -1; i >= -10; --i) l.insert(iter--, i);
for (int&v: l) cout << v << ' ';

这段代码的结果却是

1
-1 0 1 -10 2 -9 3 -8 4 -7 5 -6 6 -5 7 -4 8 -3 9 -2 

如果稍微调整一下,比如这样的代码

1
2
3
4
5
6
7
8
list<int> l;
for (int i = 0; i < 10; ++i) l.push_back(i);
auto iter = l.begin();
for (int i = -1; i >= -10; --i) {
l.insert(iter, i);
--iter;
}
for (int&v: l) cout << v << ' ';

得到的结果却是

1
-10 -9 -8 -7 -6 -5 -4 -3 -2 -1 0 1 2 3 4 5 6 7 8 9 

如果调整成这样

1
2
3
4
5
6
7
8
list<int> l;
for (int i = 0; i < 10; ++i) l.push_back(i);
auto iter = l.begin();
++iter;
for (int i = -1; i >= -10; --i) {
l.insert(--iter, i);
}
for (int&v: l) cout << v << ' ';

得到的结果也是

1
-10 -9 -8 -7 -6 -5 -4 -3 -2 -1 0 1 2 3 4 5 6 7 8 9 

这似乎有点不太符合预期。至少后两个是符合预期的,而第一个就有点奇怪了。第一反应是不是踩到 UB 了,但是很快在文档里找到了不符合预期的描述

docs

既然如此,那么就写一段测试代码看看

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
list<int> l;

void f(list<int>::iterator iter, int v) {
cout << *iter << ' ';
l.insert(iter, v);
cout << *iter << endl;
}

void solve() {
for (int i = 0; i < 10; ++i) l.push_back(i);
auto iter = l.begin();
for (int i = -1; i >= -10; --i) {
f(iter--, i);
cout << (iter == l.begin()) << endl;
for (int&v: l) cout << v << ' ';
cout << endl;
}
}

此处进行了一下代理,将每次试图写入钱,通过 f 函数进行代理后,再执行插入操作。结果发现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
0 0
0
-1 0 1 2 3 4 5 6 7 8 9
11 12
0
-1 0 1 2 3 4 5 6 7 8 9 -2
9 9
0
-1 0 1 2 3 4 5 6 7 8 -3 9 -2
8 8
0
-1 0 1 2 3 4 5 6 7 -4 8 -3 9 -2
7 7
0
-1 0 1 2 3 4 5 6 -5 7 -4 8 -3 9 -2
6 6
0
-1 0 1 2 3 4 5 -6 6 -5 7 -4 8 -3 9 -2
5 5
0
-1 0 1 2 3 4 -7 5 -6 6 -5 7 -4 8 -3 9 -2
4 4
0
-1 0 1 2 3 -8 4 -7 5 -6 6 -5 7 -4 8 -3 9 -2
3 3
0
-1 0 1 2 -9 3 -8 4 -7 5 -6 6 -5 7 -4 8 -3 9 -2
2 2
0
-1 0 1 -10 2 -9 3 -8 4 -7 5 -6 6 -5 7 -4 8 -3 9 -2

仍然有这个奇奇怪怪的问题。

正当我想要搜索一些文档来看看是不是什么奇奇怪怪的 bug 的时候,突然意识到一个问题:i++; 等价于下面这三行代码

1
2
3
auto tmp = i;
++i;
return tmp;

这似乎就能解释为什么了!因为在试图进行 iter-- 操作的时候,又进行了插入操作,实际上导致了 iter 本身先移动到了前一个指针的位置,而在 STL 标准库实现的 list 中,这个链表是一个双向带头循环链表,故实际上此时 iter 是先被移动到了 end() 的位置,然后再返回了 begin() 的位置,并在 begin() 前插入了一个值,使得实际上的 begin() 发生了更新。而实际上我们的 iter 早就被移动到 end() 的位置。