指针基础知识

Version < C++ 11

注意: 在以下所有内容中,假设存在 C++ 11 常量 nullptr。对于早期版本,将 nullptr 替换为 NULLNULL 是用于扮演类似角色的常量。

创建指针变量

可以使用特定的*语法创建指针变量,例如 int *pointer_to_int;
当变量是指针类型int *)时,它只包含一个内存地址。存储器地址是存储基础类型int)的数据的位置。

将变量的大小与指向同一类型的指针的大小进行比较时,差异很明显:

// Declare a struct type `big_struct` that contains
// three long long ints.
typedef struct {
    long long int foo1;
    long long int foo2;
    long long int foo3;
} big_struct;

// Create a variable `bar` of type `big_struct`
big_struct bar;
// Create a variable `p_bar` of type `pointer to big_struct`.
// Initialize it to `nullptr` (a null pointer).
big_struct *p_bar0 = nullptr;

// Print the size of `bar`
std::cout << "sizeof(bar) = " << sizeof(bar) << std::endl;
// Print the size of `p_bar`.
std::cout << "sizeof(p_bar0) = " << sizeof(p_bar0) << std::endl;

/* Produces:
    sizeof(bar) = 24
    sizeof(p_bar0) = 8
*/

取另一个变量的地址

指针可以作为正常变量在彼此之间分配; 在这种情况下,它是从一个指针复制到另一个指针的内存地址,而不是指针指向的实际数据
而且,它们可以取值 nullptr,它表示空的内存位置。等于 nullptr 的指针包含无效的内存位置,因此它不引用有效数据。

你可以通过在变量前加上运算符 &地址来获取给定类型变量的内存地址。& 返回的值是指向基础类型的指针,该类型包含变量的内存地址( 只要变量不超出范围,它就是有效数据 )。

// Copy `p_bar0` into `p_bar_1`.
big_struct *p_bar1 = p_bar0;

// Take the address of `bar` into `p_bar_2`
big_struct *p_bar2 = &bar;

// p_bar1 is now nullptr, p_bar2 is &bar.

p_bar0 = p_bar2;

// p_bar0 is now &bar.

p_bar2 = nullptr;

// p_bar0 == &bar
// p_bar1 == nullptr
// p_bar2 == nullptr

与参考文献相反:

  • 分配两个指针不会覆盖分配的指针所指的内存;
  • 指针可以为 null。
  • 明确要求运算符的地址

访问指针的内容

由于获取地址需要 &,因此访问内容需要使用解除引用运算符 *作为前缀。当指针被取消引用时,它将成为基础类型的变量(实际上是对它的引用)。然后可以读取和修改它,如果不是 const

(*p_bar0).foo1 = 5;

// `p_bar0` points to `bar`. This prints 5.
std::cout << "bar.foo1 = " << bar.foo1 << std::endl;

// Assign the value pointed to by `p_bar0` to `baz`.
big_struct baz;
baz = *p_bar0;

// Now `baz` contains a copy of the data pointed to by `p_bar0`.
// Indeed, it contains a copy of `bar`.

// Prints 5 as well
std::cout << "baz.foo1 = " << baz.foo1 << std::endl;

*和运算符 . 的组合缩写为 ->

std::cout << "bar.foo1 = " << (*p_bar0).foo1 << std::endl; // Prints 5
std::cout << "bar.foo1 = " <<  p_bar0->foo1  << std::endl; // Prints 5

取消引用无效指针

取消引用指针时,应确保指向有效数据。取消引用无效指针(或空指针)可能导致内存访问冲突,或读取或写入垃圾数据。

big_struct *never_do_this() {
   // This is a local variable. Outside `never_do_this` it doesn't exist.
   big_struct retval;
   retval.foo1 = 11;
   // This returns the address of `retval`.
   return &retval;
   // `retval` is destroyed and any code using the value returned
   // by `never_do_this` has a pointer to a memory location that
   // contains garbage data (or is inaccessible).
}

在这种情况下,g++clang++正确发出警告:

(Clang) warning: address of stack memory associated with local variable 'retval' returned [-Wreturn-stack-address]
(Gcc)   warning: address of local variable ‘retval’ returned [-Wreturn-local-addr]

因此,当指针是函数的参数时必须小心,因为它们可以为 null:

void naive_code(big_struct *ptr_big_struct) {
    // ... some code which doesn't check if `ptr_big_struct` is valid.
    ptr_big_struct->foo1 = 12;
}

// Segmentation fault.
naive_code(nullptr);