C语言学习1-数组、字符串
C语言学习2-指针的使用、const 指针
C语言学习3-指针和数组
C语言学习4-结构体
C语言学习5-函数
一、指针基础:理解地址与数据
1.1 什么是指针?
指针 = 内存地址,指针变量是存储内存地址的变量。
#include <stdio.h>
int main() {
int x = 5; // 在内存中分配一个整数空间
int *px = &x; // px存储x的内存地址
printf("=== 基础指针演示 ===\n");
printf("x的值: %d\n", x); // 5
printf("x的地址: %p\n", &x); // 0x7ff7bfeff374
printf("px的值: %p\n", px); // 0x7ff7bfeff374
printf("px的地址: %p\n", &px); // 0x7ff7bfeff368
printf("*px的值: %d\n", *px); // 5
return 0;
}
1.2 内存布局示意图
内存地址布局图:
+-------------------+ 指向 +-------------------+
| 指针变量 px | -----------> | 整型变量 x |
| 地址: 0x368 | | 地址: 0x374 |
| 值: 0x374 | | 值: 5 |
+-------------------+ +-------------------+
访问路径说明:
- &x → 获取x的地址:0x374
- px → px存储的值:0x374
- *px → 访问地址0x374的内容:5
图解说明:
- 每个变量都在内存中有自己的地址
- 指针变量存储的是其他变量的地址
- 通过
*指针可以访问指针指向的数据
二、通过指针修改内存数据
2.1 修改指针指向的值
#include <stdio.h>
int main() {
int x = 5;
int *px = &x;
printf("修改前: x = %d, *px = %d\n", x, *px); // 都是5
*px = 10; // 通过指针修改x的值
printf("修改后: x = %d, *px = %d\n", x, *px); // 都是10
return 0;
}
2.2 修改过程的内存变化图
修改前内存状态:
+-------------------+ 指向 +-------------------+
| 指针 px | -----------> | 变量 x |
| 地址: 0x368 | | 地址: 0x374 |
| 值: 0x374 | | 值: 5 |
+-------------------+ +-------------------+
执行 *px = 10 后:
+-------------------+ 指向 +-------------------+
| 指针 px | -----------> | 变量 x |
| 地址: 0x368 | | 地址: 0x374 |
| 值: 0x374 | | 值: 10 ← 被修改! |
+-------------------+ +-------------------+
关键理解:
-
px存储的是地址,这个地址不变 -
*px操作的是该地址指向的内存内容 - 修改
*px就是修改原始变量x的值
三、多级指针(指针的指针)
3.1 二级指针详解
#include <stdio.h>
int main() {
int x = 5;
int *px = &x; // 一级指针
int **pxx = &px; // 二级指针
printf("x = %d\n", x); // 5
printf("px = %p\n", px); // x的地址
printf("*px = %d\n", *px); // 5
printf("pxx = %p\n", pxx); // px的地址
printf("*pxx = %p\n", *pxx); // x的地址
printf("**pxx = %d\n", **pxx); // 5
return 0;
}
3.2 多级指针内存关系图
三级指针关系图:
+----------------+ 指向 +----------------+ 指向 +----------------+
| 三级指针 pxx | --------> | 二级指针 px | --------> | 一级变量 x |
| 地址: 0x360 | | 地址: 0x368 | | 地址: 0x374 |
| 值: 0x368 | | 值: 0x374 | | 值: 5 |
+----------------+ +----------------+ +----------------+
访问路径分解:
pxx = 0x368 (px的地址)
*pxx = 0x374 (px的值,即x的地址)
**pxx = 5 (x的值)
&pxx = 0x360 (pxx自己的地址)
&px = 0x368 (px自己的地址)
&x = 0x374 (x自己的地址)
3.3 通过多级指针修改变量
#include <stdio.h>
int main() {
int x = 5;
int *px = &x;
int **pxx = &px;
printf("初始: x = %d\n", x); // 5
// 三种方式修改同一个变量
x = 10;
printf("直接修改: x = %d\n", x); // 10
*px = 20;
printf("通过px: x = %d\n", x); // 20
**pxx = 30;
printf("通过pxx: x = %d\n", x); // 30
return 0;
}
修改过程图解:
所有修改都作用于同一内存位置:
+----------------+ 指向 +----------------+ 指向 +----------------+
| pxx | --------> | px | --------> | x |
| 值: 0x368 | | 值: 0x374 | | 值: 30 ← 最终值 |
+----------------+ +----------------+ +----------------+
↑ ↑ ↑
| | |
**pxx=30 *px=20 x=10
(第三层修改) (第二层修改) (第一层修改)
四、指针的大小与系统架构
4.1 指针大小测试
#include <stdio.h>
int main() {
printf("=== 指针大小测试 ===\n");
printf("char* 大小: %zu 字节\n", sizeof(char*));
printf("int* 大小: %zu 字节\n", sizeof(int*));
printf("double* 大小: %zu 字节\n", sizeof(double*));
printf("void* 大小: %zu 字节\n", sizeof(void*));
printf("int** 大小: %zu 字节\n", sizeof(int**));
return 0;
}
4.2 指针大小示意图
32位系统内存布局:
+------------------------+
| 指针变量 (4字节) | → 所有指针都是4字节
| 存储32位地址 (0-4GB) |
+------------------------+
64位系统内存布局:
+------------------------+
| 指针变量 (8字节) | → 所有指针都是8字节
| 存储64位地址 |
+------------------------+
关键结论:
- 指针大小只取决于系统架构
- 与指向的数据类型无关
- char*、int*、double* 大小都相同
五、函数参数中的指针
5.1 指针作为函数参数
#include <stdio.h>
// 参数中的 * 表示需要传递地址
void modifyThroughPointer(int *location, int newValue) {
*location = newValue; // 修改指针指向的内存
}
void demonstrateFunctionPointers() {
int value = 100;
printf("调用前: value = %d\n", value); // 100
modifyThroughPointer(&value, 200); // 传递地址
printf("调用后: value = %d\n", value); // 200
}
int main() {
demonstrateFunctionPointers();
return 0;
}
5.2 函数调用过程图解
函数调用前:
main函数栈帧:
+-------------------+
| 变量 value = 100 |
| 地址: 0x1234 |
+-------------------+
调用 modifyThroughPointer(&value, 200):
参数传递:
+-------------------+ +------------------------+
| &value = 0x1234 | ---> | location = 0x1234 |
| newValue = 200 | | newValue = 200 |
+-------------------+ +------------------------+
| *location = 200 | ← 修改0x1234处的值
+------------------------+
函数返回后:
main函数栈帧:
+-------------------+
| 变量 value = 200 | ← 值被修改!
| 地址: 0x1234 |
+-------------------+
5.3 指针参数的常见使用场景
#include <stdio.h>
// 场景1:返回多个值
void getMinMax(int a, int b, int *min, int *max) {
*min = (a < b) ? a : b;
*max = (a > b) ? a : b;
}
// 场景2:交换两个变量
void swap(int *a, int *b) {
int temp = *a;
*a = *b;
*b = temp;
}
void demonstratePracticalUsage() {
// 返回多个值
int x = 25, y = 40;
int min, max;
getMinMax(x, y, &min, &max);
printf("min=%d, max=%d\n", min, max);
// 交换变量
printf("交换前: x=%d, y=%d\n", x, y);
swap(&x, &y);
printf("交换后: x=%d, y=%d\n", x, y);
}
交换过程图解:
交换前:
+-----------+ +-----------+
| x = 25 | | y = 40 |
| 地址:0x100| | 地址:0x104|
+-----------+ +-----------+
调用 swap(&x, &y):
+-----------+ +-----------+
| a → 0x100 | | b → 0x104 |
| temp = 25 | | |
+-----------+ +-----------+
执行交换:
*a = *b → 0x100处写入40
*b = temp → 0x104处写入25
交换后:
+-----------+ +-----------+
| x = 40 | | y = 25 |
| 地址:0x100| | 地址:0x104|
+-----------+ +-----------+
六、综合调试示例
6.1 完整的指针链分析
#include <stdio.h>
void debugPointerChain() {
int data = 42;
int *ptr1 = &data;
int **ptr2 = &ptr1;
int ***ptr3 = &ptr2;
printf("=== 完整指针链分析 ===\n");
printf("data: %d (地址:%p)\n", data, &data);
printf("ptr1: %p (地址:%p) → *ptr1=%d\n", ptr1, &ptr1, *ptr1);
printf("ptr2: %p (地址:%p) → **ptr2=%d\n", ptr2, &ptr2, **ptr2);
printf("ptr3: %p (地址:%p) → ***ptr3=%d\n", ptr3, &ptr3, ***ptr3);
}
int main() {
debugPointerChain();
return 0;
}
6.2 指针链内存布局总图
完整指针链内存布局:
+-------------+ 指向 +-------------+ 指向 +-------------+ 指向 +-------------+
| ptr3 | --------> | ptr2 | --------> | ptr1 | --------> | data |
| 地址:0x360 | | 地址:0x368 | | 地址:0x370 | | 地址:0x378 |
| 值:0x368 | | 值:0x370 | | 值:0x378 | | 值:42 |
+-------------+ +-------------+ +-------------+ +-------------+
访问路径总结:
ptr3 = 0x368 (ptr2的地址)
*ptr3 = 0x370 (ptr2的值 = ptr1的地址)
**ptr3 = 0x378 (ptr1的值 = data的地址)
***ptr3 = 42 (data的值)
&ptr3 = 0x360
&ptr2 = 0x368
&ptr1 = 0x370
&data = 0x378
七、核心概念总结
7.1 指针操作符图解
操作符作用示意图:
+-------------+ &操作符 +-------------+
| 变量 x | <----------- | &x |
| 值: 5 | 获取地址 | 值: 0x374 |
| 地址: 0x374 | +-------------+
+-------------+
|
| *操作符
| 解引用
v
+-------------+
| *px |
| 值: 5 |
+-------------+
7.2 指针使用黄金法则
-
声明语法:
数据类型 *指针名 - 赋值规则:必须赋值为有效地址
-
访问数据:通过
*指针访问 -
修改数据:通过
*指针 = 值修改 - 安全使用:始终初始化,释放后置NULL
7.3 最佳实践代码模板
// ✅ 安全指针使用模板
void safePointerUsage() {
// 1. 声明并初始化为NULL
int *ptr = NULL;
// 2. 分配内存并检查
ptr = (int*)malloc(sizeof(int));
if (ptr == NULL) {
printf("内存分配失败\n");
return;
}
// 3. 使用指针
*ptr = 100;
printf("值: %d\n", *ptr);
// 4. 释放并置空
free(ptr);
ptr = NULL; // 防止悬空指针
}
// ✅ 函数参数明确意图
void goodFunction(const int *input, // 输入参数(只读)
int *output) { // 输出参数(可写)
// input指向的数据不会被修改
*output = *input * 2; // 修改output指向的数据
}