C语言,把指针按地上摩擦,爽

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

我们写一段代码:

编译,反编译,反编译这里我们用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」获取学习资料网盘链接。

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/466413.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

MFC CStringArray 字符串数组类 使用方法

https://blog.csdn.net/thanklife/article/details/76836487

C语言总结

C语言高级部分 一、内存大话题1.0、内存就是程序的立足之地,体现内存重要性。1.1、内存理解:内存物理看是有很多个Bank(就是行列阵式的存储芯片),每一个Bank的列就是位宽 ,每一行就是Words,则存…

嵌入生活的嵌入式,超市里的电子价签

纸质价签 VS 电子价签快过年了,今天特意走访了居住地附近的2家超市,不过不是为了买年货,而是为了给这篇文章提供一手的素材。今天我们来聊聊超市里的电子价签。我去的第一家超市,货架上的标签是这样的,这也是我们最常见…

Linux 移除python Error: Trying to remove “yum”, which is protected

>yum intall python >yum -y remove python 出现Error: Trying to remove "yum", which is protected 移除 >rpm -e --nodeps python转载于:https://www.cnblogs.com/eason-d/p/8589037.html

计算t-test 的C程序

/*gdb output 程序还未调试成功:http://ubuntuforums.org/archive/index.php/t-412096.html*//*(gdb) run Starting program: /home/nrh1/code/testt Program received signal SIGSEGV, Segmentation fault. 0x0804967f in var () *//*function to calculate tt…

CListCtrl::InsertColumn()和InsertItem()和SetItemText()

https://blog.csdn.net/qq_25821067/article/details/47095363 InsertColumn() 函数功能:在报告模式下插入一列 函数原型:int InsertColumn(int nCol, const LVCOLUMN* pColumn); int InsertColumn(int nCol, LPCTSTR lpszColumnHeading, in…

张一鸣:大学四年收获及工作感悟

大学里的三点收获2001年我考入了南开大学,起初大学的生活是让人有点失落的,但慢慢地从安静朴素的校园和踏实努力的氛围中,我还是找到了自己的节奏。大学期间我主要在做三件事情 ,一是写代码,因为我是搞技术的&#xff…

C语言实现x的n次方

C语言实现x的n次方#include <stdio.h> // codeblock编辑和编译的#define uint8_t unsigned char #define uint32_t unsigned int #define POWER 16// 求x的n次方&#xff0c;返回x的n次方的值 uint32_t Power(uint8_t x, uint8_t n) {uint8_t i;uint32_t val 1;for(i …

十天内提高单词量到20000! (Vocabulary 10000)

<<Vocabulary 10000>> Lesson 1 A monument was built to commemorate the victory. 一个纪念碑被兴建以记念那场胜利 The children huddled together for warmth. 孩子们卷缩在一起来获得温暖 Censure is sometimes harder to bear than punishment. 有时候忍…

关于JTAG,你知道的和不知道的都在这里

01JTAG简介JTAG&#xff08;JointTest ActionGroup&#xff09;是一个接口&#xff0c;为了这个接口成立了一个小组叫JTAG小组&#xff0c;它成立于1985年。在1990年IEEE觉得一切妥当&#xff0c;于是发布了IEEE Standard 1149.1-1990&#xff0c;并命名为Standard Test Access…

GPS NEMA 0183协议

GPS NEMA 0183协议 一、 NMEA0183标准语句(GPS常用语句) $GPGGA 例&#xff1a;$GPGGA,092204.999,4250.5589,S,14718.5084,E,1,04,24.4,19.7,M,,,,0000*1F 字段0&#xff1a;$GPGGA&#xff0c;语句ID&#xff0c;表明该语句为GlobalPositioning System Fix Data&#xff08;G…

Java:从99瓶子数到0,一个int、String变量、while循环、if条件测试

一、程序执行流程图&#xff1a; 二、代码实现&#xff1a; one: public static void main(String[] args) {int beerNumber99; String beerName"bottles";while (beerNumber<100){ System.out.println(beerNumber" :"beerName); System.out.println…

DoModal

CMFCKeyMapDialog::DoModal Displays a keyboard mapping dialog box显示键盘映射对话框 virtual INT_PTR DoModal(); Return Value A signed integer, such as IDOK or IDCANCEL, that is passed to the CDialog::EndDialog method. The method, in turn, closes the dialog …

开始→运行→输入的命令集锦( 菜鸟必读)

Nslookup&#xff0d;&#xff0d;&#xff0d;&#xff0d;&#xff0d;&#xff0d;&#xff0d;IP地址侦测器 explorer&#xff0d;&#xff0d;&#xff0d;&#xff0d;&#xff0d;&#xff0d;&#xff0d;打开资源管理器 logoff&#xff0d;&#xff0d;&#xff0d;&a…

新唐单片机如何生成精确延迟

最近在搞新唐单片机&#xff0c;所以记录下这部分内容。之前的相关文章呵&#xff0c;你会51单片机的精确延时吗&#xff1f;假如使用者想要产生精确的延迟时间&#xff0c;建议使用 __nop() 函数来组合达成。__nop() 函数能够产生 1 个精确的 CPU 频率周期延迟时间。然而&…

request获取各种路径总结、页面跳转总结。

页面跳转总结 JSP中response.sendRedirect()与request.getRequestDispatcher().forward(request,response)这两个对象都可以使页面跳转&#xff0c;但是二者是有很大的区别的&#xff0c;分条来说&#xff0c;有以下几点&#xff1a;①response.sendRedirect(url)-----重定向到…

CFileDialog

https://baike.baidu.com/item/CFileDialog/9747028

GBT19056精要

GBT19056精要 1、专业术语 1.1脉冲系数impulse ratio&#xff1a;车速传感器在车辆行驶1km距离过程中产生的脉冲信号个数。 1.2行驶开始时间 starttime of travelling&#xff1a;车辆从静止状态转变为行驶状态&#xff08;速度大于0km/h且持续10s以上&#xff09;的时间。 …

看门狗你确定会用了?(经验干货满满)

看门狗&#xff1f;看门狗(watchdog)-字面上的意思就是一条看门的dog&#xff0c;如果一切正常dog就不回叫&#xff0c;如出现异常dog就会叫&#xff0c;并且把你逼到门外&#xff01;其实看门狗不是什么特殊的外设&#xff0c;一般我们叫看门狗也叫做看门狗定时器&#xff0c;…

自定义GridView 介绍

GridView 是Microsoft DataGrid(VS2003版本)的一个替代品&#xff0c;它继承了DataGrid的很多优点&#xff0c;同时也继承了它的很多缺点&#xff0c;我们在方便使用的同时&#xff0c;还是不免会产生一丝遗憾。早在2004年的时候&#xff0c;对DataGrid 进行了二次封装(DotNetG…