浅谈指针的本质
内存地址(普通指针)或偏移地址(成员指针)。
指向变量的指针,是数据内存的地址,指向函数的指针是代码内存的地址,指向数据的指针指向数组开始位置的地址。
抽象点说,就是以一种统一的方式产生对不同数据结构的元素的引用
理解指针要注意以下几个点:
-- 每个指针都有类型,类型决定指针移动时的步长(假设可移动:++ / -- / +n / -n)
-- 从内容上讲,指针就是地址,尤其是在机器级层面上
-- 对指针进行类型转换,仅仅是改变了解释指针所指内存区域的方式,位模式没有变
-- 指针可以指向函数,其值是函数对应机器代码的第一条指令的地址
-- 指针和数组关系暧昧,如果你明白比例变址寻址,你就能清除数组名,下标,指针类型之间的关系,简单说:数组名即基地址(立即数),下标即变址,指针类型即比例因子,而指针的值就是:偏移量 + 变址 * 比例因子 +基地址【IA32】,这样你就明白数组名是个常量(在机器眼里就是个立即数,寻址时对变址没有限制也就说明了C语言对下标越界不检查)
指针是储存地址的一个变量
指针变量储存的就是一个地址, 可以有如下行为:
* 改变该变量的值;
* 取得该变量的值
这和其他变量是一样的, 但是指针还可以做到:
* 改变该变量指向的那个地址的值
* 取得该变量指向的地址的值
这只是编译器赋予指针的特定行为而已(通过& * 等语法), 在汇编层面大概就是间接寻址了.
因此指针就是储存地址, 纠结「指针的指针」什么的可以退散了.
指针为什么要有类型
汇编中数据是没有类型的, 所以指针的类型是编译器在规定的, 通过了指针的类型检查, 才能保证你少犯错.
因为其他数据有类型, 所以指针也得有类型.
指针的类型表明: 你期望从这个地址里取出来的数据, 是什么类型的.
大部分时候, 我们想从一个 *int 指针中取出来的值总是 int, 只有在特殊情况下我们才会想把 int 当成 uint 取出来, 类型检查就帮我们减少弄错类型的可能.
另外, 类型也规定了指针移动的粒度, 你期望从一个 *int 取出一个 int, 那 *(int + 1) 如果是地址+1的话, 不就取出了奇怪的数字?所以这里的 *(int + 1) 的 1 起始是指代一个 sizeof(int), 这样你才能方便地访问连续的内存数据.
在汇编中大概就是 [arr + 4]. (32位限定), 编译器根据指针的类型帮你做了这一步转换.