1. 指针的本质与内存模型
指针是C++中最强大也最危险的工具之一。理解指针首先要从计算机的内存模型开始。我们可以把内存想象成一个巨大的酒店,每个房间都有唯一的门牌号(内存地址),而指针就是记录这些门牌号的便签纸。
在32位系统中,指针固定占4字节;64位系统中则占8字节。这个大小与指针指向的数据类型无关——无论是指向char还是double的指针,其本身的大小都相同。这是因为指针存储的只是内存地址,就像酒店门牌号的长度与房间大小无关一样。
cpp复制int num = 42;
int* ptr = # // ptr保存的是num的内存地址
关键理解:指针变量本身也有自己的内存地址,这是新手常混淆的点。
&ptr获取的是指针变量本身的地址,而ptr存储的是它指向的变量的地址。
2. 指针的核心操作与陷阱
2.1 基本操作三要素
- 声明:
int* p;声明一个指向int类型的指针 - 取址:
p = &var;获取变量的内存地址 - 解引用:
*p = 10;通过指针修改目标值
常见错误模式:
cpp复制int* p; // 未初始化的野指针
*p = 42; // 灾难!访问随机内存地址
int* q = NULL; // 好习惯:初始化时置空
if(q) *q = 42; // 安全检查
2.2 指针运算的玄机
指针加减运算的实际移动距离取决于指向类型的大小:
cpp复制int arr[5] = {0};
int* p = arr; // 指向arr[0]
p = p + 1; // 移动sizeof(int)字节,指向arr[1]
这种特性使得指针成为数组遍历的高效工具,但也容易导致越界访问。在debug模式下,建议使用assert(p >= arr && p < arr+5)进行边界检查。
3. 多级指针与复杂声明
3.1 指针的指针
二级指针(int** pp)常用于以下场景:
- 动态二维数组
- 需要修改指针本身参数的函数
- 指针数组的管理
cpp复制int val = 42;
int* p = &val;
int** pp = &p; // pp指向p
// 通过二级指针修改变量
**pp = 100; // val现在为100
3.2 解读复杂声明
C++的声明语法遵循"螺旋法则":
- 从变量名开始
- 向右解析直到遇到右括号
- 向左解析
- 重复直到整个声明解析完毕
例如:
cpp复制int *(*(*fp)(int))[10];
解析步骤:
fp是一个指针- 指向接受int参数的函数
- 该函数返回指向数组的指针
- 数组包含10个int指针
4. 函数指针与回调机制
4.1 基本用法
函数指针允许运行时动态选择函数:
cpp复制int add(int a, int b) { return a + b; }
int sub(int a, int b) { return a - b; }
int (*funcPtr)(int, int) = nullptr;
funcPtr = &add; // 指向add函数
cout << funcPtr(2,3); // 输出5
4.2 现代C++改进
C++11引入了类型安全的替代方案:
cpp复制#include <functional>
std::function<int(int,int)> callback;
callback = [](int a, int b){ return a * b; };
5. 智能指针革命
5.1 三大智能指针对比
| 类型 | 所有权 | 线程安全 | 循环引用 | 典型用途 |
|---|---|---|---|---|
| unique_ptr | 独占 | 否 | 无 | 资源唯一所有者 |
| shared_ptr | 共享 | 是 | 可能 | 共享资源 |
| weak_ptr | 观察 | 是 | 无 | 打破循环引用 |
5.2 实现原理剖析
shared_ptr的核心是引用计数:
cpp复制template<typename T>
class SimpleSharedPtr {
T* ptr;
int* count;
public:
explicit SimpleSharedPtr(T* p) : ptr(p), count(new int(1)) {}
~SimpleSharedPtr() {
if(--(*count) == 0) {
delete ptr;
delete count;
}
}
// 拷贝构造/赋值运算符省略...
};
性能提示:shared_ptr的原子操作会带来额外开销,在性能关键路径避免过度使用。
6. 指针在数据结构中的应用
6.1 链表实现关键
cpp复制struct Node {
int data;
Node* next; // 自引用指针
};
class LinkedList {
Node* head;
public:
void insert(int val) {
Node* newNode = new Node{val, head};
head = newNode;
}
~LinkedList() { /* 需要手动释放所有节点 */ }
};
6.2 树结构遍历
cpp复制struct TreeNode {
int val;
TreeNode* left;
TreeNode* right;
};
void preOrder(TreeNode* root) {
if(!root) return;
cout << root->val << " ";
preOrder(root->left);
preOrder(root->right);
}
7. 指针与多态
基类指针实现运行时多态:
cpp复制class Shape {
public:
virtual void draw() = 0;
virtual ~Shape() {}
};
class Circle : public Shape {
void draw() override { /* 画圆实现 */ }
};
Shape* shape = new Circle();
shape->draw(); // 调用Circle的draw
delete shape; // 必须虚析构!
8. 指针安全最佳实践
- 初始化原则:声明时立即初始化,要么指向有效对象,要么设为nullptr
- 所有权明确:清楚每个指针的生命周期管理责任
- RAII惯用法:用对象生命周期管理资源
- 避免裸指针:现代C++优先使用智能指针
- 边界检查:对数组指针进行范围验证
- 类型安全:避免void*的随意转换
9. 调试技巧与常见问题
9.1 典型错误排查
-
段错误(Segmentation fault):
- 访问空指针
- 访问已释放内存
- 栈溢出
-
内存泄漏检测:
- Valgrind工具
- AddressSanitizer编译选项
- 重载new/delete记录分配
9.2 调试器技巧
在GDB中:
code复制(gdb) p ptr # 打印指针值
(gdb) p *ptr # 解引用指针
(gdb) watch ptr # 监视指针变化
在VS中:
- 使用内存窗口查看指针指向的内容
- 数据断点监控特定内存地址的变化
10. 指针性能优化
- 局部性原则:顺序访问数组比随机跳转效率高
- 减少间接访问:多层指针解引用会增加缓存未命中
- 预取技术:对已知访问模式的指针提前加载数据
- 自定义内存池:减少new/delete开销
cpp复制// 自定义内存池示例
class MemoryPool {
struct Block { /* ... */ };
Block* freeList;
public:
void* allocate(size_t size) {
if(!freeList) expandPool();
void* ptr = freeList;
freeList = freeList->next;
return ptr;
}
// ...
};
11. 指针与硬件交互
在嵌入式系统中,指针常用于:
- 访问内存映射寄存器
- DMA缓冲区管理
- 硬件抽象层实现
cpp复制// 访问硬件寄存器
volatile uint32_t* const HW_REG = reinterpret_cast<uint32_t*>(0x40021000);
*HW_REG |= 0x1; // 设置第一位
关键点:硬件操作必须使用volatile防止编译器优化,并且要注意内存对齐要求。
12. 指针进阶模式
12.1 类型双关(Type Punning)
安全实现方式:
cpp复制union Converter {
float f;
uint32_t u;
};
float pi = 3.14f;
Converter c;
c.f = pi;
uint32_t bits = c.u; // 合法类型双关
12.2 函数指针高级应用
实现策略模式:
cpp复制class PaymentStrategy {
public:
virtual void pay(int amount) = 0;
};
class CreditCardStrategy : public PaymentStrategy { /*...*/ };
class PaymentProcessor {
PaymentStrategy* strategy;
public:
void setStrategy(PaymentStrategy* s) { strategy = s; }
void execute(int amount) { strategy->pay(amount); }
};
13. 指针与并发编程
13.1 原子指针
C++11引入的原子类型:
cpp复制#include <atomic>
std::atomic<int*> atomicPtr;
void threadFunc() {
int* local = new int(42);
atomicPtr.store(local, std::memory_order_release);
}
// 另一个线程
int* p = atomicPtr.load(std::memory_order_acquire);
13.2 内存屏障
防止指令重排序:
cpp复制std::atomic_thread_fence(std::memory_order_seq_cst);
14. 指针的替代方案
现代C++推荐做法:
- 引用:函数参数传递优先使用const引用
- 视图类型:
std::string_view,std::span - 迭代器:标准库容器提供的安全访问方式
- optional:
std::optional表示可能为空的值
cpp复制void process(std::span<int> data) {
for(auto& item : data) {
// 安全访问,自带边界检查
}
}
15. 指针在模板元编程中的应用
类型萃取示例:
cpp复制template<typename T>
struct PointerTraits {
static constexpr bool is_pointer = false;
};
template<typename T>
struct PointerTraits<T*> {
static constexpr bool is_pointer = true;
using element_type = T;
};
template<typename T>
void func(T param) {
if constexpr(PointerTraits<T>::is_pointer) {
// 指针特化处理
}
}
16. 指针与ABI兼容性
跨二进制接口注意事项:
- 指针大小可能随平台变化
- 函数指针调用约定必须一致
- 避免在接口中传递STL智能指针
- 使用PIMPL模式隐藏实现细节
cpp复制// PIMPL示例
class MyClass {
struct Impl;
std::unique_ptr<Impl> pImpl;
public:
MyClass();
~MyClass(); // 必须声明,因为Impl是不完整类型
};
17. 指针与反射机制
有限反射实现:
cpp复制struct FieldInfo {
const char* name;
size_t offset;
size_t size;
};
class Reflectable {
public:
virtual std::vector<FieldInfo> getFields() = 0;
void* getFieldPtr(const std::string& name) {
for(auto& field : getFields()) {
if(field.name == name) {
return reinterpret_cast<char*>(this) + field.offset;
}
}
return nullptr;
}
};
18. 指针安全包装模式
18.1 非空指针保证
cpp复制template<typename T>
class NotNull {
T* ptr;
public:
NotNull(T* p) : ptr(p) {
if(!p) throw std::invalid_argument("Pointer cannot be null");
}
operator T*() const { return ptr; }
T* operator->() const { return ptr; }
// 禁用拷贝/赋值给普通指针...
};
void foo(NotNull<int*> p) {
*p = 42; // 保证安全
}
18.2 范围检查指针
cpp复制template<typename T>
class BoundedPtr {
T* ptr;
T* begin;
T* end;
public:
BoundedPtr(T* p, T* b, T* e) : ptr(p), begin(b), end(e) {
if(p < b || p >= e) throw std::out_of_range("Pointer out of bounds");
}
// 操作符重载...
};
19. 指针与移动语义
19.1 移动构造函数中的指针转移
cpp复制class ResourceHolder {
int* resource;
public:
ResourceHolder(ResourceHolder&& other) noexcept
: resource(other.resource) {
other.resource = nullptr; // 重要!避免双重释放
}
~ResourceHolder() { delete resource; }
};
19.2 自定义删除器
cpp复制auto fileDeleter = [](FILE* fp) { if(fp) fclose(fp); };
std::unique_ptr<FILE, decltype(fileDeleter)> filePtr(fopen("data.txt", "r"), fileDeleter);
20. 指针与元编程
编译时指针运算:
cpp复制template<typename T, size_t N>
constexpr size_t arraySize(T (&)[N]) {
return N;
}
int arr[10];
static_assert(arraySize(arr) == 10, "");
21. 指针与SIMD优化
数据对齐处理:
cpp复制#include <immintrin.h>
void simdAdd(float* a, float* b, float* result, size_t count) {
assert(reinterpret_cast<uintptr_t>(a) % 32 == 0 && "Alignment required");
for(size_t i = 0; i < count; i += 8) {
__m256 va = _mm256_load_ps(a + i);
__m256 vb = _mm256_load_ps(b + i);
__m256 vresult = _mm256_add_ps(va, vb);
_mm256_store_ps(result + i, vresult);
}
}
22. 指针与协程
协程帧指针:
cpp复制#include <coroutine>
struct Task {
struct promise_type {
Task get_return_object() { return {}; }
std::suspend_never initial_suspend() { return {}; }
std::suspend_never final_suspend() noexcept { return {}; }
void return_void() {}
void unhandled_exception() {}
};
};
Task coroFunc() {
int local = 42;
co_await std::suspend_always{};
// 协程暂停时,local保存在协程帧中
}
23. 指针与JIT编译
动态代码生成:
cpp复制#include <sys/mman.h>
void* allocExecutableMemory(size_t size) {
void* ptr = mmap(nullptr, size, PROT_READ | PROT_WRITE | PROT_EXEC,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if(ptr == MAP_FAILED) throw std::bad_alloc();
return ptr;
}
void emitCode(unsigned char* code) {
// 写入机器指令...
*code++ = 0xB8; // mov eax, 42
*code++ = 0x2A;
*code++ = 0x00;
*code++ = 0x00;
*code++ = 0x00;
*code++ = 0xC3; // ret
}
void runJIT() {
unsigned char* mem = static_cast<unsigned char*>(allocExecutableMemory(1024));
emitCode(mem);
auto func = reinterpret_cast<int(*)()>(mem);
std::cout << func(); // 输出42
munmap(mem, 1024);
}
24. 指针与多线程同步
24.1 双重检查锁定模式
正确实现:
cpp复制class Singleton {
static std::atomic<Singleton*> instance;
static std::mutex mtx;
Singleton() = default;
public:
static Singleton* getInstance() {
Singleton* tmp = instance.load(std::memory_order_acquire);
if(!tmp) {
std::lock_guard<std::mutex> lock(mtx);
tmp = instance.load(std::memory_order_relaxed);
if(!tmp) {
tmp = new Singleton();
instance.store(tmp, std::memory_order_release);
}
}
return tmp;
}
};
24.2 无锁编程模式
cpp复制template<typename T>
class LockFreeStack {
struct Node {
T data;
Node* next;
};
std::atomic<Node*> head;
public:
void push(const T& data) {
Node* newNode = new Node{data, head.load()};
while(!head.compare_exchange_weak(newNode->next, newNode));
}
bool pop(T& result) {
Node* oldHead = head.load();
while(oldHead && !head.compare_exchange_weak(oldHead, oldHead->next));
if(!oldHead) return false;
result = oldHead->data;
delete oldHead;
return true;
}
};
25. 指针与异常安全
25.1 资源获取即初始化(RAII)
cpp复制class FileHandle {
FILE* file;
public:
explicit FileHandle(const char* filename)
: file(fopen(filename, "r")) {
if(!file) throw std::runtime_error("File open failed");
}
~FileHandle() {
if(file) fclose(file);
}
// 禁用拷贝
FileHandle(const FileHandle&) = delete;
FileHandle& operator=(const FileHandle&) = delete;
// 允许移动
FileHandle(FileHandle&& other) noexcept : file(other.file) {
other.file = nullptr;
}
};
25.2 异常安全保证
- 基本保证:异常发生时资源不泄漏
- 强保证:操作要么完全成功,要么回滚到原始状态
- 不抛保证:操作承诺不抛出异常
cpp复制class Transaction {
std::vector<std::function<void()>> rollbackActions;
public:
template<typename Op, typename Rollback>
void execute(Op op, Rollback rb) {
op();
rollbackActions.push_back(rb);
}
~Transaction() {
if(std::uncaught_exceptions()) {
for(auto it = rollbackActions.rbegin(); it != rollbackActions.rend(); ++it) {
(*it)();
}
}
}
};
26. 指针与性能分析
26.1 缓存友好设计
- 紧凑布局:减少指针间接访问
- 预取友好:顺序访问模式
- 避免虚假共享:多线程数据对齐
cpp复制struct CacheLineAligned {
alignas(64) int data1; // 64字节对齐,典型缓存行大小
alignas(64) int data2;
};
26.2 性能分析工具
- perf:Linux性能计数器
- VTune:Intel性能分析器
- Cachegrind:缓存模拟分析
bash复制# perf示例
perf stat -e cache-misses,L1-dcache-load-misses ./program
27. 指针与调试符号
27.1 调试信息保留
编译时添加-g选项保留调试符号:
bash复制g++ -g -O0 program.cpp -o program
27.2 符号解析
cpp复制#include <execinfo.h>
#include <dlfcn.h>
#include <cxxabi.h>
void printStackTrace() {
void* callstack[128];
int frames = backtrace(callstack, 128);
char** strs = backtrace_symbols(callstack, frames);
for(int i = 0; i < frames; ++i) {
Dl_info info;
if(dladdr(callstack[i], &info)) {
int status;
char* demangled = abi::__cxa_demangle(info.dli_sname, nullptr, nullptr, &status);
printf("%s\n", demangled ? demangled : info.dli_sname);
free(demangled);
}
}
free(strs);
}
28. 指针与ABI稳定性
28.1 二进制兼容性
保持ABI稳定的关键:
- 避免改变类布局
- 不修改虚函数表顺序
- 使用PIMPL隐藏实现
- 谨慎使用内联函数
28.2 版本化接口
cpp复制// v1接口
struct InterfaceV1 {
virtual void operation(int) = 0;
virtual ~InterfaceV1() = default;
};
// v2接口扩展
struct InterfaceV2 : InterfaceV1 {
virtual void newOperation(double) = 0;
};
// 工厂函数保持兼容
extern "C" InterfaceV1* createInstance(int version) {
if(version == 1) return new ImplV1();
if(version == 2) return new ImplV2();
return nullptr;
}
29. 指针与跨语言交互
29.1 C接口设计
cpp复制// 头文件声明
extern "C" {
struct Handle;
Handle* create_resource();
void use_resource(Handle*, int param);
void destroy_resource(Handle*);
}
// 实现
struct Handle {
std::unique_ptr<Resource> res;
};
Handle* create_resource() {
try {
return new Handle{std::make_unique<Resource>()};
} catch(...) {
return nullptr;
}
}
29.2 Python扩展
使用pybind11:
cpp复制#include <pybind11/pybind11.h>
class MyClass {
int value;
public:
MyClass(int v) : value(v) {}
int get() const { return value; }
};
PYBIND11_MODULE(example, m) {
pybind11::class_<MyClass>(m, "MyClass")
.def(pybind11::init<int>())
.def("get", &MyClass::get);
}
30. 指针与安全编程
30.1 防御性编程技巧
- 指针验证:
cpp复制bool isValid(const void* p) {
if(!p) return false;
// 平台特定的地址范围检查
const uintptr_t addr = reinterpret_cast<uintptr_t>(p);
return addr >= 0x1000 && addr < 0x00007FFFFFFFFFFF;
}
- 安全解引用:
cpp复制template<typename T>
T& safeDeref(T* ptr) {
if(!ptr) throw std::invalid_argument("Null pointer dereference");
return *ptr;
}
30.2 现代安全特性
- 指针验证扩展:
cpp复制#include <memory>
void process(std::span<int> data) {
// 自动边界检查
}
- 合约编程:
cpp复制void foo(int* ptr) [[expects: ptr != nullptr]] {
// 编译器可能插入检查
}
31. 指针与编译器优化
31.1 限制指针别名
cpp复制void addArrays(int* restrict a, int* restrict b, int* restrict c, int n) {
for(int i = 0; i < n; ++i) {
c[i] = a[i] + b[i]; // 编译器知道内存不重叠,可向量化
}
}
31.2 编译器屏障
cpp复制void threadSafeIncrement(int* p) {
(*p)++;
asm volatile("" ::: "memory"); // 防止指令重排
}
32. 指针与协程内存管理
协程帧的生命周期:
cpp复制#include <coroutine>
struct Task {
struct promise_type {
Task get_return_object() { return {}; }
std::suspend_always initial_suspend() { return {}; }
std::suspend_always final_suspend() noexcept { return {}; }
void return_void() {}
void unhandled_exception() {}
};
};
Task coroFunc() {
int local = 42; // 存储在协程帧中
co_await std::suspend_always{};
// 协程恢复后local仍然有效
}
33. 指针与SIMD数据布局
高效向量化数据布局:
cpp复制// 结构体数组(AOS)
struct Particle {
float x, y, z;
float vx, vy, vz;
};
// 数组结构体(SOA) - 更适合SIMD
struct Particles {
std::vector<float> x, y, z;
std::vector<float> vx, vy, vz;
};
// 混合布局(SOAoS)
struct ParticleBlock {
alignas(32) float x[8], y[8], z[8]; // 适合AVX(8 floats)
};
34. 指针与自定义分配器
实现STL兼容分配器:
cpp复制template<typename T>
class PoolAllocator {
static std::vector<T*> pool;
static size_t next;
public:
using value_type = T;
T* allocate(size_t n) {
if(n != 1) throw std::bad_alloc();
if(next >= pool.size()) {
pool.push_back(static_cast<T*>(::operator new(sizeof(T))));
}
return pool[next++];
}
void deallocate(T* p, size_t n) noexcept {
if(n == 1) {
// 简单实现:不真正释放,保持池中
}
}
};
35. 指针与多态内存资源
C++17内存资源:
cpp复制#include <memory_resource>
void useMemoryResource() {
std::byte buffer[1024];
std::pmr::monotonic_buffer_resource pool{buffer, sizeof(buffer)};
std::pmr::vector<int> vec{&pool};
vec.reserve(100); // 使用池内存
}
36. 指针与硬件特定操作
内存屏障示例:
cpp复制inline void memoryBarrier() {
#if defined(__x86_64__)
asm volatile("mfence" ::: "memory");
#elif defined(__aarch64__)
asm volatile("dmb ish" ::: "memory");
#endif
}
37. 指针与协程性能优化
协程帧分配优化:
cpp复制struct CoroutineFrame {
// 手动管理协程状态
void (*resumeFn)(CoroutineFrame*);
void* promise;
// 局部变量...
};
void myCoroutine(CoroutineFrame* frame) {
// 手动编写协程状态机
switch(frame->state) {
case 0: /* 初始状态 */ break;
// ...
}
}
38. 指针与JIT内存管理
动态代码内存管理:
cpp复制class JITMemory {
std::vector<std::pair<void*, size_t>> allocated;
public:
~JITMemory() {
for(auto& [ptr, size] : allocated) {
munmap(ptr, size);
}
}
void* allocate(size_t size) {
void* ptr = mmap(nullptr, size, PROT_READ | PROT_WRITE | PROT_EXEC,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if(ptr == MAP_FAILED) throw std::bad_alloc();
allocated.emplace_back(ptr, size);
return ptr;
}
};
39. 指针与SIMD自动向量化
帮助编译器自动向量化:
cpp复制void vectorAdd(float* a, float* b, float* c, size_t n) {
// 告诉编译器指针不重叠
__restrict auto pa = a;
__restrict auto pb = b;
__restrict auto pc = c;
// 告诉编译器n是倍数
if(n % 8 != 0) __builtin_unreachable();
for(size_t i = 0; i < n; ++i) {
pc[i] = pa[i] + pb[i];
}
}
40. 指针与编译器内置函数
利用编译器内置功能:
cpp复制void* alignedAlloc(size_t size, size_t align) {
// GCC/Clang内置函数
return __builtin_aligned_alloc(align, size);
}
bool isPowerOfTwo(uintptr_t p) {
// 高效判断
return __builtin_popcountll(p) == 1;
}
41. 指针与调试信息
增强调试信息:
cpp复制struct DebugPtr {
void* ptr;
const char* file;
int line;
DebugPtr(void* p, const char* f, int l)
: ptr(p), file(f), line(l) {}
~DebugPtr() {
if(ptr) {
fprintf(stderr, "Potential leak at %s:%d\n", file, line);
}
}
};
#define DBG_PTR(p) DebugPtr(p, __FILE__, __LINE__)
42. 指针与静态分析
Clang静态分析检查:
cpp复制void foo(int* p) {
if(!p) return;
*p = 42; // 静态分析器能检测空指针解引用
}
// 使用注解
void bar(int* _Nonnull p) {
*p = 42; // 编译器知道p非空
}
43. 指针与性能计数器
利用PMU:
cpp复制#include <linux/perf_event.h>
#include <sys/ioctl.h>
class PerformanceCounter {
int fd;
public:
PerformanceCounter(uint64_t type, uint64_t config) {
struct perf_event_attr attr = {};
attr.type = type;
attr.config = config;
attr.size = sizeof(attr);
fd = syscall(__NR_perf_event_open, &attr, 0, -1, -1, 0);
}
~PerformanceCounter() { close(fd); }
uint64_t read() const {
uint64_t val;
::read(fd, &val, sizeof(val));
return val;
}
};
void profilePointerAccess() {
PerformanceCounter cacheMisses(PERF_TYPE_HW_CACHE,
PERF_COUNT_HW_CACHE_LL | (PERF_COUNT_HW_CACHE_OP_READ << 8) |
(PERF_COUNT_HW_CACHE_RESULT_MISS << 16));
int array[1024];
volatile int sum = 0;
for(int i = 0; i < 1024; i += 16) {
sum += array[i]; // 故意制造缓存未命中
}
std::cout << "LLC misses: " << cacheMisses.read() << "\n";
}
44. 指针与编译器优化屏障
防止过度优化:
cpp复制template<typename T>
void doNotOptimize(T const& val) {
asm volatile("" : : "r,m"(val) : "memory");
}
void benchmark() {
int* ptr = new int(42);
auto start = std::chrono::high_resolution_clock::now();
for(int i = 0; i < 1000; ++i) {
doNotOptimize(*ptr); // 防止被优化掉
}
auto end = std::chrono::high_resolution_clock::now();
delete ptr;
std::cout << "Elapsed: " << (end - start).count() << " ns\n";
}
45. 指针与硬件预取
优化预取模式:
cpp复制void prefetchExample(int* data, size_t len) {
const size_t prefetchAhead = 4; // 提前预取4个缓存行
for(size_t i = 0; i < len; ++i) {
if(i + prefetchAhead < len) {
__builtin_prefetch(&data[i + prefetchAhead], 0, 3);
}
// 处理当前数据
data[i] = process(data[i]);
}
}
46. 指针与缓存对齐
优化缓存行使用:
cpp复制struct alignas(64) CacheAligned { // 典型缓存行大小
int data1;
int data2;
// ...
};
void falseSharingExample() {
CacheAligned a, b; // 保证不在同一缓存行
std::thread t1([&]() {
for(int i = 0; i < 1'000'000; ++i) ++a.data1;
});
std::thread t2([&]() {
for(int i = 0; i < 1'000'000; ++i) ++b.data1;
});
t1.join(); t2.join(); // 无虚假共享
}
47. 指针与分支预测
优化分支预测:
cpp复制void processData(int* data, size_t len, bool condition) {
// 提前计算指针减少分支
int* end = data + len;
if(condition) {
for(; data != end; ++data) {
*data = (*data) * 2;
}
} else {
for(; data != end; ++data) {
*data = (*data) / 2;
}
}
}
48. 指针与编译器提示
给编译器提示:
cpp复制void likelyExample(int* ptr) {
if(__builtin_expect(ptr != nullptr, 1)) {
*ptr = 42; // 编译器会优化为likely分支
} else {
handleError();
}
}
49. 指针与SIMD内在函数
手动向量化:
cpp复制#include <immintrin.h>
void simdAdd(float* a, float* b, float* c, size_t n) {
size_t i = 0;
for(; i + 8 <= n; i += 8) {
__m256 va = _mm256_load_ps(a + i);
__m256 vb = _mm256_load_ps(b + i);
__m256 vc = _mm256_add_ps(va, vb);
_mm256_store_ps(c + i, vc);
}
// 处理剩余元素
for(; i < n; ++i) {
c[i] = a[i] + b[i];
}
}
50. 指针与多线程原子操作
原子指针操作:
cpp复制#include <atomic>
class LockFreeQueue {
struct Node {
int data;
std::atomic<Node*> next;
};
std::atomic<Node*> head;
std::atomic<Node*> tail;
public:
void push(int val) {
Node* newNode = new Node{val, nullptr};
Node* oldTail = tail.load();
while(!tail.compare_exchange_weak(oldTail, newNode)) {
oldTail = tail.load();
}
oldTail->next.store(newNode);
}
bool pop(int& result)
