指针基础:* 和  到底在干什么
指针基础*和到底在干什么本文面向 VS2013 C89 环境零基础讲解指针最核心的两个运算符。目录一、一个让新手崩溃的代码二、内存地址是什么三、 运算符取地址四、* 运算符解引用五、最经典的错误未初始化的指针六、完整实验通过指针修改变量七、* 和 的关系八、总结对比表思考题一、一个让新手崩溃的代码很多人在 VS2013 里第一次写指针会写出这样的代码#includestdio.hintmain(){int*p;/* 声明一个指针变量 */*p10;/* 给指针指向的位置赋值 */printf(%d\n,*p);return0;}按下 F5VS2013 直接弹窗报错0xC0000005: 访问冲突或者输出一个莫名其妙的数字比如-858993460然后你就懵了——“指针不就是用来存地址然后通过*取值的吗我哪一步写错了”问题出在哪你声明了int *p但p里面存的是什么是一个随机值VS2013 Debug 模式下是 0xCCCCCCCC你让*p 10相当于往一个随机的内存地址上写数据——操作系统直接拒绝崩了要理解这个问题必须先搞懂两样东西内存地址和两个运算符与*。二、内存地址是什么先来理解最基础的概念。内存就像一个超大的公寓楼每个房间都有一个唯一的房间号。这个房间号就是内存地址。inta100;这行代码做了两件事在内存中找一间4个字节大小的空房间在房间里放上数字 100这个房间号起始地址是多少可以用运算符取出来。ASCII 内存布局图------------------- | 内存地址 | 变量 ------------------- | 0x0019FF2C | ← 这是 a 的房间号 | [ 100 ] | a 100 | 4 个字节 | -------------------VS2013 里 32 位程序的地址通常是0x0019XXXX这种格式每次运行可能不一样。三、 运算符取地址的作用很简单取一个变量的内存地址。#includestdio.hintmain(){inta100;intb200;printf(a 的值: %d\n,a);printf(a 的地址: %p\n,a);printf(b 的值: %d\n,b);printf(b 的地址: %p\n,b);return0;}运行结果a 的值: 100 a 的地址: 0019FF2C b 的值: 200 b 的地址: 0019FF28代码解析a取到的是变量 a 在内存中的起始地址十六进制格式%p是专门打印地址的格式符注意 a 和 b 的地址相差 4 个字节——因为 int 占 4 字节它们挨着排列流程图运算符的工作过程变量 a值 100a取地址运算得到地址值0x0019FF2C变量 b值 200b取地址运算得到地址值0x0019FF28四、* 运算符解引用如果说是从变量找地址那么*就是反过来——从地址找变量。声明一个指针变量int*p;/* 声明 p 是一个指针变量它可以存一个 int 类型变量的地址 */这里int *p要分两部分理解p是一个变量它存的是地址int *表示这个地址指向的是一个 int 类型的数据看这个完整的代码#includestdio.hintmain(){inta100;int*p;/* 声明一个 int 指针变量 p */pa;/* 把 a 的地址赋值给 p现在 p 指向 a */printf(a 的值: %d\n,a);printf(p 中存的地址: %p\n,p);printf(通过 p 解引用得到的值: %d\n,*p);/* 通过指针修改 a 的值 */*p999;printf(通过 *p 修改后 a 的值: %d\n,a);return0;}运行结果a 的值: 100 p 中存的地址: 0019FF2C 通过 p 解引用得到的值: 100 通过 *p 修改后 a 的值: 999代码解析int *p— 声明指针变量 p它准备存一个 int 变量的地址p a— 把 a 的地址取出来存入 p。现在 p 说“我知道 a 在哪”*p— 解引用意思是到 p 存的地址那里去拿那个房间里的值。因为 p 存的是 a 的地址所以*p得到的就是 a 的值 100*p 999— 通过解引用直接往 p 指向的地址写入 999这等价于a 999打印 a 发现它真的变成了 999——你通过指针绕了一圈修改了原来的变量ASCII 内存关系图变量 a 变量 p指针 ----------- ----------- | 值: 999 |-------| 存的内容 | | 地址: | 指向 | 0019FF2C | | 0019FF2C | | (a的地址) | ----------- ----------- | | p 自己在内存中的地址是 p | (这是另一回事别搞混)这里有两个容易搞混的东西p的值 0019FF2Ca 的地址*p的值 999a 的值p的值 指针 p 自己这个变量在内存中的地址跟 a 无关五、最经典的错误未初始化的指针回到开头那个崩溃的例子现在你应该能看懂了intmain(){int*p;/* p 没有被初始化里面是随机地址0xCCCCCCCC */*p10;/* 往随机地址写 10 → 访问冲突崩溃 */return0;}VS2013 Debug 模式下发生了什么声明 int *p 后p 的内存状态 ------------------- | p 0xCCCCCCCC | ← VS2013 在 Debug 模式填充 0xCC ------------------- 执行 *p 10 时 → CPU 试图往地址 0xCCCCCCCC 写入数据 → 这个地址不属于当前进程 → 操作系统拦截0xC0000005 访问冲突 → 程序崩溃正确做法先让指针指向一个有效的地址#includestdio.hintmain(){inta100;int*pa;/* 初始化指针让它指向 a */*p10;/* 现在安全了p 指向 a等价于 a 10 */printf(a %d\n,a);/* 输出 10 */return0;}六、完整实验通过指针修改变量一个更完整的实验让你直观感受指针如何绕一圈修改变量#includestdio.hintmain(){/* --- 第1组指针基础操作 --- */intx1;inty2;int*ptr;/* 声明指针 */printf( 初始状态 \n);printf(x %d, x %p\n,x,x);printf(y %d, y %p\n,y,y);/* ptr 指向 x */ptrx;printf(\n ptr x 之后 \n);printf(ptr %p, *ptr %d\n,ptr,*ptr);/* 通过 ptr 修改 x */*ptr100;printf(\n 执行 *ptr 100 之后 \n);printf(x %d, *ptr %d\n,x,*ptr);/* ptr 改为指向 y */ptry;*ptr200;printf(\n ptr y; *ptr 200 之后 \n);printf(y %d, *ptr %d\n,y,*ptr);/* --- 第2组验证 和 * 互为逆运算 --- */intz50;printf(\n 和 * 互为逆运算 \n);printf(z %d\n,z);printf(z %p\n,z);printf(*(z) %d\n,*(z));/* 先取 z 的地址再解引用 → 回到 z 的值 */return0;}运行结果 初始状态 x 1, x 0019FF28 y 2, y 0019FF24 ptr x 之后 ptr 0019FF28, *ptr 1 执行 *ptr 100 之后 x 100, *ptr 100 ptr y; *ptr 200 之后 y 200, *ptr 200 和 * 互为逆运算 z 50 z 0019FF1C *(z) 50代码解析指针可以换指向ptr 先指向 x后来又指向 y。指针本身只是一个变量存的内容地址值可以随时改通过指针改值*ptr 100等价于给 ptr 当前指向的变量赋值*(z)等于 z先取 z 的地址再解引用回到 z 的值——验证了和*是一对逆运算流程图ptr 指向变化过程ptr指向yptr指向x初始ptr x*ptr 100ptr y*ptr 200int x 1地址: 0019FF28int y 2地址: 0019FF24int *ptr未初始化int x 1int y 2ptr -- x*ptr 100 修改xint x 100int y 2ptr -- y*ptr 200 修改y七、* 和 的关系这两个运算符的关系可以用一句话总结是从变量找地址*是从地址找变量。它们互为逆运算。用数学来类比就很好理解了变量 a 的值是 100地址是 0x0019FF2C a 0x0019FF2C —— 取地址 *(a) 100 —— 先取地址再解引用回到原值 (*p) p 存的地址 —— 先解引用再取地址...等等这个要小心关于(*p)如果 p 存的是 a 的地址那么*p得到 a 的值比如 100(*p)取的是 a 这个临时结果的地址——其实就是 p 本身存的那个地址。核心记忆表表达式含义结果类型a变量 a 的值inta变量 a 的地址int*指向 int 的指针p指针 p 存的值即它指向的地址int**p指针 p 指向的那个变量intp指针 p 自己的地址int**指向指针的指针八、总结对比表概念运算符*运算符名称取地址运算符解引用间接访问运算符作用获取变量的内存地址访问指针指向的内存中的数据语法变量名*指针变量返回值一个地址值指针指针指向的那个变量本身逆运算*(x) x(*p) p常见错误无很简单解引用未初始化的指针 → 崩溃指针操作三步走声明指针int *p;让指针指向有效地址p a;通过指针操作数据*p 新值;千万记住第二步不能省略不指向有效地址就解引用等于往随机地址写数据——VS2013 会直接报0xC0000005。思考题如果写出下面的代码会发生什么在 VS2013 里试试看#includestdio.hintmain(){inta1,b2;int*p;pa;*p*p10;pb;*pa*p;printf(a %d, b %d\n,a,b);return0;}提示在 VS2013 的监视窗口添加p和a和b可以实时看到指针 p 的指向变化。动手试一下标签#C语言#指针#内存地址#VS2013#C语言入门#调试技巧

相关新闻