指针

内容简介

指针是保存内存地址的变量。它让程序可以间接访问对象,是理解数组、字符串、函数参数、动态内存、系统接口和底层编程的关键。

本篇只讲主线必会:取地址、解引用、空指针、数组关系、函数参数和常见危险。更细的数组退化、指针运算、二级指针、函数指针、void* 和别名边界见 C语言指针细节CandCpp指针进阶

用法

定义、取地址、解引用

#include <stdio.h>
 
int main(void)
{
    int value = 10;
    int *p = &value;
 
    printf("%p\n", (void*)p);  // value 的地址
    printf("%d\n", *p);        // 通过地址读取 value
 
    *p = 20;                   // 通过地址修改 value
    printf("%d\n", value);
    return 0;
}

理解三件事:

  • &value 取变量地址。
  • int *p 表示 p 指向 int
  • *p 解引用,访问地址上的 int 对象。

空指针

int *p = NULL;       // C 常见写法
int *p = nullptr;    // C++ 推荐写法

空指针表示“不指向任何有效对象”。空指针可以比较,不能解引用。

指针和数组

int arr[3] = {1, 2, 3};
int *p = arr;
 
printf("%d\n", *p);       // 1
printf("%d\n", *(p + 1)); // 2
printf("%d\n", p[2]);     // 3

数组名在很多表达式中会退化为首元素地址。完整规则见 C语言指针细节

指针作函数参数

C 的参数是按值传递。如果要让函数修改调用者的变量,可以传变量地址。

void swap(int *a, int *b)
{
    int temp = *a;
    *a = *b;
    *b = temp;
}

调用:

int x = 1;
int y = 2;
swap(&x, &y);

函数只读数据时,加 const

void print_value(const int *p)
{
    if (p != NULL) {
        printf("%d\n", *p);
    }
}

最佳代码实践

  • 指针定义后立即初始化,不确定指向哪里时设为 NULLnullptr
  • 解引用前确认指针不是空指针,并确认指向对象仍然活着。
  • 数组传参时额外传长度,不要在函数里用 sizeof(arr) 猜数组长度。
  • 谁申请内存,谁负责释放;释放后不要继续访问旧地址。
  • C++ 中优先使用 RAII智能指针std::vectorstd::string 管理资源。
  • 函数只需要读取指针指向的数据时,加 const

错误用法

空指针解引用

int *p = NULL;
*p = 10;  // 错误:空指针不能解引用

野指针

int *p;
*p = 10;  // 错误:p 没有初始化,指向未知位置

内存泄漏

#include <stdlib.h>
 
int *p = malloc(sizeof(int));
*p = 10;
// 错误:没有 free(p)

返回局部变量地址

int* bad(void)
{
    int value = 10;
    return &value;  // 错误:函数结束后 value 生命周期结束
}

注意事项

学习路径