一、指针基础概念
-
指针与内存的关系
- 本质:指针是存储内存地址的变量,用于直接操作内存。
-
变量内存分配:
int a = 10; // 在栈区申请4字节(假设int为4字节)存储10 int* p = &a; // p存储a的地址,通过*p可访问a的值
-
内存单元编号:每个字节有一个唯一地址(如
0x7ffd42a1
),指针通过地址访问数据。
-
指针类型
- 常见类型:
char*
、int*
、float*
、void*
等。 -
作用:
解引用时访问的字节数:char*
访问1字节,int*
访问4字节。
指针运算的步长:p+1
的偏移量为sizeof(指针类型)
字节。
-
void*
特性:-
无具体类型,需强制转换后才能解引用。
-
常用于泛型编程(如
malloc
返回值)。
-
2.指针变量的定义与使用
-
定义语法:
类型* 变量名;
int* p; // 定义指向int的指针 char* str; // 定义指向char的指针
-
操作符:
&
:取变量地址(如p = &a
)。
*
:解引用,访问指针指向的值(如*p = 20
修改a的值)。
二、指针运算
-
指针 ± 整数
- 规则:
p ± n
的地址偏移量为n * sizeof(指针类型)
。
int arr[5];
int* p = arr; // p指向arr[0]
p = p + 3; // p指向arr[3],地址偏移3*4=12字节
2.指针 - 指针
-
规则:结果为两指针之间的元素个数(需同类型指针)。
int* p1 = &arr[2]; int* p2 = &arr[5]; int n = p2 - p1; // n = 3(元素个数)
三、指针与数组
-
数组名的本质
- 数组名是首元素地址,类型为元素类型指针(如
int arr[5]
中arr
的类型是int*
)。 -
例外情况:
sizeof(arr)
返回整个数组的字节大小(如sizeof(int[5])
)。
&arr
的类型是int(*)[5]
(数组指针)。
2.指针访问数组元素
-
等价性:
arr[i]
⇨*(arr + i)
⇨*(p + i)
。int arr[3] = {1, 2, 3}; int* p = arr; printf("%d", *(p + 1)); // 输出2
3.数组传参
-
本质:传递首元素地址,形参退化为指针。
void func(int arr[]); // 等价于 void func(int* arr);
四、指针与字符串
字符指针与字符数组
- 字符指针(不可修改):
char* str = "hello"; // 字符串常量存储在只读数据段
// str[0] = 'H'; // 错误!运行时崩溃
-
字符数组(可修改):
char str[] = "hello"; // 栈区分配内存,可修改内容 str[0] = 'H'; // 合法
五、多级指针与指针数组
-
多级指针
- 定义:指向指针的指针。
int a = 10;
int* p = &a;
int** pp = &p; // 二级指针
printf("%d", **pp); // 输出10
2.指针数组
-
定义:元素为指针的数组。
int* arr[3]; // 存放3个int*类型指针
六、野指针与安全性
1.野指针成因
- 未初始化的指针(如
int* p;
)。 -
指向已释放内存的指针(如
free(p)
后未置空)。 -
指针越界访问(如数组越界后继续操作)。
2.解决方案
-
初始化:
int* p = NULL;
。 -
释放后置空:
free(p); p = NULL;
。
七、常量指针与指针常量
-
常量指针(指向常量的指针)
- 语法:
const int* p
或int const* p
。 -
特点:不能通过指针修改指向的值,但指针本身可指向其他地址。
int a = 10; const int* p = &a; // *p = 20; // 错误! p = &b; // 合法
2.指针常量(指针本身不可变)
-
语法:
int* const p
。 -
特点:指针的指向不可变,但指向的值可变。
int a = 10; int* const p = &a; *p = 20; // 合法 // p = &b; // 错误!
3.双重常量
-
语法:
const int* const p
。 -
特点:指针指向和指向的值均不可变。
-
-
八、关键细节与注意事项
-
指针运算限制
指针相减仅在指向同一数组时有效。
-
动态内存分配
malloc
返回void*
,需强制类型转换:
int* p = (int*)malloc(sizeof(int) * 5);
指针数组 vs 数组指针
-
指针数组:
int* arr[5];
(元素为指针)。 -
数组指针:
int (*p)[5];
(指向长度为5的数组)。
九、总结
-
内存操作:通过地址直接读写内存。
-
类型匹配:指针类型决定解引用和运算行为。
-
安全性:避免野指针和内存泄漏。
-
灵活应用:结合数组、字符串、多级指针实现复杂数据结构。