C/C++ 指针解析
C/C++ 指针解析笔记
一、指针的核心本质
- 定义:
- 指针是一种特殊的变量,它存储的是另一个变量的内存地址。
- 通过这个地址,我们可以间接访问和修改该地址上存储的数据。
- 可以把内存看作一系列带有编号的格子,内存地址就是格子的编号,指针变量存储的就是这个编号。
- 为什么需要指针?
- 直接内存操作:允许对内存进行更底层的控制。
- 动态内存分配:如
malloc,new返回的就是分配内存的起始地址(指针)。 - 高效传递大型数据结构:通过传递指针(地址)而非整个数据结构的副本给函数,可以提高效率。
- 实现复杂数据结构:如链表、树、图等,其节点间的连接通常通过指针实现。
- 函数间修改外部变量:通过传递变量的地址,函数内部可以修改函数外部的变量。
二、指针的声明与基本操作
-
声明指针变量:
data_type *pointer_name;data_type:指针所指向的变量的数据类型。这个类型至关重要,它决定了:- 解引用行为:从指针指向的地址开始读取多少字节,以及如何解释这些字节。
- 指针算术的步长:指针加减运算时,地址移动的单位长度。
*:表明pointer_name是一个指针变量。pointer_name:指针变量的名称。- 示例:
int *p_int; char *p_char; double *p_double;
-
取地址运算符 (
&):-
获取一个变量在内存中的实际地址。
-
示例:
C++
int var = 10; int *ptr = &var; // ptr 现在存储了 var 的内存地址
-
-
解引用运算符 (
*) (间接寻址运算符):-
访问指针所指向地址中存储的值。
-
示例:
int value = *ptr; // value 将被赋值为 10 (var 的值) *ptr = 20; // var 的值现在被修改为 20 -
重要:解引用一个未初始化或指向无效内存区域的指针是危险的,会导致未定义行为(通常是程序崩溃)。
-
-
空指针 (
nullptrC++11及以后,NULLC/旧C++):- 表示一个不指向任何有效内存地址的指针。
- 良好的编程习惯是初始化指针为
nullptr,并在释放内存后也将其设为nullptr,以防止野指针。 - 示例:
int *ptr = nullptr; - 解引用空指针同样会导致程序崩溃。
三、指针类型的重要性
- 决定解引用的字节数和解释方式:
int *p;*p会访问sizeof(int)个字节并将其解释为整数。char *p;*p会访问sizeof(char)个字节并将其解释为字符。- 如果指针类型与实际存储的数据类型不匹配,解引用可能导致错误的数据或行为。
- 决定指针算术的“步长”:
int_ptr++:地址值增加sizeof(int)。char_ptr++:地址值增加sizeof(char)。
四、指针算术 (Pointer Arithmetic)
指针算术是基于指针所指向的数据类型的大小进行的。
- 递增 (
++) / 递减 (--):ptr++:使指针指向内存中下一个同类型元素。ptr--:使指针指向内存中上一个同类型元素。
- 加法 (
+) / 减法 (-) 整数:ptr + n:结果是一个新指针,指向从ptr当前位置之后第n个同类型元素。地址增加n * sizeof(pointed_type)。ptr - n:结果是一个新指针,指向从ptr当前位置之前第n个同类型元素。地址减少n * sizeof(pointed_type)。
- 指针相减 (
ptr1 - ptr2):- 前提:
ptr1和ptr2必须指向同一个数组中的元素(或者是数组末尾的下一个位置)。 - 结果:它们之间相差的元素个数,类型通常是
ptrdiff_t。
- 前提:
- 注意事项:
- 指针算术主要用于操作数组。
- 避免对非数组元素的指针进行越界算术运算。
- 不能对指针进行乘法、除法、模运算。
- 不同类型的指针(
void*除外,且其不能直接运算)不能直接进行算术运算。
五、void* (泛型指针 / 无类型指针)
-
定义:可以存储任何类型对象的地址,但本身不包含类型信息。
-
特点:
-
通用性:
void *vptr; int i; char c; vptr = &i; vptr = &c; -
不能直接解引用:编译器不知道如何解释所指数据。必须先显式类型转换 (cast)为具体的指针类型。
int i = 10; void *vptr = &i; int val = *( (int*)vptr ); // 转换为 int* 后解引用 -
不能直接进行指针算术 (标准C/C++):因为不知道对象大小。若需运算,先转换。 (GNU C 扩展可能允许,但不推荐,因不可移植)。
-
-
常见用途:
- 泛型函数接口:如
malloc返回void*(分配的内存块可以用于任何类型),free接收void*,memcpy,memset的参数。 - 在不同数据类型的指针间传递地址。
- 泛型函数接口:如
六、多级指针 (Pointers to Pointers)
指针本身也是变量,也存储在内存中,因此也有地址。多级指针就是指向另一个指针地址的指针。
-
二级指针 (
data_type **ptr_to_ptr;):存储一个一级指针的地址。- 示例:
int var = 10; int *p1 = &var; int **p2 = &p1; p2:存储p1的地址。*p2:解引用一次,得到p1的值 (即var的地址)。**p2:解引用两次,得到var的值 (即10)。
- 示例:
-
常见用途:
-
在函数中修改指针本身:如果函数需要改变一个外部指针变量指向的地址(例如,在函数内部分配内存并让外部指针指向它),需要传递该外部指针的地址 (即二级指针) 给函数。
void allocate(int **arr_ptr, int size) { *arr_ptr = new int[size]; // 修改了调用者传入的指针 } // ... int *my_array = nullptr; allocate(&my_array, 10); // 传递 my_array 的地址 // my_array 现在指向新分配的内存 -
动态分配指针数组:例如,管理一个字符串数组 (每个字符串本身是
char*)。char **names = new char*[count]; // names 是一个指向 char* 的指针 names[0] = new char[length]; strcpy(names[0], "Alice"); -
C语言中的
main函数参数char *argv[]或char **argv也是二级指针,argv指向一个指针数组,其中每个指针指向一个命令行参数字符串。
-
七、指针与数组
-
数组名作为指针:在大多数表达式中,数组名会隐式转换为其第一个元素的地址 (指针)。
int arr[5] = {1, 2, 3, 4, 5}; int *ptr = arr; // 等价于 int *ptr = &arr[0]; std::cout << *ptr; // 输出 1 std::cout << *(arr + 1); // 输出 2 (arr[1]) -
区别:
sizeof运算符:sizeof(arr)返回整个数组占用的字节数,而sizeof(ptr)返回指针变量自身占用的字节数 (通常4或8字节)。&运算符:&arr得到的是整个数组的地址,虽然其数值与arr(首元素地址) 相同,但类型不同 (int (*)[5]vsint*)。(&arr + 1)会跳过整个数组。- 数组名是常量指针,不能被赋值修改:
arr = another_array;(错误);ptr = another_array;(正确)。
八、函数指针
-
定义:指向函数的指针,存储函数的入口地址。
-
声明:
return_type (*pointer_name)(parameter_types);- 示例:
int (*add_func_ptr)(int, int);(一个指向返回int并接收两个int参数的函数的指针)
- 示例:
-
赋值与调用:
int add(int a, int b) { return a + b; } add_func_ptr = add; // 或 add_func_ptr = &add; int result = (*add_func_ptr)(10, 20); // 或 result = add_func_ptr(10, 20); -
用途:
- 回调函数:将函数作为参数传递给另一个函数。
- 跳转表/状态机:根据不同条件调用不同函数。
- 实现某些设计模式 (如策略模式)。
九、指针的危险与注意事项
-
野指针 (Wild Pointers):未初始化的指针,或指向已释放/无效内存区域的指针。解引用野指针是灾难性的。
- 避免:声明时初始化 (
nullptr);释放内存后将指针设为nullptr。
- 避免:声明时初始化 (
-
悬垂指针 (Dangling Pointers):指向已经被释放的内存的指针。
- 避免:确保指针在指向的内存释放后不再被使用。
-
内存泄漏 (Memory Leaks):动态分配的内存不再需要但没有被释放,导致程序持续占用不必要的内存。
- 避免:
malloc/new配对使用free/delete(或delete[]for arrays)。使用智能指针 (C++)。
- 避免:
-
访问越界:通过指针算术访问数组边界之外的内存。
-
类型不匹配:通过一个类型的指针去访问另一种类型的数据,可能导致数据损坏或错误解释。

浙公网安备 33010602011771号