零基础学 C 语言的朋友是不是一听到 “指针” 就头疼?看书上写的 “指针是指向内存地址的变量”,越看越迷糊;好不容易写出指针代码,要么程序直接崩溃,要么输出一堆乱码;听说 “野指针” 是指针里的 “定时炸弹”,却不知道它到底啥样、怎么躲。别着急,兔子哥当年学指针时,就因为没搞懂
*和&的区别,写了个简单的交换函数,结果变量值压根没变化,调试半天才发现指针用反了。今天就带零基础的你从指针概念讲到实战案例,把野指针的坑全列出来,新手学指针,跟着这篇走就对了!一、指针到底是啥?用 “生活化比喻” 轻松懂
基础问题:指针就是 “门牌号”,内存就是 “小区房子”
其实指针没那么抽象,咱们可以把计算机内存想象成一个小区,每个内存单元就是一间房子,每间房子都有唯一的门牌号(内存地址)。变量就像住在房子里的人,而指针,就是记着这个门牌号的小本本。
比如定义
int a = 10;,系统会给a分配一块内存(比如地址是 0x1234),这时候a就住在 “0x1234 号房子” 里。如果定义int *p = &a,这里的p就是指针,&a就是取a的门牌号,所以p就记下了 “0x1234” 这个地址,咱们就说 “p指向a”。*号在这里有两个作用:定义指针时int *p表示 “p是指针”;用指针时*p表示 “按门牌号找到房子里的人”,比如*p = 20;就是通过p记的门牌号,把a的值改成 20。为啥要用指针?这 3 个场景非它不可
- 函数传值改不了外部变量,用指针能改:比如写交换函数,传变量值只能在函数里换,传指针才能真正交换外部变量的值。
- 处理大数组时更高效:传数组首地址(指针)比传整个数组省内存,尤其数组很大的时候。
- 动态内存管理必须用:想在程序运行中申请或释放内存,没指针根本做不到,比如
malloc申请的内存只能用指针访问。
二、指针基本操作:从定义到使用,3 步学会
步骤 1:定义指针 —— 告诉系统 “我要记门牌号”
定义指针的格式很简单:
数据类型 *指针名;,比如:c
int *p; // 定义一个指向int类型的指针pchar *str; // 定义一个指向char类型的指针str注意
*是指针的标志,不能漏!新手常犯的错是写成int* p, q;,以为p和q都是指针,其实只有p是指针,q是普通 int 变量,定义多个指针得写成int *p, *q;。步骤 2:给指针赋值 —— 让指针 “记对门牌号”
指针定义后不能直接用,得先让它指向有效的变量或内存,不然就是野指针。常见的赋值方式有两种:
- 指向已有的变量:
int a = 10; int *p = &a(&a取a的地址) - 指向动态申请的内存:
int *p = (int*)malloc(sizeof(int));(后面讲动态内存时细说)
千万别这么写:
int *p; *p = 10;,这时候p没指向任何有效内存,就是野指针,运行会崩溃!步骤 3:用指针访问变量 ——“按门牌号找房子”
通过指针访问或修改变量值,用
*指针名:c
int a = 10;int *p = &a;printf("a的值:%d\n", *p); // 输出10,*p就是a的值*p = 20; // 通过指针改a的值printf("修改后a的值:%d\n", a); // 输出20这里的
*就像 “按门牌号开门”,通过p记的地址找到a,既能看值也能修改,是不是很简单?三、实战案例:用指针做两个实用小功能
案例 1:指针实现变量交换函数
普通传值的交换函数没法真正交换变量,用指针就能做到:
c
#include // 用指针交换两个变量的值void swap(int *x, int *y) {int temp = *x; // 取x指向的值*x = *y; // 把y指向的值赋给x指向的变量*y = temp; // 把temp的值赋给y指向的变量}int main() {int a = 3, b = 5;printf("交换前:a=%d, b=%d\n", a, b); // 3,5swap(&a, &b); // 传a和b的地址printf("交换后:a=%d, b=%d\n", a, b); // 5,3return 0;}这里
swap函数的参数是指针,接收a和b的地址,通过*x和*y直接修改外部变量,这就是指针的妙用。案例 2:用指针遍历数组
数组名其实就是数组首元素的地址,所以能用指针遍历:
c
#include int main() {int arr[5] = {1,2,3,4,5};int *p = arr; // 数组名arr就是首元素地址,等价于&arr[0]// 用指针遍历数组for (int i = 0; i < 5; i++) {printf("第%d个元素:%d\n", i+1, *(p+i)); // *(p+i)等价于arr[i]}return 0;}p+i表示指针向后移动i个元素的地址,*(p+i)就是对应位置的元素值,比用下标遍历更灵活,在 C 语言里很常用。四、野指针:指针里的 “定时炸弹”,3 个原因 + 避坑技巧
野指针就是指向无效内存的指针,它像定时炸弹,用了可能让程序崩溃、输出乱码,甚至篡改其他数据。新手一定要知道它的成因和避免方法!
| 野指针成因 | 例子 | 避坑技巧 |
|---|---|---|
| 指针未初始化 | int *p; *p = 10; | 定义指针时马上初始化,要么指向变量(int *p = &a),要么设为NULL(int *p = NULL;) |
| 指针指向的内存被释放后未置空 | int *p = (int*)malloc(4); free(p); *p = 10; | free后立刻把指针设为NULL(p = NULL;),NULL指针访问会报错但不会乱改数据 |
| 指针越界访问 | int arr[3] = {1,2,3}; int *p = arr; *(p+5) = 10; | 访问前检查范围,确保不超过数组或申请的内存长度 |
兔子哥提醒:
NULL是系统定义的空指针(地址 0),访问NULL指针程序会明确报错,比野指针的 “暗箱操作” 好调试多了,所以不用的指针一定要设为NULL!五、自问自答:新手学指针常问的 3 个问题
“指针和数组啥关系?数组名是不是指针?”
数组名确实是数组首元素的地址,和指针很像,但不完全一样。比如
int arr[5];,arr等价于&arr[0],所以能当指针用;但指针可以改指向(p++合法),数组名不能改(arr++不合法)。新手可以简单理解为 “数组名是不能移动的指针”,用的时候别试图修改数组名就行。“怎么知道指针指向的地址对不对?调试时看不到啊?”
可以用
printf打印指针地址和指向的值来调试:c
int a = 10;int *p = &a;printf("p的地址:%p,p指向的值:%d\n", p, *p); // %p专门打印地址运行后能看到
p的地址(比如 0x7ffd8b2c)和a的值,这样就能确认指针是不是指向正确的变量了。“指针太难了,有没有办法跳过不学?”
不行哦!指针是 C 语言的灵魂,很多核心功能(动态内存、链表、文件操作)都离不开它。其实指针的逻辑很简单,就是 “记地址→找地址→用地址”,多写几个小例子(比如交换函数、数组遍历),练着练着就懂了,兔子哥身边很多新手都是这么过来的。
结尾心得
指针确实是 C 语言的难点,但只要别被 “指针” 这两个字吓住,用生活化的例子理解概念,多动手写案例,其实没那么难。兔子哥的经验是,学指针时别光看教程,一定要亲手敲代码,从简单的变量访问到数组遍历,再到函数传指针,一步步来;遇到野指针问题别慌,对照避坑技巧检查是不是没初始化、没释放或越界了。指针就像一把钥匙,学会了它,才能打开 C 语言的大门,看到更强大的功能。坚持多练,你会发现指针不仅不难,还很有趣,加油,新手也能学好指针!
版权声明:除非特别标注,否则均为本站原创文章,转载时请以链接形式注明文章出处。
还木有评论哦,快来抢沙发吧~