打印
[嵌入式linux]

指针的问题

[复制链接]
773|2
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
sdnumcu|  楼主 | 2015-1-21 09:19 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
C的指针是个复合命题,三个内容组合起来才是完整的指针:实现C的平台上的地址;类型;运算。对指针的解释,任何单纯拿一部分出来说都是片面不完整的。
地址不用多说,问题出现在绝大多数人把地址等价于指针,比如指针就是地址。类型是指针可以解释成的数据组成,包括int,short这样的基本类型,也包括struct弄出来的自定义类型。类型比较好理解,甚至在未知类型的时候,也有一个不完全类型void*来做适配。运算,这个是和类型紧密相关的,比如为什么指针没有乘除法运算,为什么int*的指针做加一运算得到的结果 与 char*的指针做加一运算得到的结果可能不一样,为什么所有类型里面只有指针才有->和*运算,另外,运算概念的理解也可以用void*来说明为什么一个void*类型的指针,不能做任何合法运算。

所以指针类似一个组合概念的定义:合法地址,合法类型,针对类型的合法操作,三者共同定义一个指针。
类型也是一个组合概念:一段合法内存,即sizeof(type)的内存区域;针对该类型的合法操作,所以,比如针对float类型,我们没法用C里面的 a == b 来判断 a 是不是b,而需要用 a - b < delta 来近似等于。

重点是类型,再延伸下去,可以延伸到类型和对象,它们的结构不是很像吗。

>>>顺便记录一个关于汇编和C语言,起因是不少人喜欢用汇编来解释C,觉得这玩意高级黑,这不是错,错在用的场合。
C有自己的规范,单片机只是一个运行平台,C标准只有一份,而不是单片机单独制定一份,所以在C层面上强调单片机和其他机器那是自欺欺人,因为本就没什么不同。汇编这个层面和C是另一个领域。做设计一个很重要的就是边界定义,哪些是系统的,哪些不是系统的。C自己有自己的边界定义,它是一个完备体系,所以解释C的东西,用C系统里的元素来解释就好。汇编可以加深对编译器的理解和编译器实现的理解,但对帮助C语言的理解作用不大。

相关帖子

沙发
sdnumcu|  楼主 | 2015-1-21 09:19 | 只看该作者
本帖最后由 sdnumcu 于 2015-1-21 09:53 编辑

一般来说,至少应该理解到 指针是存储地址值的变量 还说的过去。

说 指针是地址,有时可以理解是一种简称,当然更多的可能真的是理解的不够透彻。

其实还有很多类似的说法很奇怪。
比如说 几年前我在CSDN上看到一个帖子讨论 数组作为形参传递的问题。其中一个非常莫名其妙的回答是
数组在作为形参传递时蜕变成一个指针。
这个回答居然还占了大多数人的意见得到赞同,真是非常要命。

其实我长期以来个人形成的一种统一理解就是,不管是变量名(当然是作为左值时),还是函数名,乃至于 字符常量,数值常量 等,在我眼里都是”地址“。当然我这是从编译出来的结果的角度去理解。
之所以 地址 加上一个引号,是因为后来我发现在某些场合下,我需要区分 真实的地址值 和 标识符 的概念。
比如,即便我真的可以粗暴的简单声称 一个变量名就是代表(它在编译后)它在内存中的地址值,但是一个特殊的情形是,如果我在一个地方 声明引用这个变量呢,这个时候,其实这个声明引用的,仅仅只是一个标识符,即它的确是承载着定义后这个变量所分配到的内存地址值,但这个时候,它和定义这个变量的源文件或者代码块中所使用这个变量作为左值时代表这个地址值时,是完全两个概念,因为标识符仅仅是一个,怎么说呢,只是一个数值的副本,它不再是 那个地址空间的地址值。

其实说起来,这些细微的概念,很多时候不会引起太大的麻烦和应用问题。仅仅是对我们这些 穷讲究和喜欢刨根问底的人的一种 认真劲。
而且说实话,这些很细致的理解,通常不是看完一两本C入门教程就能真正理解的——哪怕看的是类似于 C与指针 C语言入门经典 这类不朽经典。
没过几次指针乱指,复杂的地址操作变换 带来的折腾,没有对内存,编译时,运行时,加载程序时, 存储器上发生的行为的一些具体了解,根本是不可能有太多了解的。
而有的了解后来会带上自己的烙痕,比如我 至今形成的相当稳定的”一切皆地址“的概念,当然不敢保证多么正确,但它却融合了我这几年C编程的经验,我不敢说它有多么准确,毕竟我没有去看C标准定义等权威内容,但是,至少这些理解可以让我经常能够解释我自己面对的和我听说到的一些编程细节。
所以现阶段我觉得还是足够的。
因为理解是没有极限的。

说回那个 ”数组蜕变成指针“的可笑看法。
这个看法非常可笑,在于,他们也是把指针等同于地址。而更有甚者。
他们没有理解,数组,或者 变量的 左值性和右值性。

要是让我来理解,我会这么理解,数组名,类似于变量作为左值,代表的是数组分配的地址位置值。所以它作为形参,是以地址传递参数。
这个时候,不是数组蜕变成了指针,说 数组作为地址传递是合理的。 形参的写法写成 *p,这个地方,我暂时还没有什么很圆满的说道说过去。
只能简单理解为,这个*号应该作为 解地址符理解。

不对之处,freebsder见笑。

更重要的是,在主贴的解释里。
楼主的讲述精辟之处在于。
他提到了 如何看待一个数据类型: 是三方面的复合,一个是 地址,一个是 合法操作。 一个是代表的数值内容。

其实,地址这个,我并不实现理解 楼主想表达的意思。

但是,我对此的理解,也是这三个内容。
对地址,我的理解是,不管是什么类型的变量,包括 数组,包括结构体联合体,当然也包括特殊的指针(作为地址值变量理解)
它们其实都有共性,都有变量的共性。
那就是,作为左值时,它理所当然地表示在内存中分配的到得地址 或者说对与指针而言,是指它指向的(即它存储的)地址值。
这也就是我当前”一切名字,标识符 皆 地址“的理解来源;

另一个是它代表着它存储的那个内存空间里的值,这个值要特别注明的是,它不仅包含二进制数值,还包含了以其特定的数据格式去解释出来的 数值或者内容。
这个举例说就是 同样是 0x12323e33 作为整型 和 作为 浮点数,那完全是两码事,无他,无非存储格式不同;

最后一个,也是我们最容易忽略的,那就是,合法操作。

因为符号的写法或者使用方法,我们太过容易把 C 这些编程语言里的 运算符 和 数学上的运算符混为一谈。(当然,其实数学这方面也是因为我们讲的不够基础根本,严格,事实上,所谓对运算数施行运算必须保证 运算是合法的也是一项必要内容,只是我们一般不会特别强调这个,因为,大概,谁也不会没事作出把一米除以一公斤这种事情来,因为大家都知道,那不知道应该代表什么含义。)

在 C里,这种情况也是类似的。
为什么 指针只能施行加减运算,这是因为它有很明确的意义,那就是把指向的地址向前或者向后移动多少个单位(不是字节)
但是,假如你对指针施行乘除运算,请问你觉得这样操作,它的含义是什么呢?

几年前我在写一个函数的时候,试图通过这种手段来得到一个后来证明很蹩脚的运算的操作。
结果这件事情引发了一次很棘手的 越界调试经历(大家都知道,最麻烦的越界现象就是,时而正常时而不正常。)
也是在这件事情上,我终于理解到 每种变量,每种运算符并不是可以随机混合在一起的,它们是否合法,取决与他们是否有足够清晰明确的现实意义。

这也是 typedef unsigned char U8 和 #define unsignec char U8 这两种 ”重定义数据类型“ 完全不是一回事的概念。
因为前者完完全全是 把一个数据类型的所有特征,包括运算,字长,存储格式等赋予一个标识符,而前者,伙计,它仅仅只是一个宏替换。
就算你不是因为书写中语法有错误而不得不发现这样写不行,你也要知道,即使你运气好通过,并且允许这个程序永远不会出现问题,但是不好意思,错误就是错误,并不是能编译通过就是正确,也不是运行永远不出错就是正确。


使用特权

评论回复
板凳
djxf| | 2015-1-21 10:41 | 只看该作者
sdnumcu 发表于 2015-1-21 09:19
一般来说,至少应该理解到 指针是存储地址值的变量 还说的过去。

说 指针是地址,有时可以理解是一种简称 ...

对同一个事物,从不同的角度去看,得出不同的看法并不奇怪。
所谓仁者见仁智者见智,便于自己理解而又能正确应用的,就是好的。

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

个人签名:老老实实做人,认认真真做事,学习没有捷径,成功源于付出!欢迎进入【嵌入式系统】版块!

49

主题

2136

帖子

6

粉丝