C 语言 指针(Pointer)
C 语言中的 指针(Pointer)是其最强大的特性之一,它允许程序直接操作内存,提高代码的效率和灵活性。指针广泛用于动态内存分配、数据结构(如链表、树)、数组操作、函数参数传递等。本文将从基础到高级全面解析 C 指针,包括底层原理、使用技巧和注意事项。
1. 指针的基本概念
在 C 语言中,指针是一个 变量,它存储 另一个变量的内存地址。指针本身也占用内存(通常为 4 或 8 字节,取决于系统架构)。
1.1 指针的定义
int a = 10;
int *p = &a; // p 存储变量 a 的地址
*p表示 指针解引用(dereferencing),即访问指针指向的值。&a取a的地址。
1.2 指针的基本操作
#include <stdio.h>
int main() {
int a = 10;
int *p = &a;
printf("变量 a 的值: %d\n", a);
printf("变量 a 的地址: %p\n", &a);
printf("指针 p 存储的地址: %p\n", p);
printf("指针 p 指向的值: %d\n", *p);
return 0;
}
示例输出
变量 a 的值: 10
变量 a 的地址: 0x7ffee1b56c44
指针 p 存储的地址: 0x7ffee1b56c44
指针 p 指向的值: 10
2. 指针的类型
指针的类型决定了解引用(*)时读取数据的方式。例如:
int *p1; // 指向 int 类型
char *p2; // 指向 char 类型
double *p3; // 指向 double 类型
注意: 指针本身的大小通常是固定的(32 位系统 4 字节,64 位系统 8 字节),但指向的数据类型不同。
3. 指针运算
3.1 指针加减
指针可以执行加法和减法运算:
int arr[] = {10, 20, 30, 40};
int *p = arr; // 指向数组首元素
printf("%d\n", *(p + 1)); // 访问 arr[1] (20)
printf("%d\n", *(p + 2)); // 访问 arr[2] (30)
原理:
指针 p 增加 1,表示前进 sizeof(int)(4 字节)。
3.2 指针的比较
if (p1 == p2) {
printf("两个指针指向相同地址\n");
}
3.3 指针的自增、自减
p++; // 移动到下一个元素
p--; // 移动到上一个元素
4. 指针与数组
4.1 一维数组与指针
int arr[] = {1, 2, 3, 4};
int *p = arr;
printf("%d\n", p[2]); // 等价于 arr[2],输出 3
arr[i] 等价于 *(arr + i)。
4.2 二维数组与指针
int matrix[2][3] = {{1, 2, 3}, {4, 5, 6}};
int (*p)[3] = matrix; // 指向包含 3 个元素的数组
printf("%d\n", p[1][2]); // 访问 matrix[1][2],输出 6
5. 指针与函数
5.1 作为函数参数
指针可以用于函数参数,以便 修改原始数据。
void modify(int *p) {
*p = 20; // 修改原始值
}
int main() {
int a = 10;
modify(&a);
printf("%d\n", a); // 输出 20
return 0;
}
5.2 返回指针
int* getPointer() {
static int x = 100;
return &x;
}
⚠️ 注意:不要返回局部变量的地址,否则会导致未定义行为。
6. 指针与 malloc() 动态内存分配
#include <stdio.h>
#include <stdlib.h>
int main() {
int *p = (int*) malloc(5 * sizeof(int)); // 分配 5 个 int 空间
if (p == NULL) {
printf("内存分配失败\n");
return 1;
}
for (int i = 0; i < 5; i++) {
p[i] = i * 10;
printf("%d ", p[i]);
}
free(p); // 释放内存
return 0;
}
⚠️ 注意:
- 分配后必须释放
free(p),否则可能会造成 内存泄漏。
7. 指针数组 & 指针指针
7.1 指针数组
int a = 1, b = 2, c = 3;
int *arr[] = {&a, &b, &c};
printf("%d\n", *arr[1]); // 输出 2
指针数组 arr[] 里存储了 a, b, c 的地址。
7.2 二级指针(指向指针的指针)
int x = 10;
int *p = &x;
int **pp = &p;
printf("%d\n", **pp); // 输出 10
8. void * 指针
void * 指针可以指向任何类型:
void *ptr;
int a = 10;
ptr = &a;
printf("%d\n", *(int*)ptr);
9. 指针的常见错误
9.1 悬空指针(Dangling Pointer)
int *p;
{
int x = 10;
p = &x;
} // x 被销毁,p 成为悬空指针
解决方案: 避免返回局部变量地址,或者在 free() 后将指针置 NULL。
9.2 野指针(Uninitialized Pointer)
int *p; // p 未初始化,可能指向垃圾地址
解决方案: 在声明指针时初始化:
int *p = NULL;
10. 总结
| 指针特性 | 说明 |
|---|---|
| 存储地址 | 指针存储变量的地址,而非变量本身 |
* 解引用 | 访问指针指向的数据 |
& 取地址 | 获取变量地址 |
| 指针运算 | 支持 +, -, ++, -- |
malloc/free | 动态内存分配 |
void * | 通用指针,指向任意类型 |