不要陷在指针里面,最好的方法是跳出指针,我们从最终结果来思考问题。于是我的解题思路总是很偏,但是直指本质。

我们写一段代码:

编译,反编译,反编译这里我们用objdump -d hello >1.txt,如果你是用IDA,会发现出来的汇编不一样,因为各种格式的汇编,有不同的写法,这里主要就是Intel和AT&T,GNU遵循的是AT&T的写法。

在里面找到我们的add 和main

这里我不会展开去讲AT&T,这个玩意就是查表,我这里主要讲几个内容,这个非常重要。汇编语言中自己要关注堆栈平衡,再一个就是寄存器的保存与恢复,第三个就是调用参数约定。

举例来说,add %edx,%eax ,这个的结果在哪里?这个都是指令直接就决定的,也就是我们的CPU设计时候,它的这条指令执行完,数据会放在哪里。

我们看到的main方法中的

mov $0x6,%esi

mov $0x5,%edi

这两个就是我们add方法执行的两个参数,它赋值到这两个寄存器,那么在用这两个寄存器,是不是要把寄存器当前的值保存下来呢?所以你能看到紧挨着上面的就是保存动作。

然后callq 调用add方法,这里我们看紧跟着的 mov %eax ,-0x4(%rbp) ,我们刚才说了add方法执行后,eax里面是结果。

这里将%eax的值放到了 %rbp寄存器-0x4的地方,这个地方是什么?是栈,具体到代码中,就是sum的位置。

int sum =add(5,6);的执行过程就是这样的。我这里分享一个图,主要说的是传参的约定,我们知道调用函数时候是有参数的约定,其实二进制这里也是有的,这个叫做System V ABI 。

我一般是怎么掌握这些规则,去写汇编,一般就是用C写一些,编译,反汇编来看,这个大家可以参考一本书,

我们编译完的程序,是没有sum这个变量,在执行的代码中,都是变成了具体的位置,这里简单说下就是堆还是栈,局部变量是在栈上面,局部静态变量是在堆上面。

全局数据分两类,一类是初始化的全局变量,一类是未初始化的,未初始化的运行时候系统会默认给初始化为0(但是不要以为它就必须是0,这个就是跟运行机制有关,我们写代码一定记住,不要去尝试依赖外部不确定的因素)

全局数据区分为 data rodata 和 bss ,rodata这个就是read only,只读区域这个是由加载器加载程序进入进程时候,会对这个数据区域的page,做设定,设定只读,如果后续在这里写入数据,就会报错。

data就是我们常规的数据,举例就是 int a=100;这类全局变量会放在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 *p=add;然后把这个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 *p=array;

printf("p[1]=%d,array[1]=%d\n",(int)(p[1]),array[1]);

打印出来就不一样了,原因就是p+1,是加的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]是多少呢?我们前面说了,p+1是依据它指向的大小,这里就是 int [5]  的大小,所以就是输出的6。这里也就是array实际就是一个指向一行五个int的一个数组指针。

核心一句话,指针的+1是根据指向的数据大小决定,同时在指令级别去看,只有两种解析,就是值和地址。

这一节有可能讲的太晦涩,后面我们再来讲一通。建议学习下x86汇编的寻址,再一个就是计算机组成原理,或许后面我们再讲一次指针,再花费一些力气把这块讲下。下一节我来说下,静态库和动态库怎么使用,两者的本质区别,以及设计的逻辑是什么。


推荐阅读:

专辑|Linux文章汇总

专辑|程序人生

专辑|C语言

我的知识小密圈

关注公众号,后台回复「1024」获取学习资料网盘链接。

欢迎点赞,关注,转发,在看,您的每一次鼓励,我都将铭记于心~

C语言,把指针按地上摩擦,爽相关推荐

  1. c语言函数指针封装函数,C语言之函数指针、回调函数的使用

    一.背景 首先看下如下代码,这个定义是放在头文件的,在程序中tCdrvCallbackFkt也定义了另一个变量,而且括号后面还跟定义了几个变量,不理解这个定义. typedef void (PUBLI ...

  2. 初学者对C语言中指针的爱恨情仇

    C语言中指针和数组的爱恨情仇 文章目录 C语言中指针和数组的爱恨情仇 一.前言 二.为什么学指针 三.说明 四.跟我一起学 (一)C语言中的*和& 1.C语言中为什么存在&和* 2.& ...

  3. C语言重点——指针篇(一篇让你完全搞懂指针)

    C语言重点--指针篇(一篇让你完全搞懂指针) 一. 前言 C语言是比较偏底层的语言,为什么他比较偏底层,就是因为他的很多操作都是直接针对内存操作的. 这篇我们就来讲解C语言的一大特点,也是难点,指针和 ...

  4. c语言中void指针,C 语言 void指针

    C 语言 void指针 到目前为止,我们已经研究了分配给指针的地址应该与指针声明中指定的类型相同. 例如,如果我们声明了int指针,则此int指针不能指向float变量或某种其他类型的变量,即它只能指 ...

  5. 理解C语言中指针的声明以及复杂声明的语法

    昨天刚把<C程序设计语言>中"指针与数组"章节读完,终于把心中的疑惑彻底解开了.现在记录下我对指针声明的理解,顺便说下如何在C语言中创建复杂声明以及读懂复杂声明. 本文 ...

  6. C语言函数指针 和 OC-Block

    C语言函数指针 和 OC-Block 一. C语言函数指针 关于函数指针的知识详细可参考: http://www.cnblogs.com/mjios/archive/2013/03/19/296703 ...

  7. 【C 语言】指针 与 数组 ( 指针 | 数组 | 指针运算 | 数组访问方式 | 字符串 | 指针数组 | 数组指针 | 多维数组 | 多维指针 | 数组参数 | 函数指针 | 复杂指针解读)

    相关文章链接 : 1.[嵌入式开发]C语言 指针数组 多维数组 2.[嵌入式开发]C语言 命令行参数 函数指针 gdb调试 3.[嵌入式开发]C语言 结构体相关 的 函数 指针 数组 4.[嵌入式开发 ...

  8. c语言中指针中 - 和 。的区别?

    c语言中指针中 -> 和 .的区别? 例子1:比如有如下结构 typedef strut node{ int data;   strut node * next; } ListNode; Lis ...

  9. 清华大学c语言指针ppt,清华大学出版社-C语言10指针.ppt

    清华大学出版社-C语言10指针 void print(char *name[ ],int n) {int i: for(i=0:i<n:i++) printf(″%s\n″,name[i]): ...

最新文章

  1. SAP MM - MIGO界面里的Via Delivery选项
  2. Spring系列之AOP分析之为目标类挑选合适的Advisor(五)
  3. 原 layer父子页面交互
  4. C# 文件操作详解(一)---------File类
  5. oracle fiscal year,Version 0 is not defined for fiscal year 2007.
  6. TensorFlow生成.mat文件
  7. 信息学奥赛一本通 1069:乘方计算 | OpenJudge NOI 1.5 13
  8. 计算机二级excel试题练习网盘,计算机二级练习试题excel
  9. AD学习笔记(三)PCB封装库绘制
  10. 【NA】高斯积分公式(二)
  11. 如何批量将图片转换为 Excel 文档
  12. 使用ToUpperInvariant避免使用ToUpper
  13. CVPR 2022 Oral | 视频文本预训练新SOTA!港大腾讯推出基于多项选择题的借口任务...
  14. ubuntu 右键选单没有创建文档
  15. OneNote 英文默认字体修改方法(2020.10)
  16. kodi安卓4.0版及中文插件安装方法
  17. html两行字的上下间隔,css字体上下间距怎么调?
  18. 利用pandas实现json文件转化成csv文件
  19. 矩阵计算(Matrix Computations) 1.3~1.4整理
  20. 小程序收集箱:批量调节图像曝光度、锐利度、对比度、亮度的demo

热门文章

  1. Permissions for id_rsa are too open
  2. ActiveMQ学习笔记(2)——JMS消息模型
  3. Java对数组的操作(二)——集合与数组的切换
  4. Swans and ducks, Piešťany, Slovakia
  5. 开发高级 Web 部件
  6. IBM DS300 安装
  7. 如何revert一个merged branch上所有的改动
  8. SurfaceView介绍
  9. Docker 精通之常用命令
  10. Linux-(C/C++)生成并使用静态库/动态库