[C语言]指针的*理解(从底层实现理解)

[C语言]指针的*理解

基础

首先简单介绍一下我认为指针比较基础的东西,指针指向一片地址,指针的值是这片地址的值(一般是32bits如0x000014FA,0x为16进制,十六进制下的一位等于4bit),指针本身也有地址,但和指针的值是完全两个东西,用指针可以对指针指向的地址上保存的值进行修改,注意,这完全是两个东西,计算机内存中的地址就像是盒子的编号(计算机本身自带),而地址上存的值就像是盒子内保存的东西,比如说这样
0x00000000(地址) 00000020 (值,32,这里为了方便写成16进制)
0x00000001 <—(指针的值就是这类东西)
0x00000002
假设有一个指针ptr,那么*ptr就可以表示指针指向的地址上所保存的值,可以作为左值,也就是说可以修改这片地址上所保存的值,&ptr就表示指针的地址,建议初学者把ptr和 *拆分开来看,int *ptr只是告诉你声明了一个变量ptr,这个ptr指向一个int类型(ptr的值上存的是int)
[C语言]指针的*理解(从底层实现理解)

(都是干货)
还有指针数组,数组指针一些比较简单的结构,建议自行了解,只要注意ptr先和括号还是 * 结合就很容易看懂具体是什么
[C语言]指针的*理解(从底层实现理解)

OK,下面进入正题,对指针的高阶理解,建议前面没看懂的先自学,对指针有初步了解后再往下看

C的内存设计(C Memory Management)

[C语言]指针的*理解(从底层实现理解)
内存被分为四块,栈(stack),堆(heap),静态数据(static data),代码(code)
[C语言]指针的*理解(从底层实现理解)

其中栈保存的是局部变量,比如说在函数里定义的变量,他们的作用于仅限在函数内,它的存放方式是从上往下的;堆保存的是动态数据,通常是malloc函数分配的;静态数据保存在函数外定义的数据;代码保存所需执行的代码,只有在程序运行时才从内存中读取

对,你没有听错,内存也不是计算机跑代码的地方,充其量只是一个仓库,在运行的时候执行时需要一点读一点(具体可以关注我之后会出的RISC-V的教程,以伯克利CS61C课程为模板为各位介绍,话说今年不出意外的话,应该会到伯克利EE department深造,以后会更新更多高质量的内容,关注走一波)
[C语言]指针的*理解(从底层实现理解)
计算机在处理时,是在处理器(processor)中处理的,是一个从内存读入然后写入的过程(写入就是修改一些内存中的变量的值)

malloc函数

这里我找了官方文档的介绍(官网路口,不谢)
[C语言]指针的*理解(从底层实现理解)
Figure 1
[C语言]指针的*理解(从底层实现理解)
Figure
void*会使你malloc的指针类型自动和左值的类型匹配,防止了bug
[C语言]指针的*理解(从底层实现理解)
[C语言]指针的*理解(从底层实现理解)
[C语言]指针的*理解(从底层实现理解)
[C语言]指针的*理解(从底层实现理解)
malloc通过按块划分,每块是前一块的一半大小,进行合并,来达到快速的分配内存
!!malloc必须和free配套使用,用一次malloc就要有对应的一次free,而不可以malloc两次,再free两次,首先对同一个地址free两次的操作是不可行的,其次第一次malloc的内存还在那儿,但是我们已经不知道它的具体位置了,没有释放掉,会出现内存泄漏
也就是说,当内存中空间足够时,会分配相应的内存,然后返回指向该内存头部的指针
这里从几个问题切入,让大家深入了解指针和malloc函数
一、
我曾今有一个疑问:就是malloc分配的指针能否进行指针算术,因为,我当时想的是,malloc会找一片内存,如果这片内存不够大,它会再找一片内存,然后把前一片内存的尾部和这一片内存的头部架桥连起来,这样的话指针算术就不管用了,因为指针算术的前提是内存连续,但是后来发现我的假设就错了,这里声明一下 !!!!!malloc只会分配内存中连续的地址,如果没有一片连续的地址够大,那么就不分配了,返回NULL,就是这么任性
二、
如果函数里malloc要非常小心,因为你原本想要处理的数据存在某一片地址里,结果你申请了一片新的地址,利用指针修改的就不是原本地址上的值了,建议使用realloc,它会直接将之前那片内存地址直接移到一个新的地方。但实际上这也会迎来新的问题

[C语言]指针的*理解(从底层实现理解)
[C语言]指针的*理解(从底层实现理解)

作为函数参数传入的指针

我们知道,C语言和C++语言风格迥异,后者有类(class)的概念(虽然有时候继承很复杂的时候用起来比较操蛋),前者只有结构(struct),后者可以使用引用(reference),前者没有只能使用指针。当然最重要的是!!!!!C是值传递,传入的是一个copy,所以参数是个值得时候在函数里无法改变原本的值,因为根本不是同一个东西,只是一个copy。想要用函数改变值只能使用地址,告诉计算机到原本的地址上去改,这里C和C++的思想都是一样的,看下下面的例子,调用后y仍旧是3
[C语言]指针的*理解(从底层实现理解)
[C语言]指针的*理解(从底层实现理解)
这样就可以,当然传入的不是指针,而是直接传入y的地址。右边是C++的引用用法
[C语言]指针的*理解(从底层实现理解)
没有初始化的指针是没有意义的,这里没给ptr的值(也就是地址),却直接在虚空地址上赋值
[C语言]指针的*理解(从底层实现理解)
这里付一段我在二维链表中写的代码
[C语言]指针的*理解(从底层实现理解)
我当时下意识想的写法并不是这样而是

(*(this_row->down)).left = (*(this_row->left)).down
              
              
      ----------------
      |             |   <------
      -----------------        

这两种实际上是一样的,只是第二种更加直观一点,因为归根结底指针值只是地址,不会因为是this_row指向的element里的下指针所指向element里的左指针而有所区别,只要看最后的值就行了,这两个写法明显是等价的
就像下面的例子,只不过是作为右值
[C语言]指针的*理解(从底层实现理解)

为什么要用指针

[C语言]指针的*理解(从底层实现理解)
[C语言]指针的*理解(从底层实现理解)

用函数改变指针

同样的,只不过我们这次应该传入的参数是指向指针的指针,下面这个反例,大家可以自己试试看跑出来的是什么
[C语言]指针的*理解(从底层实现理解)
[C语言]指针的*理解(从底层实现理解)
[C语言]指针的*理解(从底层实现理解)
[C语言]指针的*理解(从底层实现理解)

在for循环里以指针为条件

[C语言]指针的*理解(从底层实现理解)
[C语言]指针的*理解(从底层实现理解)

上一篇:⭐malloc


下一篇:Java数据结构---线索二叉树递归创建与遍历