Skip to content

Commit 078728b

Browse files
authored
Merge branch 'Awesome-Embedded-Learning-Studio:main' into main
2 parents 764aa4a + 5878e83 commit 078728b

12 files changed

Lines changed: 733 additions & 148 deletions

File tree

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
#include <stdio.h>
2+
#include <string.h>
3+
4+
/* 浮点数为什么是近似值?把 0.1f 的 32 个二进制位摊开看就明白了。 */
5+
int main(void) {
6+
/* 1) float 只有 32 位:1 符号 + 8 指数 + 23 尾数 */
7+
float f = 0.1f;
8+
unsigned int bits;
9+
memcpy(&bits, &f, sizeof(bits));
10+
11+
printf("0.1f 存进 float 的 32 个二进制位 (符号 | 指数 | 尾数):\n ");
12+
for (int i = 31; i >= 0; i--) {
13+
printf("%d", (bits >> i) & 1);
14+
if (i == 31 || i == 23)
15+
printf(" ");
16+
}
17+
printf("\n\n");
18+
19+
/* 尾数 1001100110011... 是无限循环,23 位放不下只能截断,所以不是 0.1 */
20+
printf("用不同精度打印它,都不是 0.1:\n");
21+
printf(" 9 位精度 : %.9f\n", f);
22+
printf(" 20 位精度: %.20f\n\n", f);
23+
24+
/* 2) 经典的 0.1 + 0.2 != 0.3 在 double 下才看得见 */
25+
double a = 0.1, b = 0.2, c = 0.3;
26+
printf("double: 0.1 + 0.2 == 0.3 ? %s\n", (a + b == c) ? "yes" : "no");
27+
printf(" a + b = %.20f\n", a + b);
28+
printf(" c = %.20f\n", c);
29+
30+
return 0;
31+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
#include <stdio.h>
2+
#include <string.h>
3+
4+
/* Why are floating-point numbers approximations? Spread out the 32 bits of 0.1f and it becomes
5+
* clear. */
6+
int main(void) {
7+
/* 1) float has only 32 bits: 1 sign + 8 exponent + 23 mantissa */
8+
float f = 0.1f;
9+
unsigned int bits;
10+
memcpy(&bits, &f, sizeof(bits));
11+
12+
printf("32 bits of 0.1f in a float (sign | exponent | mantissa):\n ");
13+
for (int i = 31; i >= 0; i--) {
14+
printf("%d", (bits >> i) & 1);
15+
if (i == 31 || i == 23)
16+
printf(" ");
17+
}
18+
printf("\n\n");
19+
20+
/* The mantissa 1001100110011... repeats forever; 23 bits can't hold it, so it gets truncated.
21+
*/
22+
printf("Printed at different precisions — never exactly 0.1:\n");
23+
printf(" 9 digits : %.9f\n", f);
24+
printf(" 20 digits: %.20f\n\n", f);
25+
26+
/* 2) The classic 0.1 + 0.2 != 0.3 only shows up under double */
27+
double a = 0.1, b = 0.2, c = 0.3;
28+
printf("double: 0.1 + 0.2 == 0.3 ? %s\n", (a + b == c) ? "yes" : "no");
29+
printf(" a + b = %.20f\n", a + b);
30+
printf(" c = %.20f\n", c);
31+
32+
return 0;
33+
}

code/examples/vol2/01_rvalue_reference.cpp

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
// 01_rvalue_reference.cpp
22
// 右值引用与值类别:构造、拷贝、移动、析构全过程追踪
3+
// Standard: C++17
34

45
#include <iostream>
56
#include <string>
@@ -12,17 +13,35 @@ class Tracker {
1213
explicit Tracker(std::string name) : name_(std::move(name)) {
1314
std::cout << " [" << name_ << "] 构造\n";
1415
}
16+
1517
Tracker(const Tracker& other) : name_(other.name_ + "_copy") {
1618
std::cout << " [" << name_ << "] 拷贝构造\n";
1719
}
20+
1821
Tracker(Tracker&& other) noexcept : name_(std::move(other.name_)) {
1922
other.name_ = "(moved-from)";
2023
std::cout << " [" << name_ << "] 移动构造\n";
2124
}
25+
2226
~Tracker() { std::cout << " [" << name_ << "] 析构\n"; }
27+
28+
Tracker& operator=(const Tracker& other) {
29+
name_ = other.name_ + "_copy";
30+
std::cout << " [" << name_ << "] 拷贝赋值\n";
31+
return *this;
32+
}
33+
34+
Tracker& operator=(Tracker&& other) noexcept {
35+
name_ = std::move(other.name_);
36+
other.name_ = "(moved-from)";
37+
std::cout << " [" << name_ << "] 移动赋值\n";
38+
return *this;
39+
}
40+
2341
const std::string& name() const { return name_; }
2442
};
2543

44+
/// @brief 返回临时对象(prvalue)
2645
Tracker make_tracker(std::string name) {
2746
return Tracker(std::move(name));
2847
}
@@ -34,16 +53,23 @@ int main() {
3453

3554
std::cout << "=== 2. 拷贝构造 ===\n";
3655
Tracker b = a;
37-
std::cout << " a.name = " << a.name() << "\n b.name = " << b.name() << "\n\n";
56+
std::cout << " a.name = " << a.name() << "\n";
57+
std::cout << " b.name = " << b.name() << "\n\n";
3858

3959
std::cout << "=== 3. 移动构造(显式 std::move)===\n";
4060
Tracker c = std::move(a);
41-
std::cout << " a.name = " << a.name() << "\n c.name = " << c.name() << "\n\n";
61+
std::cout << " a.name = " << a.name() << "\n";
62+
std::cout << " c.name = " << c.name() << "\n\n";
4263

4364
std::cout << "=== 4. 返回临时对象 ===\n";
4465
Tracker d = make_tracker("D");
4566
std::cout << " d.name = " << d.name() << "\n\n";
4667

47-
std::cout << "=== 5. 程序结束,析构顺序 ===\n";
68+
std::cout << "=== 5. 移动赋值 ===\n";
69+
d = std::move(b);
70+
std::cout << " b.name = " << b.name() << "\n";
71+
std::cout << " d.name = " << d.name() << "\n\n";
72+
73+
std::cout << "=== 6. 程序结束,析构顺序 ===\n";
4874
return 0;
4975
}

code/examples/vol2/02_move_semantics.cpp

Lines changed: 35 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
// 02_move_semantics.cpp
2-
// 移动构造与移动赋值:Buffer 资源所有权转移与 vector 操作对比
2+
// 移动构造与移动赋值:Buffer 资源转移全过程追踪
3+
// Standard: C++17
34

45
#include <cstring>
56
#include <iostream>
@@ -17,24 +18,40 @@ class Buffer {
1718
: data_(new char[capacity]), size_(0), capacity_(capacity) {
1819
std::cout << " [Buffer] 分配 " << capacity << " 字节\n";
1920
}
21+
2022
~Buffer() {
2123
if (data_) {
2224
std::cout << " [Buffer] 释放 " << capacity_ << " 字节\n";
2325
delete[] data_;
2426
}
2527
}
28+
2629
Buffer(const Buffer& other)
2730
: data_(new char[other.capacity_]), size_(other.size_), capacity_(other.capacity_) {
2831
std::memcpy(data_, other.data_, size_);
2932
std::cout << " [Buffer] 拷贝构造 " << capacity_ << " 字节\n";
3033
}
34+
3135
Buffer(Buffer&& other) noexcept
3236
: data_(other.data_), size_(other.size_), capacity_(other.capacity_) {
3337
other.data_ = nullptr;
3438
other.size_ = 0;
3539
other.capacity_ = 0;
3640
std::cout << " [Buffer] 移动构造(指针转移)\n";
3741
}
42+
43+
Buffer& operator=(const Buffer& other) {
44+
if (this != &other) {
45+
delete[] data_;
46+
data_ = new char[other.capacity_];
47+
size_ = other.size_;
48+
capacity_ = other.capacity_;
49+
std::memcpy(data_, other.data_, size_);
50+
std::cout << " [Buffer] 拷贝赋值 " << capacity_ << " 字节\n";
51+
}
52+
return *this;
53+
}
54+
3855
Buffer& operator=(Buffer&& other) noexcept {
3956
if (this != &other) {
4057
delete[] data_;
@@ -48,17 +65,20 @@ class Buffer {
4865
}
4966
return *this;
5067
}
68+
5169
void append(const char* str, std::size_t len) {
5270
if (size_ + len <= capacity_) {
5371
std::memcpy(data_ + size_, str, len);
5472
size_ += len;
5573
}
5674
}
75+
5776
std::size_t size() const { return size_; }
77+
std::size_t capacity() const { return capacity_; }
5878
};
5979

6080
int main() {
61-
std::cout << "=== 1. 创建缓冲区 ===\n";
81+
std::cout << "=== 1. 创建两个缓冲区 ===\n";
6282
Buffer a(1024);
6383
a.append("Hello", 5);
6484
Buffer b(2048);
@@ -71,17 +91,25 @@ int main() {
7191

7292
std::cout << "=== 3. 移动构造 ===\n";
7393
Buffer d = std::move(b);
74-
std::cout << " d.size() = " << d.size() << "\n\n";
94+
std::cout << " d.size() = " << d.size() << "\n";
95+
std::cout << " b.capacity() = " << b.capacity() << "\n\n";
7596

76-
std::cout << "=== 4. vector 中的移动 ===\n";
97+
std::cout << "=== 4. 移动赋值 ===\n";
98+
a = std::move(d);
99+
std::cout << " a.size() = " << a.size() << "\n";
100+
std::cout << " d.capacity() = " << d.capacity() << "\n\n";
101+
102+
std::cout << "=== 5. vector 中的移动 ===\n";
77103
std::vector<Buffer> buffers;
78104
buffers.reserve(4);
79105
std::cout << " push_back 左值:\n";
80-
buffers.push_back(c);
106+
buffers.push_back(c); // 拷贝
81107
std::cout << " push_back std::move:\n";
82-
buffers.push_back(std::move(c));
108+
buffers.push_back(std::move(c)); // 移动
83109
std::cout << " emplace_back 原位构造:\n";
84-
buffers.emplace_back(512);
110+
buffers.emplace_back(512); // 直接在 vector 中构造
111+
std::cout << '\n';
85112

113+
std::cout << "=== 6. 程序结束 ===\n";
86114
return 0;
87115
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
// noexcept_vector_realloc.cpp -- noexcept 移动 vs 非 noexcept 移动 在 vector 扩容时的差异
2+
// Standard: C++17
3+
4+
#include <iostream>
5+
#include <string>
6+
#include <type_traits>
7+
#include <utility>
8+
#include <vector>
9+
10+
// 用模板参数 NoexceptMove 切换移动构造是否标 noexcept,其余代码完全相同
11+
template <bool NoexceptMove> class TrackedBuffer {
12+
char* data_;
13+
std::size_t capacity_;
14+
std::string tag_;
15+
16+
public:
17+
explicit TrackedBuffer(std::size_t cap, std::string tag)
18+
: data_(new char[cap]), capacity_(cap), tag_(std::move(tag)) {}
19+
20+
~TrackedBuffer() { delete[] data_; }
21+
22+
TrackedBuffer(const TrackedBuffer& other)
23+
: data_(new char[other.capacity_]), capacity_(other.capacity_), tag_(other.tag_) {
24+
std::cout << " [" << tag_ << "] 拷贝构造\n";
25+
}
26+
27+
// 唯一的区别:noexcept 标记
28+
TrackedBuffer(TrackedBuffer&& other) noexcept(NoexceptMove)
29+
: data_(other.data_), capacity_(other.capacity_), tag_(std::move(other.tag_)) {
30+
other.data_ = nullptr;
31+
other.capacity_ = 0;
32+
std::cout << " [" << tag_ << "] 移动构造\n";
33+
}
34+
35+
TrackedBuffer& operator=(const TrackedBuffer&) = delete;
36+
TrackedBuffer& operator=(TrackedBuffer&&) = delete;
37+
};
38+
39+
int main() {
40+
using NB = TrackedBuffer<true>; // 移动构造标了 noexcept
41+
using TB = TrackedBuffer<false>; // 移动构造没标 noexcept
42+
43+
static_assert(std::is_nothrow_move_constructible_v<NB>, "NB 的移动构造是 noexcept");
44+
static_assert(!std::is_nothrow_move_constructible_v<TB>, "TB 的移动构造不是 noexcept");
45+
46+
std::cout << "=== noexcept 移动 + vector 扩容 ===\n";
47+
{
48+
std::vector<NB> v;
49+
v.reserve(1); // 先预留 1 个槽位
50+
v.emplace_back(64, "Noexcept版"); // 占住唯一的槽位
51+
std::cout << "--- 触发扩容 ---\n";
52+
v.emplace_back(64, "Noexcept版"); // 超出容量,必须扩容搬运
53+
}
54+
55+
std::cout << "\n=== 非 noexcept 移动 + vector 扩容 ===\n";
56+
{
57+
std::vector<TB> v;
58+
v.reserve(1);
59+
v.emplace_back(64, "Throwing版");
60+
std::cout << "--- 触发扩容 ---\n";
61+
v.emplace_back(64, "Throwing版"); // 扩容时 vector 不敢用移动,退回拷贝
62+
}
63+
64+
return 0;
65+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// rule_of_five_fallback.cpp -- 只有析构函数时,std::move 退化为拷贝构造
2+
// Standard: C++17
3+
4+
#include <iostream>
5+
#include <type_traits>
6+
#include <utility>
7+
8+
// 只定义了析构函数,没有声明任何拷贝/移动操作
9+
class OnlyDestructor {
10+
char* data_;
11+
12+
public:
13+
explicit OnlyDestructor(std::size_t n) : data_(new char[n]) {}
14+
15+
~OnlyDestructor() { delete[] data_; }
16+
// 注意:这里既没有声明移动构造,也没有声明拷贝构造
17+
};
18+
19+
// 编译期验证:它「能移动构造」,但不是因为真有移动构造函数
20+
static_assert(!std::is_trivially_move_constructible_v<OnlyDestructor>,
21+
"没有真正的(平凡的)移动构造函数");
22+
static_assert(std::is_move_constructible_v<OnlyDestructor>,
23+
"但 is_move_constructible 为 true —— 编译器退回到拷贝构造来满足");
24+
25+
int main() {
26+
std::cout
27+
<< "is_trivially_move_constructible_v: "
28+
<< std::is_trivially_move_constructible_v<OnlyDestructor> << " (没有真正的移动构造)\n";
29+
std::cout << "is_move_constructible_v: "
30+
<< std::is_move_constructible_v<OnlyDestructor> << " (但能用拷贝构造蒙混过关)\n";
31+
32+
// 真要执行 OnlyDestructor b = std::move(a),隐式拷贝构造做浅拷贝,
33+
// a 和 b 的 data_ 指向同一块内存,两者析构时 double free。
34+
// 这里不真的跑(会崩),编译期 static_assert 已经给出结论。
35+
return 0;
36+
}

0 commit comments

Comments
 (0)