搜索
bottom↓
回复: 8

来聊一下,指针到底是何方神圣?

[复制链接]

出0入234汤圆

发表于 2021-11-4 17:31:17 | 显示全部楼层 |阅读模式
本帖最后由 正点原子 于 2022-1-7 17:40 编辑

以下文章来源于:公众号:开源电子网,读取更多技术文章,请扫码关注

讨论发帖图.png





一篇足以全面理解指针的文章


     什么是指针:
       指针你真正了解吗?那么你懂得计算机内存是如何存储吗,如果你真正了解指针到底是是什么,那么你就会懂得计算机内存是如何存储的。

       首先了解指针之前,我们必须了解计算机内存是如何存储的,一般的32位计算机把内存分割为字节,一般32位的机器int类型为32比特,也就是4字节,一字节有8bit数据,这个必须知道的知识点,如以下图所示:


       一篇足以理解指针到底是何方神圣(1)198.png

       上图可知:一个字节8bit的数据,所以计算机为了区分内存的每一个字节,那么计算器给它们进行排号,而这些号就是地址 ,例如学校的宿舍,每一间的宿舍住了八个人,宿舍必定有一个宿舍号,所以舍管阿姨会对宿舍号进行查找的某宿舍人员的原理类似。

          一篇足以理解指针到底是何方神圣(1)345.png

      上图可知,一般来说int型变量有4字节,所以每一个地址偏移4个字节,如果指针P指向0x20000004地址,那么该地址存储的值就是0x82。

     C语言指针是什么
       上面我们已经了解计算机内存是如何存储以及如何读取地址的值,显然小编是举32位的计算机就是为了方便我们学习MCU,因为MUC大部分也是32位的。

       C语言指针可以简化一些C编程任务执行的任务,例如动态内存分配,如果没有指针那么无法执行的,所以成为C程序员或者嵌入式工程师,学习指针很有必要的。

       数据在内存中的地址也称为指针,如果一个变量存储了一份数据的指针,我们就称它为指针变量。在学习C语言时候,变量都有一个内存位置,那么它们可以使用“&”运算符访问地址,表示它在内存中的一个地址。如以下源码所示:
  1. #include <stdio.h>
  2. int main ()
  3. {
  4.    /* int类型的变量 */
  5.    int  lv_variate = 10;
  6.    /* 指针变量 */
  7.    int  *ip_variate;
  8.    /* 在指针变量存储 lv_variate 地址 */
  9.    ip_variate = &lv_variate;  
  10.    printf("lv_variate 变量的地址: %p\n", &lv_variate  );
  11.    /* 在指针变量中存储的地址 */
  12.    printf("ip_variate 指针变量存储的地址: %p\n", ip_variate );
  13.     /* 在指针变量的地址 */
  14.    printf("ip_variate 指针变量的地址: %p\n", &ip_variate );
  15.    /* 使用指针访问值 */
  16.    printf("*ip_variate 变量的值: %d\n", *ip_variate );
  17.    return 0;
  18. }
复制代码

       串口调式助手输出他们的打印信息,如以下所示:、

  1. lv_variate 变量的地址: 20000784

  2. ip_variate 变量存储的地址: 20000784

  3. ip_variate 指针变量的地址: 20000780

  4. *ip_variate 变量的值: 10
复制代码

        我们怎么理解上述输出的信息呢?,可以使用示意图来解析上述的信息,如图所示:


         一篇足以理解指针到底是何方神圣(1)1340.png

总结:
        ① 变量名、函数名、字符串名和数组名在本质上是一样的,它们都是地址的助记符

        ② “&” 运算符访问地址

        ③ *ip_variate就是读取指针变量存储的地址对应的值(20000784的地址的值10)

        ④ ip_variate读取指针变量存储的地址

     小知识:
       如果程序被编译和链接后,使用 * ip_variate 的话,要先通过地址 20000780取得变量 ip_variate 本身的值,这个值是变量 lv_variate 的地址,然后再通过这个值取得变量 lv_variate 的数据,前后共有两次步奏;而使用 lv_variate 的话,可以通过地址 20000784直接取得它的数据,只需要一步运算。

       可以这样说,使用指针是间接获取数据,使用变量名是直接获取数据。


      什么是二级指针:

        上面我们以及理解什么是指针以及一级指针的操作,如果一个指针指向的是另外一个指针,我们就称它为二级指针,或者指向指针的指针。


        二级指针是指向一级指针的指针。二级指针指向一级指针,也就是二级指针中存储的是一级指针的内存地址。

      二级指针的使用:
  1. int  main( void )

  2. {

  3.      int  a = 10;                            //声明一个变量a

  4.      int  *p = &a;                           //声明指针p,指向变量a

  5.      int  **q = &p;                          //声明二级指针q,指向一级指针p

  6.      printf ( "a = %d\n" ,a);                //打印变量a的值

  7.      printf ( "a的地址&a=%p\n" ,&a);         //打印变量a的地址

  8.      printf ( "p = %p\n" ,p);                //打印p的值

  9.      printf ( "p的地址&p=%p\n" ,&p);         //打印p的地址

  10.      printf ( "p的解引用*p=%d\n" ,*p);       //打印p的解引用

  11.      printf ( "q = %p\n" ,q);                //打印q的值

  12.      printf ( "q的地址&q=%p\n" ,&q);         //打印q的地址

  13.      printf ( "q的解引用*q=%p\n" ,*q);       //打印q的解引用

  14.      printf ( "q的双重解引用**q=%d\n" ,**q); //打印q的双重解引用

  15.      return  0;

  16. }
复制代码

      执行结果:

  1. a = 10

  2. a的地址&a=20000788

  3. p = 20000788

  4. p的地址&p=20000784

  5. p的解引用*p=10

  6. q = 20000784

  7. q的地址&q=20000780

  8. q的解引用*q=20000788

  9. q的双重解引用**q=10
复制代码

        我们怎么理解上述输出的信息呢?,可以使用示意图来解析上述的信息,如图所示:


         一篇足以理解指针到底是何方神圣(1)2691.png


      总结:
        ① 变量名、函数名、字符串名和数组名在本质上是一样的,它们都是地址的助记符

        ② “&” 运算符访问地址

        ③ q读取指针变量存储的地址(0x20000784)

        ④ *q读取指针变量存储的地址的对应的值(就是一级指针变量存储的地址(0x20000788))

        ⑤  **q读取指针变量存储的地址的对应的值的对应地址的值(就是一级指针变量存储的地址(0x20000788)对应的值(10))

出140入158汤圆

发表于 2021-11-4 18:03:19 | 显示全部楼层
指针就是地址,就这么简单

出0入22汤圆

发表于 2021-11-4 19:36:59 | 显示全部楼层
变量名 就是一个宏定义的指针。

出200入2554汤圆

发表于 2021-11-4 19:52:23 | 显示全部楼层
指针是 CPU 间接寻址的高层抽象

出0入37汤圆

发表于 2021-11-4 20:17:04 | 显示全部楼层
本帖最后由 下一页 于 2021-11-4 20:20 编辑

如果是汇编语言,你需要知道你把这个数据放什么位置,还需要知道什么位置空着,这些都得由程序员管理。
如果是高级语言,你完全不关心这事,编译器和操作系统配合,他们决定这个数据放哪,给你个变量名,你随时能找到就行,其他不用你管

c语言是汇编到高级语言的过渡,最早的c程序员是汇编程序员过来的,我们知道,c是编写unix的过程中为了替代汇编提高编码效率写的编译器,大家还不太放心把分配内存的事交给编译器,还想指手画脚,还想干预或者部分干预数据存到什么位置的事情,比如不知道一个数组有多大,不知道要开多大的内存空间放这个数组,而且内存还比较紧张,这时候,用指针。我只需要知道这一串数的起始地址(用一个指针代替,我不再想这个数是啥了,我知道他的地址在这个变量里就行)和这一串数的长度,我就能放任意多的数,也能访问其中任意一个数。这就是指针的历史背景和现实意义。

出0入25汤圆

发表于 2021-11-4 21:12:11 | 显示全部楼层
t3486784401 发表于 2021-11-4 19:52
指针是 CPU 间接寻址的高层抽象

精辟,但理解这个需要有CPU结构基础。

出105入79汤圆

发表于 2021-11-4 23:17:04 | 显示全部楼层
C指针就是装着地址的32位变量。本身是占用空间的。C++又升级了,加入了&引用,可以看作安全版本的指针。

出0入475汤圆

发表于 2021-11-5 10:19:06 来自手机 | 显示全部楼层
哎呀看着这个贴就想学习了,于是有了下面问题:
我想将一个double变量的8个字节放在一个u8数组中的特定位置,比如buf[10]的第三个字节开始的地方,然后输出,即是buf[2]~buf[9],那么该怎么赋值啊,接收端将对应的8个字节恢复为double变量,测试了好几个方式都不对,

出0入475汤圆

发表于 2021-11-5 10:32:28 | 显示全部楼层
double A,u8 buf[10]
试了
memcpy((unsigned char *)&A,buf+2,8); 和 A = *((double *)(&buf)+2);
多种组合(就是强制指针转换的括号那些放不同范围,只要编译不报错)结果还是不对,
最后不得已就这样:
Buf[2+0] = ((unsigned char *)&A)[7];
。。。
Buf[2+1] = ((unsigned char *)&A)[6];
Buf[2+7] = ((unsigned char *)&A)[0];
来发

((unsigned char *)&A)[7]=Buf[2+0];
((unsigned char *)&A)[6]=Buf[2+1];
。。。
((unsigned char *)&A)[1]=Buf[2+6];
((unsigned char *)&A)[0]=Buf[2+7];

来收,这样是对的。可是太累了,肯定有一个我前面那个一条语句搞定的办法吧
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-4-18 20:46

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表