这里更多探究的是指针的机制。

debug对下面程序进行分析,记录每一条C语句运行后,相关内存单元的值。

程序a.c

注意理解指针机制

我们编写如下代码:

编译加载进debug查看:

我们先看其反汇编的代码:

在这里我们对比着C语句分析:

第一条语句是将1000H(16进制,以下16进制用H说明,未说明的都为十进制数据。且汇编语言数据都为16进制,不加H说明。)。放置在变量P(偏移地址01FAH)处。

第二句中,使用*的地方,即*p处,这里的语句被翻译成两句,即:MOV BX,[01FA]和ADD AL,[bx]。在这里我们知道,在汇编中,[]代表的是地址,也就是说先将01FAH地址处的数据放到BX中然后在将DS:[BX]中的数据加到AL中。

第三句中,在使用&处的语句,即p=&ch;处,我们看到在上条语句中,AL最后放到了01A8H处,也就是01A8H处是ch的存放位置。我们看到,p=&ch;这条语句被翻译成了:WORD PTR [01FA],01A8。也就是说,将ch在内存中的存放位置这个数,放在了DS:[01FA]处。从这里我们能看出。1.指针类型也是存放在内存中,存放的内容是一个地址。2.指针类型是一个2字节的。

第四句中,是用*P处。我们看到,首先,将p的地址中存放的地址(也就是ch所占的内存),放到BX中,然后将DS:[BX]的数据放到al中,inc al。我们可以看到,*指针所对应的操作是取指针指向的内存处的数据。

第五句,这里看到了双指针,我们来看,分析上下文我们得出,这句赋值的语句被翻译成了MOV BX,[01A6] 我们看到,这里是将指针P的地址放在了pa中。

我们查看单步执行的过程,对比我们的分析:

在这里我们看到,第一二句执行的时候,确实是将1000H处的数据放到了AL中,也就是P指向的位置的数据。

在这里我们看到,对*p的处理,是将指针内的数据给BX,并将DS:[BX]处的数据放在AL中。

我们看到,这里是将指针p的地址给了pa。

这里调用的时候,是先将01A6H处的数据传给BX,再将DS:[BX]处的数据放到BX中,也就是说*的作用是将这里的数据当做地址向外拿数据。

在这里我们看到char far*,在放入地址的时候,将DS也放入的进去,这里也可以说明far型的指针变量的大小为四个字节。

而且,指针变量还可以被转换为int型的变量。转换过后,是将指针指向的地址作为int型变量的值。

*(char *)n这里是将n的值作为地址,取这里的char型的值。

综合来看,指针是一种特殊的类型,指针内存放(这里是指针内,存放)的是一个地址。他有2字节和4字节两种形式。&是取地址符,*是取指针指向位置的数据。

程序b.c

注意Struct指针的用法,指针“+”运算的意义。

我们编写程序如下:

我们在反汇编后看到,首先,pstuàname[1]与((*pstu).name[2])在翻译过后的形式是一样的,都是MOV BYTE PTR [ ], 这样的形式。也就是说他们的功能是一样的。其次,对于Struct指针,记录的是Struct的首地址。最后,当指针做“+”运算的时候,其实加上的不是定值,而是它所指向的类型的长度。对于char型是1,对于int型是2,对于自定义类型,是其自定义的数据的长度之和。

我们查看单步的情况:

那么,这样做的意义在哪儿?指针自加,所加上的是其指向的数据类型的长度,那么我们就可以方便的在程序中使用指针,也可以在循环中使用指针。并且,这样的方式使得程序寻找内存的方式更加多样,更加灵活。

程序c.c

注意理解“[]”运算的意义及数组名与指针的关系。

从整体来说,在这里我们可以看出,p的地址是01d0H,pf的地址是01ceH和01ccH;str的首地址为01caH,而01caH里边存的数据是字符串的首地址0194H;a的首地址为01a8H。

我们可以分析出的一个for循环的位置,是从0206H到022eH。

将01CAH(01CA中结合上下语句可以看出是放的N)处的数据放在BX中,然后将BX+0194H处的数据放到AL中,即是数组中的值。 01caH处放的是pf+n的数据,*(pf+n)就是将[1ca]给了bx,然后将al给了[bx],完成在0:200中写入字符串。

第二个for是024AH到026AH,这里我们可以看出*(str+n)功能与*str+*n是相同的。

第三个for是从026EH到029BH,这里我们看出,其inc WORD PTR[01AC]这条指令被替换成了两条指令:SHL DX,1 shl bx,1和inc word ptr [01CA]。这是因为所取的数据变成了int型。

第四个for,在实现上与第三个for是类似的。

综合来看,数组在底层的实现过程和指针在底层实现的过程是一样的。数组名就相当于指向数组空间首地址的指针。而‘数组[下标]’的方式就相当于‘指针+数据类型长度*下标’的方式。

深入研究C语言 第四篇的更多相关文章

  1. 深入研究C语言 第三篇

    本篇研究TC2.0下其他几个工具.同时看看TC由源代码到exe程序的过程. 1. 用TCC将下面的程序编为.obj文件 我们知道,TCC在默认的编译连接一个C语言的源程序a.c的时候分为以下两步: ( ...

  2. 深入研究C语言 第一篇(续)

    没有读过第一篇的读者,可以点击这里,阅读深入研究C语言的第一篇. 问题一:如何打印变量的地址? 我们用取地址符&,可以取到变量的偏移地址,用DS可以取到变量的段地址. 1.全局变量: 我们看到 ...

  3. Flask最强攻略 - 跟DragonFire学Flask - 第四篇 Flask 中的模板语言 Jinja2 及 render_template 的深度用法

    是时候开始写个前端了,Flask中默认的模板语言是Jinja2 现在我们来一步一步的学习一下 Jinja2 捎带手把 render_template 中留下的疑问解决一下 首先我们要在后端定义几个字符 ...

  4. flask 第四篇 模板语言jinja2

    是时候开始写个前端了,Flask中默认的模板语言是Jinja2 现在我们来一步一步的学习一下 Jinja2 捎带手把 render_template 中留下的疑问解决一下 首先我们要在后端定义几个字符 ...

  5. R语言学习 第四篇:函数和流程控制

    变量用于临时存储数据,而函数用于操作数据,实现代码的重复使用.在R中,函数只是另一种数据类型的变量,可以被分配,操作,甚至把函数作为参数传递给其他函数.分支控制和循环控制,和通用编程语言的风格很相似, ...

  6. 解剖SQLSERVER 第十四篇 Vardecimals 存储格式揭秘(译)

    解剖SQLSERVER 第十四篇    Vardecimals 存储格式揭秘(译) http://improve.dk/how-are-vardecimals-stored/ 在这篇文章,我将深入研究 ...

  7. RabbitMQ学习总结 第四篇:发布/订阅 Publish/Subscribe

    目录 RabbitMQ学习总结 第一篇:理论篇 RabbitMQ学习总结 第二篇:快速入门HelloWorld RabbitMQ学习总结 第三篇:工作队列Work Queue RabbitMQ学习总结 ...

  8. 第四篇 Integration Services:增量加载-Updating Rows

    本篇文章是Integration Services系列的第四篇,详细内容请参考原文. 回顾增量加载记住,在SSIS增量加载有三个使用案例:1.New rows-add rows to the dest ...

  9. [老老实实学WCF] 第四篇 初探通信--ChannelFactory

    老老实实学WCF 第四篇 初探通信--ChannelFactory 通过前几篇的学习,我们简单了解了WCF的服务端-客户端模型,可以建立一个简单的WCF通信程序,并且可以把我们的服务寄宿在IIS中了. ...

随机推荐

  1. ajax同步异步问题

    之前一直在写JQUERY代码的时候遇到AJAX加载数据都需要考虑代码运行顺序问题.最近的项目用了到AJAX同步.这个同步的意思是当JS代码加载到当前AJAX的时候会把页面里所有的代码停止加载,页面出去 ...

  2. python Tornado(招聘的一个比较经常问到的知识)

    Tornado既是一个webserver也是一个web框架 这是一个总结的比较详细的内容 http://www.nowamagic.net/academy/detail/1332612 开源中国中的关 ...

  3. 无向图的DFS遍历(方法之一)

    如果看不懂辅助解释在后面第点 1.录入方式: 输入 u - v  表示一边的2个端点 2.存储结构 struct edge { int from; int to; int next; } e[MAXN ...

  4. Sql Server隔离级别(2)

    Sql Server2005之后,引入了一个新的隔离级别Snapshot(Read Committed Snapshot Isolation (RCSI))和(Snapshot Isolation ( ...

  5. 机器学习--Classifier comparison

    最近在学习机器学习,学习和积累和一些关于机器学习的算法,今天介绍一种机器学习里面各种分类算法的比较 #!/usr/bin/python # -*- coding: utf-8 -*- "&q ...

  6. iOS 开发 常用的正则验证表达式:电话 、邮箱等等

    #pragma mark - 验证手机号 +(BOOL)checkForMobilePhoneNo:(NSString *)mobilePhone{ NSString *regEx = @" ...

  7. JS中this的指向问题

    JS中this的定义:this对象是在运行时基于函数的执行环境绑定的(通俗点来说就是:this代表当前函数属于哪个对象). this一般情况下都代表的是global对象,在浏览器中就是window对象 ...

  8. 转-servlet 获取 post body 体用流读取为空的问题

    目前基于rest风格的很多API开始使用通过body data来传输来代替之前的key-value传输方式.在Java servlet或者springmvc中可以通过如下代码来获取并图片通过流方式传输 ...

  9. Struts——(四)异常处理机制

    在通常的情况下,我们得到异常以后,需要将页面导航到一个错误提示的页面,提示错误信息.利用Stuts我们可以采用两种方式处理异常: 1.编程式异常处理 即我们在Action中调用业务逻辑层对象的方法时, ...

  10. 利用Native Client OLEDB 11 高效率地对SQL SERVER 进行查询和插入操作

    前言: 鄙司原始用的都是ADO来访问数据库,而我现在着手的项目是从我的GPS历史数据库中,取出历时数据的一个接口,一个DLL.用ADO写完之后,测试下来,平均4000条的数据,需要 180 毫秒左右. ...