c语言中再议运算符:箭头运算符“->“、结构体成员访问运算符“.“、数组元素访问运算符“[]“

最近,我在看linux内核源码程序,里面有大量代码使用了箭头运算符->、结构体成员访问运算符.、数组元素访问运算符\[\],以及这些运算符结合使用的代码,有了一些心得,可能不是很准确,现在先记录下来。

(1)箭头运算符->

箭头运算符->前后的变量在物理内存上是不相连的,也就是内存地址是不相连的,这个比较容易理解,因为箭头运算符是为了实现一个通过一个结构体指针变量来读写访问执行这个结构体变量的内部成员。我们来分析一下这句话,首先存在一个结构体指针的四字节变量,里面存储了一个结构体变量的地址(这个地址为结构体首元素的首地址),这个结构体指针变量所在位置(地址)是在不与真正结构体变量相邻的地方,例如最常见的就是使用malloc()类似函数实现堆内存变量定义,并将定义的内存变量的地址赋值给了一个例如在栈上的临时指针变量。一个在堆上,一个在栈上,从物理角度看两个地址是不相邻的。

(2)结构体成员访问运算符"."、数组元素访问运算符"\[\]"

之所以将上面2个运算符一起讲解,是因为两者存在相同之处,也就是都是实现一个大变量内部内部一个元素的访问。结构体的概念和数组的概念存在很多一致性。结构体是内部成员类型不同的数组,数组是内部成员类型相同的结构体。我们分析"内部成员"这个概念的含义,从物理内存上看无论是结构体的内部成员还是数组的内部元素,都是在内存上紧密相邻的,不是一个在这个地方,另外一个在另外一个地方,内部成员都是挨个相邻的。

结构体成员访问运算符可以这样分析:首先它是一个双目运算符,运算符前面的变量是代表这个结构体整体,运算符后面代表结构体整体肚子内部一个小的成员。结构体成员访问运算符的目的是实现对这个成员变量的读写访问(使用成员的内存空间或者使用成员内存空间中的数值)。相邻的访问方式是最简单,最高效的。

数组元素访问运算符可以这样分析:首先它也是一个双目运算符,虽然\[\]是由两部分组成的,但是一直是作为一个整体使用的,可以看做一体。运算符前面的表示数组这个整体,是一个比较大的概念,运算符\[\]内部代表数组这个整体内部的某一个元素,为什么使用索引值这个数字呢?因为数组中每一个元素类型相同,占用内存空间相同,因此具有相同属性只是在内存中存储位置不同,因此可以使用数字表示这个元素。现实中常见的就是部队士兵报数,这个道理是一样对的。数组元素访问运算符\[\]的目的是实现对数组内部元素的访问,要么使用内部元素的内存空间,也么使用内部元素内存空间中的数值。数值是一种更加间接、高效的特殊结构体。

(3)c语言中的搜索的接续性

结构体成员访问运算符、数组元素访问运算符写出的代码本事就有很好的接续性,比较容易理解。我的意思是从相对地址这个概念思考内部成员的访问。首先结构体变量名代表一个绝对地址,成员变量代表一个相对地址,绝对地址+相对地址就是内部成员的绝对地址,有了地址就能实现对成员变量的访问。数组也是类似的概念,数组名表示一个绝对地址,运算符内部的索引值表示一个相对地址,数组绝对地址+索引相对地址就找到了数组元素的绝对地址,就可以实现对数组元素的访问。

我不知道大家使用箭头运算符->的时候会不会有这样的疑问,感觉少了一个结构体整体这样的一个中间元素,这样的感觉。结构体指针变量存储的是结构体这个整体的地址,通过它找到的应该是对应的结构体变量名,然后在通过内部成员对应相对地址找到成员变量的绝对地址,然后实现访问。正确的连续的访问流程应该是这样,但是箭头运算符->后面根本没有结构体整体这个变量,而是直接跟着结构体内部成员变量名。c语言在设计箭头运算符->的时候为什么没有体现真正的结构体变量名,而是选择忽略呢?我的答案是,**有时候根本不知道真正结构体变量名(**或者没有直接给出结构体变量名),还是刚才的例子使用malloc()函数分配堆内存,分配的这个堆内存结构体变量是没有真正名字的,如果按照那种写法是没法继续写的,因此c语言在设计箭头运算符的时候选择直接忽略,最终结果也是接续的,没问题的。我们从绝对地址+相对地址的角度出发,箭头运算符忽略结构体变量名是没问题的,因为绝对地址和相对地址都知道了就是能事项对内部成员的访问。

相关推荐
xiaobobo33303 个月前
c语言结构体相关箭头运算符和点号运算符的联系以及c语言的“索引”思想
c语言·箭头运算符·点号运算符·索引思想