C语言指针详解(最清晰、最完整的入门到进阶指南)
指针是 C 语言中最强大、最重要、也最容易出错的核心特性。
一句话总结:指针就是“地址的变量”,它存储的是另一个变量(或内存块)的内存地址,通过指针可以“间接访问”和“操作”那块内存。
下面从零基础到高级,一步步把指针讲透。
1. 指针最基础的概念(必须死记)
inta=10;// 普通变量,值是 10int*p=&a;// p 是一个指针变量,存的是 a 的地址// 三行最核心的理解:printf("%d\n",a);// 输出 10 (变量的值)printf("%p\n",&a);// 输出地址,如 0x7ffdf1234568printf("%p\n",p);// 输出地址,和 &a 相同printf("%d\n",*p);// 输出 10 (通过指针“解引用”得到的值)最关键的三个符号(每天默写 10 遍,直到刻进 DNA)
| 符号 | 含义 | 读法(中文) | 英文说法 |
|---|---|---|---|
| & | 取地址运算符 | “取地址” | address-of operator |
| * | 1. 定义指针类型 2. 解引用(取值) | “指针类型” / “解引用” | pointer declaration / dereference |
| *p | 通过指针 p 访问它指向的内存的值 | “p 指向的值” | value pointed by p |
一句话记忆口诀:
& 是“去哪找”,是“找来干什么”*
2. 指针变量的定义方式(最容易混淆的地方)
inta=10;int*p1=&a;// 推荐写法:* 紧跟变量名int*p2=&a;// 也可以,但容易误导int*p3,q;// p3 是指针,q 是普通 int(最常出错!)int*p4,*p5;// p4 和 p5 都是指针(正确)正确记忆:*是跟变量走的,不是跟类型走的
所以永远写成int *p而不是int* p,这样定义多个变量时才不会出错。
3. 指针的各种常见类型(必须掌握)
| 指针类型 | 定义写法 | 指向的内容大小(步长) | 典型用途 |
|---|---|---|---|
| 指向基本类型 | int *p | sizeof(int) 通常 4 | 操作单个 int 变量 |
| 指向数组 | int arr[10]; int *p = arr; | 同上 | 遍历数组(最常见) |
| 指针数组 | int *arr[10]; | — | 每个元素都是指针 |
| 数组指针 | int (*p)[10]; | 整个数组大小 | 指向整个数组(多维数组常用) |
| 指向函数的指针 | int (*func)(int,int); | — | 回调函数、函数表 |
| void 指针 | void *p; | 无步长(不能 ++) | 通用指针(malloc 返回的就是 void*) |
4. 指针的经典用法(面试 + 实战必会)
4.1 指针遍历数组(最常见写法)
intarr[5]={10,20,30,40,50};int*p=arr;// 数组名就是首元素地址for(inti=0;i<5;i++){printf("%d ",*(p+i));// 方式1:指针 + 偏移// 或printf("%d ",p[i]);// 方式2:像数组一样用 [](本质相同)// 或printf("%d ",*p++);// 方式3:指针自增(最简洁,但注意顺序)}4.2 交换两个变量(不用第三变量)
voidswap(int*a,int*b){inttemp=*a;*a=*b;*b=temp;}// 调用intx=5,y=10;swap(&x,&y);// 必须传地址4.3 动态内存分配(malloc / calloc / realloc / free)
#include<stdlib.h>// 分配一个 int 大小的内存int*p=(int*)malloc(sizeof(int));if(p==NULL){/* 分配失败处理 */}*p=100;// 分配 10 个 int 的数组int*arr=(int*)malloc(10*sizeof(int));// 分配并清零int*arr2=(int*)calloc(10,sizeof(int));// 全初始化为 0// 扩容(很重要!)int*new_arr=(int*)realloc(arr,20*sizeof(int));if(new_arr)arr=new_arr;// 释放(必须成对!)free(p);free(arr);p=arr=NULL;// 防止野指针现代推荐(C99+):用sizeof(*p)而不是sizeof(int)
int*p=malloc(sizeof(*p));// 更安全,类型改了也不用改代码5. 指针最容易犯的致命错误(面试 + 生产必考)
解引用空指针(Segmentation fault)
int*p=NULL;*p=10;// 崩溃!使用后释放(Use After Free)
int*p=malloc(sizeof(int));free(p);*p=20;// 未定义行为!重复释放(Double Free)
free(p);free(p);// 崩溃或内存损坏野指针
int*p;// 未初始化*p=10;// 随机地址,极度危险数组越界
intarr[5];*(arr+10)=999;// 越界写,破坏内存
防御式编程黄金准则:
- 所有指针初始化为 NULL
- free 后立刻置 NULL
- 使用前永远判断 !NULL
- 永远用
sizeof(*p)而不是硬编码类型
6. 面试最常考的指针笔试题(背熟这些基本能过指针关)
指针 + 1 到底加了多少?
int*p;// 加 4(int 4 字节)char*q;// 加 1double*r;// 加 8*p++、(*p)++、++*p、p++区别写一个函数返回两个数的最大值和最小值(用指针)
实现字符串复制(不调用 strcpy)
用指针实现二维数组动态分配
const 指针 vs 指针 const 的四种写法
总结:指针的本质一句话
指针就是地址的变量,通过它可以“隔空操控”内存
C 语言把内存管理权完全交给程序员,所以指针强大,但也最容易出错。
现代 C 项目中,“指针不离手,但别裸奔”—— 能用数组用数组,能用结构体用结构体,指针主要用来做参数传递、动态内存、遍历和底层操作。
想继续深入哪个指针专题?
A. 指针面试经典 20 题详解
B. const 与指针的四种组合(const int*、int* const 等)
C. 指针数组 vs 数组指针完整对比 + 多维数组写法
D. 动态内存分配的完整案例(链表、哈希表)
E. 野指针、悬垂指针、内存泄漏的真实案例分析
告诉我字母,我们继续深挖!