火龙信奥
  • 首页
  • 课程
  • 题库
  • 打卡
    • 代码对战
    • 快速对战
  • 题单
  • 团队
  • 荣誉墙
  • 商城
  • 登录 / 注册

指针

作者: 作者的头像   吴新雄 , 时间:2026-04-26 14:26:58 , 所有人可见, 阅读  20

指针

一、什么是指针?

指针是一个变量,它存储的是另一个变量的内存地址。

int a = 10;     // 普通变量,存储值10
int *p = &a;    // 指针变量,存储a的地址

// & 取地址运算符
// * 间接访问运算符(解引用)

内存示意图:

变量a:  [  10  ]   地址:0x0012FF44
         ↑
指针p:  [0x0012FF44] 地址:0x0012FF48

什么是指针的解引用?

解引用(Dereferencing) 就是通过指针访问它所指向的变量的值。

int a = 10;
int *p = &a;  // p存储a的地址

// *p 就是解引用操作
cout << *p;   // 输出10,通过p找到了a的值

比喻理解: - 指针就像一张藏宝图,上面写着宝藏的位置 - 解引用就是拿着藏宝图去那个位置取出宝藏

指针解引用的本质

int a = 10;
int *p = &a;

// 以下三种写法等价
a = 20;      // 直接修改
*p = 20;     // 通过解引用修改
*(&a) = 20;  // 取地址再解引用

// 图解:
// p  = 0x1000  (存地址)
// *p = 10      (取地址0x1000里的值)

内存视角:

地址0x1000: [ 10 ]  ← 变量a
             ↑
指针p: [0x1000]      ← 存着a的地址
       解引用*p → 取出10

二、指针的基本操作

int main() {
    int a = 10;
    int *p;          // 声明指针(未初始化,野指针!)
    p = &a;          // 指针p指向a

    cout << "a的值: " << a << endl;        // 10
    cout << "a的地址: " << &a << endl;     // 0x0012FF44
    cout << "p的值: " << p << endl;        // 0x0012FF44(就是a的地址)
    cout << "p指向的值: " << *p << endl;   // 10(解引用)

    *p = 20;         // 通过指针修改a的值
    cout << "修改后a: " << a << endl;      // 20

    return 0;
}

三、指针与一维数组

3.1 数组名的本质

数组名就是指向数组第一个元素的指针常量!

int arr[5] = {10, 20, 30, 40, 50};

// arr 等价于 &arr[0]
// arr + i 等价于 &arr[i]
// *(arr + i) 等价于 arr[i]

内存布局:

arr[0]    arr[1]    arr[2]    arr[3]    arr[4]
[  10  ]  [  20  ]  [  30  ]  [  40  ]  [  50  ]
0x1000    0x1004    0x1008    0x100C    0x1010
↑
arr

3.2 数组元素的访问方式

int arr[5] = {10, 20, 30, 40, 50};
int *p = arr;  // 或 int *p = &arr[0];

// 四种访问数组元素的方式
cout << arr[2];        // 1. 下标法:30
cout << *(arr + 2);    // 2. 指针法(数组名):30  
cout << p[2];          // 3. 下标法(指针):30
cout << *(p + 2);      // 4. 指针法(指针变量):30

// 指针移动
p++;                   // 指向下一个元素(arr[1])
cout << *p;            // 20
p += 2;                // 指向arr[3]
cout << *p;            // 40

3.3 指针加减的字节数

int arr[5] = {10, 20, 30, 40, 50};
int *p = arr;

cout << "p: " << p << endl;      // 0x1000
p++;  // 不是加1个字节,而是加sizeof(int)个字节
cout << "p+1: " << p << endl;    // 0x1004(int占4字节)

// 指针加减的规律:p + n = p + n * sizeof(类型)

四、指针与二维数组

4.1 二维数组的内存布局

二维数组在内存中是按行连续存储的!

int arr[3][4] = {
    {1, 2, 3, 4},
    {5, 6, 7, 8},
    {9, 10, 11, 12}
};

内存布局(线性):

行0: [1] [2] [3] [4]
行1: [5] [6] [7] [8]  
行2: [9] [10] [11] [12]
内存地址连续:1,2,3,4,5,6,7,8,9,10,11,12

4.2 二维数组的地址表示

arr            // 二维数组名,指向第0行(类型:int (*)[4])
arr + i        // 指向第i行
*(arr + i)     // 第i行的首地址,等价于arr[i](类型:int*)
*(arr + i) + j // 第i行第j列的地址,等价于&arr[i][j]
*(*(arr + i) + j) // 第i行第j列的值,等价于arr[i][j]

图解:

arr → 行0: [1] [2] [3] [4]
       ↑
     arr[0] (行首地址)

arr+1 → 行1: [5] [6] [7] [8]
         ↑
       arr[1] (行首地址)

*(arr+1)+2 → 行1列2的地址
*(*(arr+1)+2) = 7

4.3 二维数组的访问方式

int arr[3][4] = {...};

// 1. 下标法(最直观)
cout << arr[1][2];        // 第1行第2列:7

// 2. 指针法(用数组名)
cout << *(*(arr + 1) + 2);  // 7

// 3. 用行指针
int (*p)[4] = arr;        // p是行指针,指向包含4个int的一维数组
cout << p[1][2];          // 7
cout << *(*(p + 1) + 2);  // 7

// 4. 用普通指针(将二维数组看作一维)
int *q = &arr[0][0];      // 指向第一个元素
cout << *(q + 1*4 + 2);   // 7(行*列数 + 列)

4.4 行指针(数组指针)

int arr[3][4] = {...};

// 声明一个指向"包含4个int的一维数组"的指针
int (*p)[4] = arr;  // 注意括号不能少!

// p+1 跳过一行(4个int,16字节)
cout << "arr[0]地址: " << arr << endl;      // 0x1000
cout << "arr[1]地址: " << arr + 1 << endl;  // 0x1010(+16字节)
cout << "p+1地址: " << p + 1 << endl;       // 0x1010(+16字节)

// 通过行指针访问
cout << p[0][0];     // 1
cout << p[1][2];     // 7

五、指针与地址的对应关系表

表达式 含义 类型
arr 二维数组名,第0行地址 int (*)[4]
&arr[0][0] 第0行第0列地址 int*
arr[0] 第0行首地址 int*
*arr 第0行首地址 int*
arr[0] + 2 第0行第2列地址 int*
*(arr + 1) 第1行首地址 int*
&arr[1][2] 第1行第2列地址 int*
*(arr + 1) + 2 第1行第2列地址 int*
*(*(arr + 1) + 2) 第1行第2列的值 int

六、指针运算总结

int a[5] = {10, 20, 30, 40, 50};
int b[3][4] = {...};

// 指针 + 整数 = 指针
p + 1;     // 指向下一个元素(加sizeof(类型)字节)

// 指针 - 指针 = 整数(元素个数)
p2 - p1;   // 两个指针相差的元素个数

// 比较运算
p1 < p2;   // 比较地址大小

// 指针可以自增自减
p++; p--; ++p; --p;

// 指针可以加下标
p[2];      // 等价于 *(p+2)

七、常见错误

// 错误1:未初始化指针
int *p;
*p = 10;  // 野指针,危险!

// 错误2:类型不匹配
int arr[3][4];
int *p = arr;  // 错误!arr是int (*)[4]类型

// 正确写法
int (*p)[4] = arr;  // 行指针
int *q = &arr[0][0]; // 普通指针指向第一个元素

// 错误3:混淆指针和数组
int *p;
p[2] = 5;  // p未指向有效内存

引用和指针的对比

指针 vs 引用 代码对比

int a = 10, b = 20;

// ===== 指针版本 =====
int *p = &a;     // 初始化
*p = 30;         // 修改a
p = &b;          // 改指b(可以改指向)
*p = 40;         // 修改b
if(p != nullptr) // 要检查空指针
    cout << *p;

// ===== 引用版本 =====
int &r = a;      // 必须初始化
r = 30;          // 修改a
// r = b;        // ❌ 这不是改指向!这是把b的值赋给a
// 不能改指向,r永远是a的别名
r = 40;          // 继续修改a
// 不需要检查空,引用永远有效

引用常见用处

// 1. 修改实参
void swap(int &a, int &b) {
    int temp = a;
    a = b;
    b = temp;
}

int x = 10, y = 20;
swap(x, y);  // x=20, y=10

核心区别图解

指针p:            引用r:
[0x1000]          (别名)
    ↓                ↓
  [ 10 ]           [ 10 ]
  变量a             变量a

指针可以改:p = &b → 指向b
引用不能改:r永远是a的别名

—— 本文来自火龙信奥(义乌睿码科技):义乌青少年信息学奥赛与编程教育平台,专注 CSP-J/S、NOIP、GESP 竞赛培训,线上线下融合教学,助力编程升学。网址:hlcoding.com

©2026加盟我们 | 关于我们 | ACM课程 | 常见问题 | 成果墙 | 评测记录 | 浙ICP备2021013995号
在线画图 | OI WIki | 打字练习
火龙信奥
请输入登录信息


请完成安全验证
验证码底图 滑块
向右拖动滑块完成验证
请输入用户名 / 绑定的手机号码



请输入注册信息(手机号验证码注册)





验证码5分钟有效,60秒内不可重复获取,每日最多3次

微信登录

微信登录二维码

正在生成二维码...

账号已过期,请续期。
去续期

绑定手机号

📱

为了更好地保护您的账号安全,享受完整的平台服务

请您尽快绑定手机号码