搜索
bottom↓
回复: 142
打印 上一主题 下一主题

[微知识]求求你,不要再纠结指针了……(1)

  [复制链接]

出0入296汤圆

跳转到指定楼层
1
发表于 2015-12-29 15:29:51 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
本帖最后由 Gorgon_Meducer 于 2015-12-31 00:41 编辑


说在前面的话

不得不说,看了太多的人在各种地方讨论指针……越发看下去,越发觉得简单的事情被
搞那么复杂,真是够了,求求你们,放开那个变量,让我来!

以下讨论均以ARM环境下使用C语言进行嵌入式软件开发为背景。感谢网友的批评指正



1、从变量的三要素开始谈起
  为了把复杂的事情说简单,我们抛开指针先从变量谈起。(好吧,不知道这个笑话是
不是够冷)一个变量(Variable),或者顺便兼容下面向对象(OO)的概念,我们统一
称为对象(Object),除了保存于其中的内容以外,只有三个要素:

1)由一定宽度无符号整数(Unsigned Integer)所表示的地址“数值”(Address Value)
2)对象的大小(Size)
3)可对该对象适用的“方法”(Method)和“运算”(Operation)

其中,我们习惯于把后两者合并在一起称之为,变量的"类型"。

> 地址数值(Address Value)
  地址的数值是一个无符号整数,其位宽由CPU的地址总线宽度所决定。话虽如此,其实
主要还是编译器在权衡了“用户编写代码的便利性”以及“生成机器码的效率”后为我们提供的
解决方案:例如,针对8位机,编译器普遍以等效为uint16_t的整数来保存地址信息;针对
16位机和32位机,编译器则普遍选择uint32_t的整数来保存地址信息;针对64位机,编译
器则可能会提供两种指针类型,分别用来对应uint32_t的4G地址空间和由uint64_t所代表的
恐怖地址空间……
  提问,8086有20根地址线,请问用哪种整形来表示其地址呢?(uint16_t、uint32_t
还是uint20_t)——由于uint20_t并不存在,也并不适合CPU进行地址运算,所以统一用
uint32_t来表示最为方便。

  总而言之,地址的数值是一个无符号整数。知道这个有什么用呢?我们待一会再说。这
里我们需要强调一句废话:地址的数值既然是整数,那么它就可以用另外的变量(类型合适
的整形变量或者指针变量)进行保存——任何指针变量,其本质,首先是一个无符号整形变
量。任何指针常量,其本质首先是一个无符号整数。
  请一定要记住(重要的事情说三遍):

  变量的三要素中,仅有地址值有可能会占用物理存储空间
  变量的三要素中,仅有地址值有可能会占用物理存储空间
  变量的三要素中,仅有地址值有可能会占用物理存储空间


> 大小(Size)
  如果仅从变量的大小来看整个计算机世界,就好像一副彩色图片被二值化了,到处是
Memory Block,他们的尺寸通常是1个字节、2个字节、4个字节、8个字节、16个字节或者由
他们组合而成的长度各异Block。这些Block通常被编译器在代码生成的时候对其到地址的宽度
上,比如地址宽度是32bit的,就对齐到4字节,地址宽度是16bit的,就对齐到2字节……
  如果你习惯于使用汇编语言来进行开发,你一定能体会我所描述的这种感觉。这些你统统
都可以忘记,但有一点绝对要记住(重要的事情说三遍):

  变量的三要素中,大小值不会额外占用物理存储空间。
  变量的三要素中,大小值不会额外占用物理存储空间。
  变量的三要素中,大小值不会额外占用物理存储空间。

注意:地址的大小信息描述的是这个变量占用几个字节,这里说大小信息并不占用物理存储器
空间,并不是说,变量中保存的内容不占用存储器空间。请注意区别。

  C语言中,可以用sizeof( )来获取一个变量的大小。前面我们说过,指针首先是一个整形变
量,那么容易知道:

  1.   uint8_t *pchObj;
  2.   uint16_t *phwObj;
  3.   uint32_t *pwObj;
复制代码

sizeof(pchObj) 、sizeof(phwObj)、sizeof(pwObj)以及sizeof任意其它指针的结果都是一样的,
都是当前系统保存地址数值的整形变量的宽度。对32位机来说,这个数值就是4——因为,
sizeof( ) 求的是括号内变量的宽度,而指针变量首先是一个整形变量!同一CPU中同一寻址能力
的指针,其宽度是一样一样一样的!

> 适用的方法(Method)和运算(Operation)
  对面向对象中的对象来说,方法就是该对象类中描述的各种成员函数(Method);
  对数据结构中的各类抽象数据类型(ADT,Abstract Data Type)来说,就是各类针对该数
据类型的操作函数,比如链表的添加(Add)、插入(Insert)、删除(Delete)、和查找
(Search)操作;比如队列对象的入队(enqueue)、出队(Dequeue)函数;比如栈对象的
入栈(PUSH)、出栈(POP)等等……
  对普通数值类的变量来说,就是所适用的各类运算,比如针对 int的四则运算(+、-、*、
/、>、<、==、!=...)。你不能对float型的数据进行移位操作,为什么呢?因为不同的类型拥
有不同的适用方法和运算。
  也许你已经猜到了,类型所适用的方法和运算也不会占用物理存储空间。由于变量的“大小
信息”和“适用的方法和运算信息”统称为“类型(Type)信息”,我们可以简化为:

  变量的三要素中,类型信息从不会额外占用物理存储空间。
  变量的三要素中,类型信息从不会额外占用物理存储空间。
  变量的三要素中,类型信息从不会额外占用物理存储空间。

2、化繁为简的威力
  前面说了那么多,实际上可以简化为下面的等式:

  Variable = Address Value + Type Info
  变量 = 地址数值 + 类型信息


  其中,地址数值的保存、表达和运算是(有可能)实实在在需要占用物理存储器空间的
(RAM和ROM);而类型信息则是编译器专用的——仅仅在编译时刻会用到,用来为编译器语
法检测和生成代码提供信息的——话句话说,你只需要知道,类型信息是一个逻辑上的信息,
是虚的
,在最终生成的程序中并不占用任何存储器空间。你也可以理解为,类型信息最终以程
序行为的方式体现在代码中,而并不占用任何额外的数据存储器空间


  既然知道了变量的本质,我们就可以随心所欲了,比如,我们可以随意创建一个全局变量:

  1.       #define s_wMyVariable    (* (( uint32_t *) 0x12345678))
复制代码

  s_wMyVariable是一个 uint32_t类型的全局变量,它的地址是0x12345678。它和我们通过
普通方式生成的全局变量使用起来没有任何区别——当然,它是个黑户,简单说就是它所占用的
空间是非法的,无证的,在编译器的户口本看来,这块空地上什么都没有,因此它仍然会将
0x12345678开始的4个字节用作其它目的。
  一方面,是不是突然觉得手上拥有了神一般的权利?其实,这种方法非常常用,MCU的寄存
器就是这么定义的,例如:

  1.   #define CONTROL      (*(volatile uint32_t *) CONTROL_BASE_ADDR)
复制代码

我们可以将上述定义全局变量的方法提炼成所谓的全局变量公式:

  1. #define <全局变量的名称>    ( *(  <全局变量的类型>  * )  <全局变量的地址>  )
复制代码

甚至,我们干脆定义一个宏来替我们批量生产全局变量:

  1.   #define __VAR( __TYPE,  __ADDR  )        (  *(  __TYPE *  ) (__ADDR)  )
复制代码

使用起来也很方便,例如:

  1.   __VAR(  float, 0x20004000  ) = 3.1415926;
复制代码

  总结来说:只要给我一个整数,我就可以把它变成任何类型的全局变量!你可以的!我看
好你哦。

3、万能类型转换
  只要你牢记了那句话:给我一个整数,我就能翘起地球,那么我们就可以用它玩出更好玩
的东西。

首先,整数从何而来呢?除了前面的直接使用常数以外,当然还可以从整形变量中来,例如,
前面的例子可以简单的改写成:

  1.   uint32_t wTemp = 0x20004000;
  2.   __VAR(  float, wTemp  ) = 3.1415926;
复制代码

毫无压力!整数还可以从指针中来,例如:

  1.   //!我们定义一个全局变量 wDemo,其地址是0x20004000
  2.   #define wDemo    (*(uint32_t *) 0x20004000   )
  3.   uint32_t *pwSrc = &wDemo;                //!< 获取wDemo的地址
  4.   
  5.   //!< 获取指针保存的地址数值,并用普通整形变量保存下来
  6.   uint32_t wTemp = (uint32_t) pwSrc;   

  7.   __VAR(  float, wTemp  ) = 3.1415926;
复制代码

是不是觉得wTemp有点多余?因此我们可以直接写成:

  1.   //!我们定义一个全局变量 wDemo,其地址是0x20004000
  2.   #define wDemo    (*(uint32_t *) 0x20004000   )
  3.   uint32_t *pwSrc = &wDemo;                //!< 获取wDemo的地址

  4.   __VAR(  float, (uint32_t) pwSrc  ) = 3.1415926;
复制代码

是不是pwSrc也多余了?好,我们继续来:

  1.   //!我们定义一个全局变量 wDemo,其地址是0x20004000
  2.   #define wDemo    (*(uint32_t *) 0x20004000   )
  3.   __VAR(  float, (uint32_t) &wDemo  ) = 3.1415926;
复制代码

当然,如果这个时候你说直接填 0x20004000 不就行了,要么你已经懂了,要么你还糊涂着,
仔细想想:

> 如果wDemo是任意由编译器生成的对象(变量),意味着什么呢?(前面说过,作为全
   局变量,我们土法制造的和compiler原装的用起来没有任何区别)
> 如果我们有任意的指针,我们需要对指针指向的类型进行转换(转换后才好操作),应该
   怎么办?

接下来,我们很容易根据前面的讨论,得出第二个万能公式,可以将任意变量(或地址)转
换成我们想要的类型:

  1.   #define CONVERT(  __ADDR,   __TYPE )   __VAR(  (__TYPE),  (uint32_t) (__ADDR)  )
复制代码

例如,我们可以直接将字节数组中某一段内容截取出来,当做某种类型的变量来访问:


  1.   //! 某数据帧解析函数
  2.   void command_handler(  uint8_t *pchStream, uint16_t hwLength  )
  3.   {
  4.     // offset 0, uint16_t  
  5.     uint16_t hwID = CONVERT( pchStream, uint16_t);  

  6.     // offset 4, float
  7.     float fReceivedValue = CONVERT( &pchStream[ 4 ], float ) ;
  8.     ...
  9.   }
复制代码


3、请忘记指针
  如果你是一个指针苦手,那么请忘记之前所学的一切。记住一句话:指针只是一个用法
怪异的整形变量,专门用来保存变量的地址数值
。指针的类型都是用来欺骗编译器的,我是
聪明的人类,我操纵类型,我不是愚蠢的编译器。

  推论:因为指针变量的本质是整形变量,所以指向指针的指针,只不过是一个指向普通
               整形变量的普通指针,因此指向指针的指针并不存在——世界上只存在普通指针
               ——世界上只存在用法怪异的整形变量,专门用来保存目标变量的地址数值。
  推论:世界上并不存在指向指针的指针的指针的指针……

  给我一个整数,我自己造自己的变量。
  指针的数值运算太坑?转换成整数,加减乘除,随便整。

  哈哈哈哈哈哈……

4、小结

地址:所谓地址就是一个整形的数值(常数)。地址不包含任何类型信息
指针:指针分为指针常量和指针变量,单独说指针的时候,通常指指针常量。其中:
指针常量 = 地址数值(常数)+ 类型信息
指针变量 = 整形变量 + 类型信息

变量 = (* 指针)
指针 = &变量

类型信息可以通过强制类型转换来实现,也就是大家熟悉的  (<Type>) 用法。
地址数值的改变,则统一转化为普通整数以后再说。

指针常量 = 整数常量 + 类型信息      
也就是:
指针常量 = (<类型信息> *)整数 常量

反过来也成立:

整数常数 = 指针常量 - 类型信息
也就是:
整数常数 = (unsigned int)指针常量      //! 仅对32位机使用unsigned int

同理,可以获得整形变量和指针之间的转换关系,这里就不一一列举了。

怎么样,事情是不是变得简单了?哪有什么指针,哪有那么多麻烦事情?统统都是整数。
下回我们将一起来捅一个马蜂窝。哈哈哈哈哈


说在后面的话:

  其实,每次看到一群人热热闹闹的谈论指针,我心里真实的想法是:这么简单的事
情被你们搞这么复杂,我要让你们见识见识,什么叫把简单的事情搞复杂,把复杂的事
情搞成课题,把课题搞成疑难杂症,把疑难杂症搞成科幻片……哈哈哈哈哈哈哈

  为了您的已经吃下去的食物,请自动无视以上内容。

______________未完待续______________

如果你喜欢我的思维,除了在阿莫论坛关注我,您也可以订阅公众号  裸机思维
所有内容原创,严禁任何形式的转载。

阿莫论坛20周年了!感谢大家的支持与爱护!!

一只鸟敢站在脆弱的枝条上歇脚,它依仗的不是枝条不会断,而是自己有翅膀,会飞。

出0入4汤圆

2
发表于 2015-12-29 15:35:39 | 只看该作者
学习咯,我一直把指针认为是地址

出0入0汤圆

3
发表于 2015-12-29 15:37:51 | 只看该作者
学习         

出0入0汤圆

4
发表于 2015-12-29 15:52:44 来自手机 | 只看该作者
感谢分享,学习了!

出0入0汤圆

5
发表于 2015-12-29 15:53:07 | 只看该作者
学习了,很不错~

出0入10汤圆

6
发表于 2015-12-29 15:55:27 | 只看该作者
我一直也觉得指针很简单。
变量的三要素中,大小值从不会占用物理存储空间——大小信息实际上存放在操作的指令中;
“s_wMyVariable是一个 uint32_t类型的全局变量,它的地址是0x12345678。它和我们通过 普通方式生成的全局变量使用起来没有任何区别——当然,它是个黑户,简单说就是它所占用的 空间是非法的,无证的,在编译器的户口本看来,这块空地上什么都没有,因此它仍然会将 0x12345678开始的4个字节用作其它目的。”——这段写得不错,黑户可能导致程序奔溃啊,茫茫内存中,随随便便开户可比现实中要恐怖得多。

出0入8汤圆

7
发表于 2015-12-29 16:06:38 | 只看该作者
顶!……类似大侠说的这种讨论实在太多了,什么指针的好坏啊,要不要上 OS 啊,用 C  还是用汇编啊,啪啪啪的啊,每月都会有几那么次的……我觉得就当大家是闲着鸟没事,扯淡玩呢

出0入0汤圆

8
发表于 2015-12-29 16:08:03 | 只看该作者
对象的类型信息会从指令流中反映出来。所以,类型信息不占用空间这个观点是不太稳固的;不单独占用空间的说法相对可靠一点。

出0入0汤圆

9
发表于 2015-12-29 16:17:35 | 只看该作者
【指针】有点高深 留下了 慢慢体会

出0入0汤圆

10
发表于 2015-12-29 16:31:29 | 只看该作者
学习一下,感谢分享。

出0入296汤圆

11
 楼主| 发表于 2015-12-29 16:32:35 | 只看该作者
本帖最后由 Gorgon_Meducer 于 2015-12-29 16:35 编辑
dr2001 发表于 2015-12-29 16:08
对象的类型信息会从指令流中反映出来。所以,类型信息不占用空间这个观点是不太稳固的;不单独占用空间的说 ...


是的,类型信息决定了生成代码的行为,这个在文章里面已经说了,但是对大部分人来说
简单粗暴的,才是容易记忆的。

已经根据你的意见作了内容上的更新。

出0入0汤圆

12
发表于 2015-12-29 16:33:27 | 只看该作者
大师好久不见!

出0入296汤圆

13
 楼主| 发表于 2015-12-29 16:35:31 | 只看该作者

好久不见。最近可好?

出0入0汤圆

14
发表于 2015-12-29 16:43:19 | 只看该作者
感谢分享,学习了!

出0入8汤圆

15
发表于 2015-12-29 16:47:34 | 只看该作者
微信已关注大师!!

出0入42汤圆

16
发表于 2015-12-29 16:49:11 | 只看该作者
高屋建瓴!

出0入25汤圆

17
发表于 2015-12-29 16:50:36 | 只看该作者
学下汇编和指令集就知道指针了

变量就是直接寻址,MOV R1, 1234       把内存中地址1234处的数据取出来放到R1中
指针就是间接寻址,MOV R1,[1234]     把内存中地址1234处的数据取出来,假设取出来的数2345,把内存中地址2345处的数据取出来放到R1中

出0入0汤圆

18
发表于 2015-12-29 16:53:09 | 只看该作者
不错,对于指针是又爱又恨啊

出10入46汤圆

19
发表于 2015-12-29 17:02:42 | 只看该作者


看到,讨论指针的指针的指针的指针... ... 就头大,也只有中国的教授和学生才懂了。
简单点,就能解决的问题,整那么复杂... ..

出0入0汤圆

20
发表于 2015-12-29 17:03:58 | 只看该作者
好贴,支持!学习一下

出0入296汤圆

21
 楼主| 发表于 2015-12-29 17:17:35 | 只看该作者
XIVN1987 发表于 2015-12-29 16:50
学下汇编和指令集就知道指针了

变量就是直接寻址,MOV R1, 1234       把内存中地址1234处的数据取出来放 ...

是啊,用过汇编就能理解的很深刻了。可惜的是,现在很多人入门的时候就是C语言。
很多技术细节都被抽象的概念所取代了。我试图通过一定的讲解让这些人有机会不
接触汇编的情况下就能抓住一些必要的本质信息。

出0入296汤圆

22
 楼主| 发表于 2015-12-29 17:18:07 | 只看该作者
不舍的六年 发表于 2015-12-29 16:47
微信已关注大师!!

多谢支持哈。里面还有很多好文,可以通过关键字来查看。

出0入0汤圆

23
发表于 2015-12-29 17:25:37 | 只看该作者
大师好久不见了!
思维总是那么独特啊, "听君一席话,胜读十年书!"

出0入0汤圆

24
发表于 2015-12-29 17:28:13 | 只看该作者
大师的公众号说明 还有错别字呢!

出0入0汤圆

25
发表于 2015-12-29 17:28:35 | 只看该作者
有了指针或有了能通过地址访问内存的手段,C才成为了真正的C

出0入0汤圆

26
发表于 2015-12-29 17:38:49 | 只看该作者
Gorgon_Meducer 发表于 2015-12-29 16:35
好久不见。最近可好?

虽然是电工,不码代码!但喜欢大师的思维方式与表达方式,粉你好多年了

出0入296汤圆

27
 楼主| 发表于 2015-12-29 17:44:32 | 只看该作者
bondxie3 发表于 2015-12-29 17:28
大师的公众号说明 还有错别字呢!

纳尼?哪里?

出0入296汤圆

28
 楼主| 发表于 2015-12-29 17:44:48 | 只看该作者
ZY_Hong 发表于 2015-12-29 17:38
虽然是电工,不码代码!但喜欢大师的思维方式与表达方式,粉你好多年了  ...

求继续粉!

出0入0汤圆

29
发表于 2015-12-29 18:21:45 | 只看该作者
随便搜一下  linux内核  双链表, 看懂了里面最核心的两行代码,就真正领悟到“指针”了

出0入0汤圆

30
发表于 2015-12-29 18:22:39 来自手机 | 只看该作者
我也觉得,很多东西多年前看不懂,就是那些说得人带上太多专业术语以及套加概括,搞得很高端,其实自己好好使用一下,后面的全部都通了

出0入0汤圆

31
发表于 2015-12-29 18:25:54 | 只看该作者
已粉                  

出0入0汤圆

32
发表于 2015-12-29 18:46:37 | 只看该作者
裸机思维 取得好

出0入296汤圆

33
 楼主| 发表于 2015-12-29 18:51:40 来自手机 | 只看该作者
dhbighead 发表于 2015-12-29 18:46
裸机思维 取得好

是啊,用了双关

出0入0汤圆

34
发表于 2015-12-29 19:05:36 | 只看该作者
微信已关注,从多级菜单开始认识的大师

出0入0汤圆

35
发表于 2015-12-29 19:20:01 | 只看该作者
学C之前学过一点点汇编, 所以指针也没算太怎么理解.
之前纠结过为变量类型的问题, 一个变量的位宽是固定的, 那它的类型信息保存在那里呢? 后来看过一点编译原理后算是彻底明白了, 类型信息在编译时存在于符号表中, 编译完成后就丢掉了, 如上面那位大师所说, 它会在指令流中表现出来, 指令流生成的依据之一就是符号表嘛.
另外你放个微信二维码嘛, 一扫就行.

出0入0汤圆

36
发表于 2015-12-29 19:32:37 | 只看该作者
学习,谢谢楼主分享:)

出0入296汤圆

37
 楼主| 发表于 2015-12-29 19:39:16 | 只看该作者
DevLabs 发表于 2015-12-29 19:20
学C之前学过一点点汇编, 所以指针也没算太怎么理解.
之前纠结过为变量类型的问题, 一个变量的位宽是固定的 ...

二维码会过期的。你的新路历程也是我的新路历程,我也是这么过来滴。

出50入10汤圆

38
发表于 2015-12-29 19:47:17 来自手机 | 只看该作者
不学汇编的后果!就是想用人类的思维去控制机器。把自己越搞越复杂。

出0入76汤圆

39
发表于 2015-12-29 19:54:13 | 只看该作者
本帖最后由 foxpro2005 于 2015-12-29 20:09 编辑

感觉这句话,有些复杂化了:
“变量的三要素中,仅有地址值有可能会占用物理存储空间。”

想表达的意思是?
如果一旦把这个变量的地址值赋给了其它变量(普通变量 或 指针变量)那么就占用物理存储空间。 ?

出0入296汤圆

40
 楼主| 发表于 2015-12-29 20:02:21 来自手机 | 只看该作者
foxpro2005 发表于 2015-12-29 19:54
感觉这句话,有些复杂化了:
“变量的三要素中,仅有地址值有可能会占用物理存储空间。”


对啊。因为装这个值的变量要占空间啊

出0入0汤圆

41
发表于 2015-12-29 20:07:18 | 只看该作者
写的精简  复杂的事情简单化了

出110入0汤圆

42
发表于 2015-12-29 20:11:40 | 只看该作者
傻孩子出品,必属精品,顶贴之后再慢慢看……

出0入0汤圆

43
发表于 2015-12-29 20:19:08 | 只看该作者
大师有公众号拉,赶紧粉,每次看大师的讲解总让人影响深刻!通俗易懂呀!

出0入0汤圆

44
发表于 2015-12-29 20:22:31 | 只看该作者
已加关注

出0入0汤圆

45
发表于 2015-12-29 20:24:21 | 只看该作者
精华帖子,前排就座。

出0入50汤圆

46
发表于 2015-12-29 20:35:18 | 只看该作者
又见傻孩子大作,不顶不行

出0入0汤圆

47
发表于 2015-12-29 20:38:20 | 只看该作者
已关注公众号

出0入0汤圆

48
发表于 2015-12-29 20:47:17 | 只看该作者
学习了,谢谢LZ。

出20入0汤圆

49
发表于 2015-12-29 21:24:11 | 只看该作者
  嗯,其实指针就是地址加上附带的呈现方法。

出0入0汤圆

50
发表于 2015-12-29 21:35:52 来自手机 | 只看该作者
本帖最后由 myxiaonia 于 2015-12-30 10:46 编辑

linux大神有个用二级指针比较链表节点大小的,很好玩

昨天手机更新的,更正一下,是使用二级指针删除链表节点

出0入0汤圆

51
发表于 2015-12-29 21:44:43 | 只看该作者
好像公众号刚开始弄吧

出85入85汤圆

52
发表于 2015-12-29 21:58:22 | 只看该作者
收藏了。

出0入0汤圆

53
发表于 2015-12-29 22:04:43 | 只看该作者
本帖最后由 knight_avr 于 2015-12-29 22:10 编辑

我看很多人不会指针,就是 没有领悟LZ的思路。
同时很多领悟的人又不能很好的把指针形象的表达出来让不会的来学。这叫作只可意会,不能言传。就像LZ 本来是想简单给人介绍一下指针的实际,可越写越复杂了,还深入地址总线去了(虽然确实和地址总线有息息相关),让不懂的人越发看不懂。懂的人基本不用看。
半懂不懂的人倒是可以深入学习一下,就看其悟性和人品怎样了。

其实很简单:指针、变量、值、寄存器的关系就和其定义一样:指针就是寄存器的地址编号,变量就是给此地址编号的寄存器取个名称,值就是此地址编号的寄存器里面的数据。

形象一点就是: 寄存器比作---箩筐、 那么 指针---箩筐的编号、变量---箩筐的名称、值---就是箩筐里面的苹果的个数
比如:
    获取变量的值--------名称为“ADCVALUE”箩筐中苹果有几个
   使用指针获取值 ------ 告诉我第 n? 好箩筐有几个苹果
   只用指针变量取值 ----- 先告诉我名称为"pADCVAL"箩筐中有9个苹果, 然后到第9个框中数一下有几个苹果
  指针的指针的指针变量取值 ---- 如上,也就是多跳几下而已。


总的来说,任何数据(包括变量指针、数据、函数指针)都需要寄存器储存,都是数据,就看你是把它当数据、数据指针、还是当函数指针看待了。
C语言的强制转换,非常贴切数据的本质(这也是C语言所以千年不老的原因),强制转换不会产生任何多余的代码,只是告诉编译器正确使用此数据而已。

出0入0汤圆

54
发表于 2015-12-29 22:08:46 来自手机 | 只看该作者
前来顶帖。。。

出0入0汤圆

55
发表于 2015-12-29 22:20:43 | 只看该作者
关注公众号

出0入0汤圆

56
发表于 2015-12-29 22:22:11 | 只看该作者
总结的很好,谢谢分享

出90入0汤圆

57
发表于 2015-12-29 22:27:07 | 只看该作者
微信已关注~

出0入0汤圆

58
发表于 2015-12-29 22:43:42 | 只看该作者
应该要好好理解一下 对指针的操作不是很熟练!

出0入296汤圆

59
 楼主| 发表于 2015-12-29 23:25:38 | 只看该作者
knight_avr 发表于 2015-12-29 22:04
我看很多人不会指针,就是 没有领悟LZ的思路。
同时很多领悟的人又不能很好的把指针形象的表达出来让不会的 ...

其实我想说,你这是经典的比喻,但是一个容易让人半懂不懂的比喻,
因为,箩筐或者是房子是装东西的,门牌号之类的是地址值,这都懂
但房间或者箩筐里装的东西是什么就很模糊。容易让人觉得是某种类
型的数据,比如float型,那么类型信息是不是也保存在里面?这个就
没讲清楚,也容易让人疑惑。说到这里也许你还觉查不到多少差异,
等后面我介绍多维数组的编译逻辑变换的时候,房屋或者是箩筐的比
喻就没法解释清楚了。另外地址总线宽度是逃不开的,无论你用何种
方式进行解释。这是必须要介绍的。而且,专门在地址值里介绍,并
不会显得跑题或者把事情弄复杂了。我的目的是让人对这个问题有定
性定量的本质性认识,而不是一个帮助理解的模型。所以,必须深入
的细节,是逃不开,而且不能逃开的。

出0入0汤圆

60
发表于 2015-12-29 23:33:17 | 只看该作者
谢谢大神指点!!
请问你现在从事神马工作?

出0入296汤圆

61
 楼主| 发表于 2015-12-29 23:39:40 | 只看该作者
calt1987 发表于 2015-12-29 23:33
谢谢大神指点!!
请问你现在从事神马工作?

Cortex- M 和 KEIL技术支持

出0入0汤圆

62
发表于 2015-12-29 23:56:26 | 只看该作者
从没觉得指针复杂,指针就是个地址值,根本不可能复杂

倒是楼主写了这么多,反倒是给搞复杂了 ......

另外楼主举的8086这个例子 ....... 其实如果不是很熟悉的话,你可以换成别的

出15入118汤圆

63
发表于 2015-12-30 00:03:27 | 只看该作者
mark下 等待继续

出0入0汤圆

64
发表于 2015-12-30 00:25:53 | 只看该作者
感谢分享,学习了!

出0入0汤圆

65
发表于 2015-12-30 01:03:19 | 只看该作者
赞一个!!!!!!

出0入296汤圆

66
 楼主| 发表于 2015-12-30 01:46:38 | 只看该作者
gamalot 发表于 2015-12-29 23:56
从没觉得指针复杂,指针就是个地址值,根本不可能复杂

倒是楼主写了这么多,反倒是给搞复杂了 ......

哈哈,会者不难,说明你早就烂熟于胸了。我还要考虑另外一部分不太理解的指针的嘛。

出0入0汤圆

67
发表于 2015-12-30 08:28:56 | 只看该作者

傻孩子图'示'工作室.

出0入58汤圆

68
发表于 2015-12-30 09:13:11 | 只看该作者
纠错别字来了,“而指针变量首先是一个整形变量!”

出0入0汤圆

69
发表于 2015-12-30 09:27:23 | 只看该作者
感觉楼主越说越复杂。

对我来说,指针,就是存储其他常变量地址的常变量。 能改变就是变量,不能改变就是常量。

出0入0汤圆

70
发表于 2015-12-30 10:59:44 | 只看该作者
二级指针算是指针用法里比较精巧的,而且也是比较实用的技巧,linus大神称之为core low-level coding

固定大小内存管理就是一个典型的二级指针用法,代码相当紧凑干脆,我给大家贴上rtx中membox源码,一起体味下个中奥秘

  1. void *rt_alloc_box (void *box_mem) {
  2.   /* Allocate a memory block and return start address. */
  3.   void **free;
  4. #ifndef __USE_EXCLUSIVE_ACCESS
  5.   int  irq_dis;

  6.   irq_dis = __disable_irq ();
  7.   free = ((P_BM) box_mem)->free;
  8.   if (free) {
  9.     ((P_BM) box_mem)->free = *free;
  10.   }
  11.   if (!irq_dis) __enable_irq ();
  12. #else
  13.   do {
  14.     if ((free = (void **)__ldrex(&((P_BM) box_mem)->free)) == 0) {
  15.       __clrex();
  16.       break;
  17.     }
  18.   } while (__strex((U32)*free, &((P_BM) box_mem)->free));
  19. #endif
  20.   return (free);
  21. }

  22. int rt_free_box (void *box_mem, void *box) {
  23.   /* Free a memory block, returns 0 if OK, 1 if box does not belong to box_mem */
  24. #ifndef __USE_EXCLUSIVE_ACCESS
  25.   int irq_dis;
  26. #endif

  27.   if (box < box_mem || box >= ((P_BM) box_mem)->end) {
  28.     return (1);
  29.   }

  30. #ifndef __USE_EXCLUSIVE_ACCESS
  31.   irq_dis = __disable_irq ();
  32.   *((void **)box) = ((P_BM) box_mem)->free;
  33.   ((P_BM) box_mem)->free = box;
  34.   if (!irq_dis) __enable_irq ();
  35. #else
  36.   do {
  37.     *((void **)box) = (void *)__ldrex(&((P_BM) box_mem)->free);
  38.   } while (__strex ((U32)box, &((P_BM) box_mem)->free));
  39. #endif
  40.   return (0);
  41. }
复制代码

看看核心代码,几乎一两句话就搞定

出0入0汤圆

71
发表于 2015-12-30 11:16:17 | 只看该作者
MARK 学习了

出0入0汤圆

72
发表于 2015-12-30 11:31:22 | 只看该作者
不错                     

出0入296汤圆

73
 楼主| 发表于 2015-12-30 13:09:18 来自手机 | 只看该作者
TBG1 发表于 2015-12-30 09:27
感觉楼主越说越复杂。

对我来说,指针,就是存储其他常变量地址的常变量。 能改变就是变量,不能改变就是 ...

哈哈哈哈,对不住了~

出5入14汤圆

74
发表于 2015-12-30 13:26:37 | 只看该作者
楼主总结的清晰明白深刻!

出0入0汤圆

75
发表于 2015-12-30 13:55:09 | 只看该作者
学习了!新书什么时候出版啊!

出0入4汤圆

76
发表于 2015-12-30 14:03:44 | 只看该作者
本来我觉得指针就是一个放东西(变量)的格子,这个格子有个名字,通过这个名字就可以找到这个格子,然后拿出里面的东西或放进去东西。但是看了大师的文章,我又晕了。

出0入0汤圆

77
发表于 2015-12-30 14:13:42 | 只看该作者
标记一下,回去慢慢研究

出0入0汤圆

78
发表于 2015-12-30 14:24:25 | 只看该作者
指针就是指针, 用多了就会懂, 我觉得纠结指针的人就楼主一个, 楼主还是花多点时间在设计模式上更有建树;
不要再为初学者不懂指针而烦恼啦, 不会指针的人只是没用过指针的人而已, 仅此而已!

出0入0汤圆

79
发表于 2015-12-30 14:26:13 | 只看该作者
Gorgon_Meducer 发表于 2015-12-29 23:39
Cortex- M 和 KEIL技术支持

你同事是不经常给别人发律师函

出0入93汤圆

80
发表于 2015-12-30 14:37:12 | 只看该作者
本帖最后由 takashiki 于 2015-12-30 15:12 编辑

我觉得,LZ还是把范围缩小一下,比如限定为C语言。

因为有的编程语言中,方法指针是占用2倍一般指针空间大小的,因为还有一个额外的对象指针需要传递。您的
任何指针常量,其本质首先是一个无符号整数。
前提就已经错了,后面的就不用再看了。
典型的编程语言比如Delphi、C++Builder、VC++、部分GCC的编译器实现

比如:代码
  1. class CA{
  2. public:
  3.         typedef int (CA::*pClassFun)(int, int);

  4.         CA(){
  5.                 printf("sizeof(CA::pClassFun) is %d Byte", sizeof(pClassFun));
  6.         }
  7. };

  8. int main(int argc, char* argv[]){
  9.         CA a;
  10.         return 0;
  11. }
复制代码

我用32位WinXP平台,VC6进行编译,
选择为第一项“任意类”或者第三项“单继承”时,结果为4字节。
选择第二项“单继承和多继承”时,结果就变为8字节了。

如果使用C++Builder,加上关键字__closure之后的事件指针全部是8字节。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

出0入93汤圆

81
发表于 2015-12-30 14:41:52 | 只看该作者
TBG1 发表于 2015-12-30 09:27
感觉楼主越说越复杂。

对我来说,指针,就是存储其他常变量地址的常变量。 能改变就是变量,不能改变就是 ...

对于很多编程语言来说,常量依然是能改变的……不能改变的是常数。
常量只不过是契约的只读变量而已,而且是只防君子不防小人的那种契约。

出0入0汤圆

82
发表于 2015-12-30 14:45:23 | 只看该作者
takashiki 发表于 2015-12-30 14:41
对于很多编程语言来说,常量依然是能改变的……不能改变的是常数。
常量只不过是契约的只读变量而已,而 ...

是,所以有人建议吧常量改为“只读”

出0入296汤圆

83
 楼主| 发表于 2015-12-30 23:47:41 来自手机 | 只看该作者
本帖最后由 Gorgon_Meducer 于 2015-12-31 00:56 编辑
onepower 发表于 2015-12-30 14:24
指针就是指针, 用多了就会懂, 我觉得纠结指针的人就楼主一个, 楼主还是花多点时间在设计模式上更有建树;
不 ...


设计模式其实也是不需要怎么总结的,都是具体情况具体对待。仔细想一想,我应该还是心态不太好。
你的建议我需要认真反省反省。总整这些没用的其实是偷懒的一种吧。你觉得呢?

其实,诚实的说。这么多年来,我也不是在指针这种级别的小事情上吹毛求疵浪费时间,我不仅
研究了嵌入式设计模式,持续投入了大量资源进行实践和探索,而且有了相当商业化的成果,
但我也是有私心的,国内对知识缺乏应有的尊重和环境,请原谅我藏着掖着,我的确要靠这些部分
换口饭吃。最近几年写的文章基本上也被你们看出来了,旁敲侧击却总绕开重点。没办法的事情。
我也要活命,写点文章说交流很多时候效果甚微,客观上换点虚名是事实。我不否认。也不觉得
是见不得人的事情。有时候还请不要点破,我没有欺骗大家,但也想有所保留。对所有信任我的
人,我想说,对不起了。公开场合,很多东西我不愿多谈,但单独找我交流的,我摸着良心说,
我从来没有半点保留。

出0入296汤圆

84
 楼主| 发表于 2015-12-30 23:49:01 来自手机 | 只看该作者
takashiki 发表于 2015-12-30 14:37
我觉得,LZ还是把范围缩小一下,比如限定为C语言。

因为有的编程语言中,方法指针是占用2倍一般指针空间大 ...

你说的没错,我应该加入一个限定。必须是C语言。谢谢你的建议。

出0入296汤圆

85
 楼主| 发表于 2015-12-31 00:38:28 | 只看该作者
idle 发表于 2015-12-30 14:26
你同事是不经常给别人发律师函

其实我没见过呢。做技术支持前问人要序列号倒是常态

出0入296汤圆

86
 楼主| 发表于 2015-12-31 00:44:17 | 只看该作者
liuqian 发表于 2015-12-30 14:03
本来我觉得指针就是一个放东西(变量)的格子,这个格子有个名字,通过这个名字就可以找到这个格子,然后拿 ...


哪里有觉得不清楚的,抓住机会正好讨论讨论啊!否则,我岂不是帮了倒忙哈。

出0入0汤圆

87
发表于 2015-12-31 08:54:41 | 只看该作者
裸机思维,没有走字的,赶紧关注傻孩子。

出0入0汤圆

88
发表于 2015-12-31 09:12:17 | 只看该作者
傻孩子,好久不见了啊

出0入0汤圆

89
发表于 2015-12-31 09:16:14 | 只看该作者
这个不是傻孩子吗?好像已经很久没有见到你冒泡了,感谢分享你的知识,为论坛带来不少人气。

出0入0汤圆

90
发表于 2015-12-31 09:32:20 | 只看该作者
听大师一席话,豁然开朗.谢谢!

出0入296汤圆

91
 楼主| 发表于 2015-12-31 12:10:31 | 只看该作者
NFC 发表于 2015-12-31 09:16
这个不是傻孩子吗?好像已经很久没有见到你冒泡了,感谢分享你的知识,为论坛带来不少人气。 ...

钱不好憎……能来就补挫咧

出0入0汤圆

92
发表于 2015-12-31 13:04:27 | 只看该作者
已关注公众号。

“既然知道了变量的本质,我们就可以随心所欲了,比如,我们可以随意创建一个全局变量:
      #define s_wMyVariable    (* (( uint32_t *) 0x12345678))”
我理解,这只是一个比喻。表示理论上可以这么做,如果想捣乱的话。但由于“它是个黑户”,实际上不可以对RAM这么做。或者严格说,只可以对寄存器这么做。因为只有寄存器地址才在编译器中领了身份证。

出0入296汤圆

93
 楼主| 发表于 2015-12-31 14:38:21 | 只看该作者
xizi 发表于 2015-12-31 13:04
已关注公众号。

“既然知道了变量的本质,我们就可以随心所欲了,比如,我们可以随意创建一个全局变量:

基于以下情况,你可以实际使用这个方法:
1、这块空间已经被通过别的方法在Compiler里面注册占用了,
    比如,用一个大数组事先占过地方了。
2、如果你用MDK,会用Scatter文件来控制资源的分配;如果你用IAR,会用ICF文件来控制资源分配;
    如果你用GCC,会用link-script文件来控制资源分配。那么你完全可以告诉Compiler把某一块区域
    空出来,然后你可以用这个方法来使用这个区域。
3、寄存器是最常见的使用方法。

出0入0汤圆

94
发表于 2016-1-1 02:23:58 | 只看该作者
有道理。果然不止寄存器这一种方法。

出0入0汤圆

95
发表于 2016-1-1 08:39:33 | 只看该作者
我只知道,指针是地址,存放着指向某个变量的地址。

出0入296汤圆

96
 楼主| 发表于 2016-1-1 21:12:45 来自手机 | 只看该作者
liuerbin 发表于 2016-1-1 08:39
我只知道,指针是地址,存放着指向某个变量的地址。

指针可不是地址。指针比地址多了类型信息。地址只是一个单纯的整数。

出0入0汤圆

97
发表于 2016-1-3 10:05:15 | 只看该作者
复 杂了,怎么又和空间扯上了,这这…………还是纠结

出0入296汤圆

98
 楼主| 发表于 2016-1-3 11:54:54 来自手机 | 只看该作者
holts2 发表于 2016-1-3 10:05
复 杂了,怎么又和空间扯上了,这这…………还是纠结

指针混地址空间的嘛~

出0入0汤圆

99
发表于 2016-1-3 18:16:37 | 只看该作者
Gorgon_Meducer 发表于 2016-1-1 21:12
指针可不是地址。指针比地址多了类型信息。地址只是一个单纯的整数。

学习了:确实多了类型信息。

出0入0汤圆

100
发表于 2016-1-3 18:38:24 | 只看该作者
大师的话的确很有见地
虽然用的少,又在理解上拨了一层皮
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

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

GMT+8, 2024-3-29 16:41

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

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