指针、内存布局、生命周期
笔记目录
Phase 1:C/C++ + Linux 基础地基
-
内存与指针
-
struct/class 与对象生命周期
-
RAII 与资源管理
-
STL 容器
-
多线程同步
-
socket 网络基础
-
Linux 调试与构建工具
Phase 2:音视频 + 传输协议
-
图像/视频基础
-
H.264/H.265
-
时间戳与同步
-
RTP/RTSP/RTMP/HLS/WebRTC
-
FFmpeg / GStreamer
Phase 3:嵌入式平台链路
-
交叉编译
-
SDK 与媒体 pipeline
-
板端部署
-
性能、日志、排障
-
端到端链路追踪
第 1 周:指针、内存、生命周期
目标:
-
知道栈、堆、全局区大概怎么回事
-
知道指针和变量到底是什么关系
-
知道悬空指针、野指针、重复释放为什么危险
-
能用 gdb 看地址和内存变化
第 2 周:struct/class、构造/析构、拷贝/移动
目标:
-
搞清对象何时创建、何时销毁
-
能看懂值传递、引用传递
-
理解浅拷贝/深拷贝
-
知道为什么 C++ 要有移动语义
第 3 周:STL 与 RAII
目标:
-
vector / map / unordered_map / queue
-
string / pair
-
unique_ptr / shared_ptr
-
资源管理为什么要 RAII
第 4 周:多线程与同步
目标:
-
线程创建
-
mutex
-
condition_variable
-
生产者-消费者模型
第 5 周:Linux 网络编程基础
目标:
-
socket 是什么
-
TCP 服务器最小模型
-
client/server 通信过程
-
阻塞/非阻塞
第 6 周:Linux 工具链与排障
目标:
-
gdb
-
strace
-
lsof
-
tcpdump
-
Makefile
-
CMake
-
core dump 基础排查
第一课:指针、内存布局、生命周期
学一个核心:
“变量到底存在哪里,指针到底指向什么”
这是后面学 socket、线程、音视频 buffer、板端 SDK 的底层基础。
1. 先建立最小认知模型
先记这句话:
变量 = 一块内存里的数据
指针 = 这块内存的地址
比如:
int a = 10;
int* p = &a;
含义不是“p 等于 a”,而是:
-
a 是一个 int
-
a 的值是 10
-
&a 是 a 的地址
-
p 保存这个地址
-
*p 表示去这个地址里取值
所以:
-
p 看的是“位置”
-
*p 看的是“位置里的内容”
2. 内存区
(1)栈 stack
特点:
-
函数里的局部变量通常在栈上
-
生命周期跟函数作用域绑定
-
函数返回,这块局部内存通常就失效了
例子:
void f() {
int x = 5;
}
x 一般就是栈变量。
(2)堆 heap
特点:
-
手动申请
-
手动释放
-
生命周期不由作用域自动控制
例子:
int* p = new int(5);
delete p;
(3)静态/全局区
特点:
-
程序启动就存在
-
程序结束才释放
例子:
int g_value = 100;
static int s_value = 200;
3. 为什么生命周期是核心
看这个错误例子:
int* bad_func() {
int x = 10;
return &x;
}
为什么错?
因为 x 是局部变量,函数结束后它的生命周期结束了。
你返回它的地址,外面拿到的是一个失效地址。
这就是典型的:
悬空指针 dangling pointer
这类 bug 在嵌入式和服务端都很难查,因为:
-
编译可能不报错
-
运行不一定马上崩
-
但行为已经不可靠
4. 第一个实验:打印地址,建立“内存感”
把下面代码保存为 lesson1_memory.cpp:
#include <iostream>
int g_value = 100;
void test_stack() {
int a = 10;
int b = 20;
std::cout << "[stack] a value = " << a << ", address = " << &a << std::endl;
std::cout << "[stack] b value = " << b << ", address = " << &b << std::endl;
}
void test_heap() {
int* p = new int(30);
std::cout << "[heap] p value(address stored in p) = " << p << std::endl;
std::cout << "[heap] *p = " << *p << std::endl;
std::cout << "[heap] &p(address of pointer variable itself) = " << &p << std::endl;
delete p;
p = nullptr;
}
void test_global() {
static int s_value = 200;
std::cout << "[global] g_value = " << g_value << ", address = " << &g_value << std::endl;
std::cout << "[static] s_value = " << s_value << ", address = " << &s_value << std::endl;
}
int main() {
test_stack();
test_heap();
test_global();
return 0;
}
编译:
g++ -std=c++17 -g lesson1_memory.cpp -o lesson1_memory
运行:
./lesson1_memory
观察:
-
a、b 的地址彼此很近
-
p 的值本身是一个地址
-
&p 和 p 不是一回事
-
全局/static 变量地址通常和局部变量区域风格不同
5. 第二个实验:错误生命周期
保存为 lesson1_dangling.cpp:
#include <iostream>
int* bad_func() {
int x = 123;
return &x;
}
int main() {
int* p = bad_func();
std::cout << "p = " << p << std::endl;
std::cout << "*p = " << *p << std::endl; // 未定义行为
return 0;
}
编译:
g++ -std=c++17 -g -Wall lesson1_dangling.cpp -o lesson1_dangling
你可能会看到编译器给 warning。
即使程序跑出来一个值,也不要以为“能跑就是对”。
这里要记住一句工程判断:
只要生命周期错了,程序就已经错了;没崩只是运气好。
6. 今天第三个实验:用 gdb 看变量
运行:
gdb ./lesson1_memory
进入后执行:
break test_stack
run
next
print a
print &a
next
print b
print &b
continue
你今天只需要学会 4 个 gdb 动作:
-
break 函数名
-
run
-
next
-
print 变量
五、今天必须掌握的 6 个概念
1. p和 *p的区别
-
p:地址
-
*p:地址里存的值
2. &a是什么
-
变量 a 的地址
3. 栈变量什么时候失效
-
函数返回或作用域结束后
4. 堆内存谁负责释放
-
申请它的人负责释放,C++ 现代写法会进一步交给 RAII/智能指针管理
5. 为什么返回局部变量地址危险
-
因为生命周期结束,地址不再可靠
6. 为什么必须会看地址
-
后面调试 buffer、网络收发、SDK 指针传递时,不会再只靠猜
六、今天的练习题
练习 1:自己写一个交换函数
要求你分别写:
错误版本
void swap_bad(int a, int b)
正确版本
void swap_good(int* a, int* b)
观察为什么前者交换失败,后者成功。
练习 2:写一个函数返回堆内存
例如:
int* create_value(int x)
函数内部用 new 分配,返回地址,在 main 中打印并释放。
体会:
-
这种写法生命周期是“可继续存在”的
-
但要自己 delete
练习 3:写一个数组与指针实验
例如:
int arr[5] = {1,2,3,4,5};
打印:
-
arr
-
&arr[0]
-
arr[1]
-
*(arr + 1)
体会数组名和首元素地址的关系。
练习 4:gdb 调试一次
自己给程序打断点,打印:
-
局部变量值
-
局部变量地址
-
指针值
-
*指针
七、今天的输出要求
1. 一个目录
cpp-foundation-labs/
└── lesson01_memory/
├── lesson1_memory.cpp
├── lesson1_dangling.cpp
└── notes.md
2. 一个 notes.md
里面只写这几项:
# Lesson 01 - 指针与内存
## 我今天学了什么
- 指针保存的是地址
- *p 表示解引用
- 局部变量生命周期结束后地址会失效
## 我做了哪些实验
- 栈/堆/全局变量地址打印
- 返回局部变量地址的错误实验
- gdb 打断点看变量地址
## 我遇到的问题
- 例如:不理解 p 和 &p 的区别
## 我现在能回答的问题
- 为什么不能返回局部变量地址
- 为什么堆内存需要释放
3. 一个 Git 提交
git init
git add .
git commit -m "lesson01: pointer memory lifetime basics"
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)