C语言中的灵魂是指针,我愿意称他为上帝变量,C语言能够变出一切戏法全是依赖指针,因为它最接近底层。
    在C语言运行体系中,读取内存数据需要核心的两个条件,分别是这个地址的开头和这段内存地址的长度
    C指针具备某个地址段的起始头地址和你想要取的地址长度时,它可以奇妙的取到内存数据,就是下文讲没描述的C的运算。譬如,int 类型在x64中的长度是64/8=8字节,假设某个int长度地址开头是0x0001,那么通过指针可以从0x0001——-0x00008 成功解析到你想要的int数据。
    戏法开始:)
    *为什么变戏法的是上帝指针?
    因为他甚至比各种变量局部变量还高出一个维度,哪怕是静态全局变量都无法何他站在一个维度去衡量。我们知道数据变量中,被各种作用域局限,C语言的内存堆栈机制对各种区域变量进行管理(锁定访问区域),譬如局部变量外无法访问局部变量内的int i; 然而在C语言中的指针直接脱离局限,或者说指针变量是超维度的存在,简直就是“超越三界之外,不在五行之中”,我们来看一个例子。

    1. int main(){
    2. int i=1;
    3. func(&i); // 传入i的地址
    4. return 0;
    5. }
    6. void func(int* p){
    7. (*p)++; // *p表示取出p地址长度为int 的数据
    8. }

    int i没有在其他区域打交道,i的定义存在main()作用域,但是C语言可以通过一个去全局的上帝维度操控了i的自增,那就是指针操控。通过指引i的地址,C语言可以在所有角落所有区域对i进行高纬度的数据操控,
    也就是说,你只要能把某个值的地址传到你想要的地方,无需考虑各种局部变量的影响,他地址所对应的实际值必然发生改变。更加不会被形参,实参等函数内零时堆栈所干扰。因此指针的维度是比全局变量还高的。我们知道不管是底层数据还是高级计算机模型,计算机内部经常会产生数据副本来存储一些临时数据变量,利用完即可销毁。如果没有指针的操控,每个数据都可以是无限复制的副本,它将变得非常的凌乱不堪。C语言操控者意图很简单,他其实就是想在任何区域任何时间操控某个区域的内存数据,那么我们只需要知道这个数据再内存中存放在哪,我们就可以百分百从上帝视觉操控你想要的数据。 记住,任何计算机虚拟数据的载体都依赖与物理实体,虚拟数据在高级抽象的计算机逻辑中可以变化成各种副本,底层越接近物理的载体它越不会如此抽象,那C语言中的基础物理载体便是指针在控制内存地址,他在最接近底层最接近物理层的控制着数据。

    指针如何定义和使用?指针变量存在在一般C语言定义中非常的隐性,但是他的操控实体无处不不在,而指针这个数据想被使用一般存放于指针变量中,指针变量存放其实是计算机最大操作字节长度,这个稍后详细描述。
    我们从指针的定义的指针的使用观察。 int *p=&a; // 定义p指针取地址头为a的地址,长度为int长度
    *p;// *运算符在C语言和地址结合就是进行取这个地址的实际值
    int *p=&a;在这个定义中,p先被赋予了a的首地址,并且在这里告诉了接下来的运算符要取得长度(一个指针想要取得内存数据的长度就得回过头来询问这个int),p未来要进行运算必须取决于现在定义的int 长度。换句话说你定义了什么长度,他在取内存长度中就会取什么长度,如果这个假设合理,那你可以大胆一点预测char *p2=&a; 这个定义后,在未来遇到对p2地址取长度时候,p2就会和我们想的一样去取sizeof(char)长度的地址。同理,我们定义的如果是short *p2=&a;这时候我们可以同样推测到p2将取得sizeof(shot)长度。但是要注意的是,因为a的数据被定义局限,最好不要定义和a本身长度不一样的指针长度。而目前C语言定义取地址的长度局限于C语言内部的固定类型,譬如int long int short char 等基本类型,(在后期可能有一种方式可以变相的自定义拓展取内存的长度,譬如 char (*p)[2];它可以取到2个char的长度的内存)
    在上面的例子中都是用到了a的地址,a是用户自己已经定义的一个变量,在C语言中不一定如此局限,当然如果你有备而来,没有准备赋值某个变量的地址。你可以直接手写一个地址给p,如char *p=(char*)0x123;他会强行告诉编译器p取得是地址0x123,p运算后取的是起始为0x123往后取char长度的内存空间。
    &这个运算没啥意思,他就是单纯的取内存中变量的地址,简称取地址符号。*注意前提是内存中的变量实体必须被定义且存在。否则&运算无效,比如a是内存中的变量实体,所以可以取到a的地址为&a,而使用&&a则出错。

    指针是什么?指针被定义后,它的本质其实是一个变量,可以理解为指针变量。我们在一般C语言接触的时候都强调过int变量 char变量等。而指针变量本身被定义后也同样会是一类变量,这个变量我们都称为指针变量,其变量存放了某个其他变量的地址。既然是变量,它在内存中必然存在数据,那么也必然会在内存中找到他,于是指针变量同样存在它自己的内存长度和他的地址头。和其他变量没什么本质的区别,记住但凡是C语言内存模型中,变量都是有他的内存地址头和他的数据内存长度,指针变量也是如此。隐性的是它很少被人当成一类类型在考虑,因为更多的人对int 和 short 等基本变量印象深刻。在上文我们提到过,指针变量的内存区域长度就是计算机系统最大的操作字节,他和C模型中的int类型长度非常相似。但是要区别的是指针变量的长度并非可以和int类型相等而论。那么现在我们知道了这个特殊变量的类型长度和他的地址头,他本质就是一个长度是计算机最大操作字节的变量。

    高级指针的戏法?现在我们明白了指针存放的本体是个什么,它无非和int i; char c; short a;是一样的数据变量。那么既然这些 i ,c,a,可以被其他指针指向,那么指针变量也是可以的。于是我们可以有个想法,那就是C语言的高级指针戏法,指针变量可以被其他指针所指向,即指针的指针。这里在陈述一个事实,C模型中,只要你可以用&对某个变量拿到内存地址,他就可以诞生属于这个&取到地址后的的指针。接下来从基本类型的指针定义开始

    1. int i=0;
    2. int* p=&i;
    3. char c='0';
    4. char* p2=&c;
    5. short a=0;
    6. short* p3=&a

    定义了三个基本类型以及它们对应的指针后,我们可以用他们各自的p,p2,p3寻找他们的值。那么对于指针变量自身来说,谁来寻找p,p2,p3的值?这时候,我想起了一个之前我最早理解的是C语言的问题,为什么不可以有一种类似叫指针类型的类型,它可以直接定义指针point p=i;就完事了,为什么要int p=i呢?其实这是C语言戏法巧妙地地方。在C语言高级指针的定义中,它无视你中间的指针过程,因为他知道你指针的指针还是指针的指针的指针你最终是要指向一个实体变量的,他中间的所有类型就是指针类型。它只需要关注你最终这个指针的指针的指针…..的指针指向了什么实体。如果最初C的设计者简单粗暴出现一个类似point的类型指向万物,那么这将对后期的数据指引加大复杂程度,你可能不知道你定义的指针从何而来,也不知道它被调用时要取得是什么数据。为了避免这种不合理的指针类型,C语言将指针定义和基本类型结合,并且用的数量表述指针的层级。这就是C语言巧妙地高级指针戏法。

    1. int** q_2=&p; // 取p的地址给指针q_2,q_2最终跳2次地址后指向一个int的长度内存
    2. int*** q_3=&q_2; // 取q_2的地址给指针q_3,q_3最终跳3次地址后指向一个int的长度内存
    3. int**** q_4=&q_3; // 取q_3的地址给指针q_4,q_4最终跳4次地址后指向一个int的长度内存
    4. ....

    7b100fa266fdbf996a528cb2d4e71c5.png
    高级指针的内存模型,二级指针变量存的是一级指针的地址变量,长度也为计算机系统最大的操作字节,三级指针存放了二级指针的地址变量,长度也为计算机系统最大的操作字节以此类推。所以不管几级指针本质都是一个指针变量存放的都是指针变量的长度,为计算机系统最大的操作字节,每个指针变量的内存占有长度一样和最终指向的数据类型长度无关。
    模型中可以突出的特点:箭头表示指针指向的是目标变量的内存地址头。不同的矩形表示数据存储占有的不同的内存长度。