第5.15章: 常见指针陷阱

English

概述

指针很强大,但极其容易被误用。C++ 程序中的许多缺陷 都来自对指针有效性、生命周期、别名、对齐以及算术的错误假设。 本章汇总最常见的错误,以便你能避开它们。

你将学到:

1. 悬垂指针

悬垂指针指向一个已经不存在的对象。


int const *p;

{
    int x{42};
    p = ::std::addressof(x);
} // x is destroyed here

// *p;   // ❌ dangling pointer

指针只在它所指向的对象存活期间有效。

2. 空指针误用

解引用 nullptr 永远是未定义行为。


int *p{nullptr};

// *p = 10;   // ❌ undefined behavior

另外,把 nullptr 配非零大小传给 memcpy、memmove、 memcmp 或 memchr 也是未定义 行为(见第5.14章)。

3. 差一错误

指针算术以所指类型为单位移动。


int a[3]{1,2,3};

int *p = a;
int *end = a + 3;   // one past the end (valid but not dereferenceable)

// *end;   // ❌ undefined behavior

只有区间 [a, a + 3) 可用于解引用。

4. 未对齐的指针

某些类型要求比其他类型更严格的对齐。把指针转换为对齐要求 更严格的类型可能导致未定义行为。


unsigned char buffer[8]{};

// int *p = reinterpret_cast(buffer + 1);   // ❌ misaligned

对大多数类型,未对齐访问是不允许的。

5. 严格别名违规

通过不相关类型的指针访问对象是未定义 行为(见第5.13章)。


float f{1.0f};
int *p = reinterpret_cast(::std::addressof(f));

// *p;   // ❌ strict aliasing violation

6. 内存重叠的危险

对重叠的内存使用 memcpy 是未定义行为。


// memcpy(dst, src, n);   // ❌ if ranges overlap

不确定时请使用 memmove。

7. 指针算术错误

指针算术以所指类型为单位,而不是以字节为单位。


int a[3]{1,2,3};

int *p = a;
int *q = p + 1;   // moves by sizeof(int) bytes

另外,sizeof(pointer) 不是所指对象的大小。

8. 生命周期问题

指针并不会延长它所指向对象的生命周期。


int const *p;

{
    int x{10};
    p = ::std::addressof(x);
}

// *p;   // ❌ x is gone

9. 类型双关错误

用 reinterpret_cast 做类型双关几乎总是未定义 行为。请改用 memcpy 或 ::std::bit_cast。

10. 内存函数的错误使用

常见错误包括:

11. 指针比较陷阱

顺序比较(<、>)只对指向同一数组的 指针有效。


// p < q;   // ❌ undefined behavior if p and q are unrelated

要点