网站建设工作进度表,网络工程适合女生吗,陕西省新安康市公司,品牌网站建设网不要陷在指针里面#xff0c;最好的方法是跳出指针#xff0c;我们从最终结果来思考问题。于是我的解题思路总是很偏#xff0c;但是直指本质。我们写一段代码#xff1a;编译#xff0c;反编译#xff0c;反编译这里我们用objdump -d hello 1.txt#xff0c;如果你… 不要陷在指针里面最好的方法是跳出指针我们从最终结果来思考问题。于是我的解题思路总是很偏但是直指本质。我们写一段代码编译反编译反编译这里我们用objdump -d hello 1.txt如果你是用IDA会发现出来的汇编不一样因为各种格式的汇编有不同的写法这里主要就是Intel和ATTGNU遵循的是ATT的写法。在里面找到我们的add 和main这里我不会展开去讲ATT这个玩意就是查表我这里主要讲几个内容这个非常重要。汇编语言中自己要关注堆栈平衡再一个就是寄存器的保存与恢复第三个就是调用参数约定。举例来说add %edx%eax 这个的结果在哪里这个都是指令直接就决定的也就是我们的CPU设计时候它的这条指令执行完数据会放在哪里。我们看到的main方法中的mov $0x6,%esimov $0x5,%edi这两个就是我们add方法执行的两个参数它赋值到这两个寄存器那么在用这两个寄存器是不是要把寄存器当前的值保存下来呢所以你能看到紧挨着上面的就是保存动作。然后callq 调用add方法这里我们看紧跟着的 mov %eax -0x4(%rbp) ,我们刚才说了add方法执行后eax里面是结果。这里将%eax的值放到了 %rbp寄存器-0x4的地方这个地方是什么是栈具体到代码中就是sum的位置。int sum add5,6的执行过程就是这样的。我这里分享一个图主要说的是传参的约定我们知道调用函数时候是有参数的约定其实二进制这里也是有的这个叫做System V ABI 。我一般是怎么掌握这些规则去写汇编一般就是用C写一些编译反汇编来看这个大家可以参考一本书我们编译完的程序是没有sum这个变量在执行的代码中都是变成了具体的位置这里简单说下就是堆还是栈局部变量是在栈上面局部静态变量是在堆上面。全局数据分两类一类是初始化的全局变量一类是未初始化的未初始化的运行时候系统会默认给初始化为0(但是不要以为它就必须是0这个就是跟运行机制有关我们写代码一定记住不要去尝试依赖外部不确定的因素)全局数据区分为 data rodata 和 bss rodata这个就是read only只读区域这个是由加载器加载程序进入进程时候会对这个数据区域的page做设定设定只读如果后续在这里写入数据就会报错。data就是我们常规的数据举例就是 int a100这类全局变量会放在data区域。而我们如果是int a这个全局变量就会放置到bss这个区域叫做全局未初始化区域这个跟data的区别在于这个bss在程序中不占用大小只是在加载时候会在内存中占用大小。text区域就是代码段。说到这里我这里再说一个内容我们在看到代码时候发现printf这个函数后面有个plt。我们来说下这个plt。plt的意思是这个方法不在这个程序里面是在外面的而对应的位置这里就是4003f0这个位置是什么我们知道printf是在glibc.so ,这里用的动态库。我们程序要跑起来是要补全这里的printf的执行块的系统的做法就是先放置一个占位位置然后程序加载的时候加载器知道这里需要一个printf的真实地址这些需要放置地址的区域统一在plt这个区域里面在这个位置放入真正的printf的入口点。听起来很绕我们用一个例子你就能明白了。但是这里的例子估计又牵扯进来新的概念大家先理解下吧。typedef int (*operate)(int a,int b);这个定义了一个类型类型是一个有两个参数一个返回值的函数类型。我们平时的类型就是int这里是一个函数的类型。然后声明一个列表把add和sub放进来我们直接调用即可。这里就想给大家说这个是可以放置一个函数名的等下我们继续操作就能够更深入的理解这个函数名。那么看到这里我们开始真正进入指针的世界我们来理解指针。我的操作就是编译反汇编我们先看下代码这里我们引入了指针p,储存了变量a的地址然后*p代表拿出p地址里面的内容我们看下反编译汇编分析下这个过程。这里mov $0x5,-0xc(%rbp)将5放入rbp-0xc的位置。lea -0xc(%rbp)%rax mov %rax-0x18(%rbp) 这两句话的意思是拿到 -0xc(%rbp)的地址放入 -0x18(%rbp)位置也就是指针p的位置。这里的mov %rax%eax 指的意思是将rax里面的值读出来找到这个值对应的地址的内容存储到%eax里面这里可以用c语言写就是int c*p;从这里面我想说的就是我们的指针这些在汇编形态下不过是两种类型一个是读取寄存器的值一个是读取把寄存器中的值当做地址对应位置的值。我们只要这样子去理解基本上就能清晰的了解指针指针所存储的值我们一般都是用它所指向的地址内容它本身的地址只是途径类似于我们在图书馆查出来书的序号A-1-303我们真正要的是这个位置的那本书。当我们理解了这个这里的add函数就是个地址我们这么来看下。我们直接用void *padd;然后把这个p让编译器按照add对应的参数返回类型去调用这样子就可以用到add函数。int 这类我们就能理解了那么我们再来说下int[]这个看完汇编语句一下子就明白了。我们说过一点就是在真实的计算机上面执行的是指令指令理解就两类一个是值一个是地址也可以理解成直接引用间接引用。这里想说的是array在编译器里面就是理解成一个指针指向了一个int数组。我们把array赋值到p指针发现p[1]跟array[1]是一样的。我们看反汇编代码打印语句改成printf(p[1]%d,array[1]%d\n,p[1],array[1]);来比对下。这里数组的取值直接被优化了直接用的mov -0x1c(%rbp),%edx 从上面的存储可以看到这个位置直接就是array[1],具体指令是movl $0x2,-0x1c(%rbp)我们指针的获取这里很明确拿到数组起始地址用add %0x4,%rax进行了偏移找到了p[1]位置。这里分享下地址的1指的是地址所指向的内容的大小进行偏移。理解了这个再去理解数组就很好理解了。我们把代码改成long *parray;printf(p[1]%d,array[1]%d\n,(int)(p[1]),array[1]);打印出来就不一样了原因就是p1是加的sizeof(long) 的大小也就是它所指向的内容所代表的大小。所以我们再来说下int array[3][5]{1,2,3,4,5, 6,7,8,9,10,11,12,13,14,15};然后 int *p[5] array; 那么p[1][0]是多少呢我们前面说了p1是依据它指向的大小这里就是 int [5] 的大小所以就是输出的6。这里也就是array实际就是一个指向一行五个int的一个数组指针。核心一句话指针的1是根据指向的数据大小决定同时在指令级别去看只有两种解析就是值和地址。这一节有可能讲的太晦涩后面我们再来讲一通。建议学习下x86汇编的寻址再一个就是计算机组成原理或许后面我们再讲一次指针再花费一些力气把这块讲下。下一节我来说下静态库和动态库怎么使用两者的本质区别以及设计的逻辑是什么。推荐阅读专辑|Linux文章汇总专辑|程序人生专辑|C语言我的知识小密圈关注公众号后台回复「1024」获取学习资料网盘链接。欢迎点赞关注转发在看您的每一次鼓励我都将铭记于心~