`
dogasshole
  • 浏览: 842459 次
文章分类
社区版块
存档分类
最新评论

C语言一些特性的分析

 
阅读更多
这篇文章主要是讲解了一些c语言里边容易让人迷惑的方面,通过分析程序的汇编代码来解释.这里边的东西在<C专家编程>这本书里边讲的很清楚的

在这里我主要是想用汇编代码进行一下说明,看看到底是怎么回事.


编译器gcc 有的代码进行了2级的优化,当然不进形优化会看的更清除gcc -S -O2


BY YANGXI


1.让我们来看看数组和指针的相同和不同的地方.我们首先应该明白一个基本的概念:对于x=y这个表达式,符号x代表一个地址(在这里叫做左值.)而符号y在这里代表一个地址里边的内容(在这里叫做右值).我们看下边这断code.

void a()

{

int *p

int a[10];

p = a;

p[2] = 2;

a[3] = 3;

}

下面是a函数的汇编代码

a:

pushl %ebp

movl %esp, %ebp

subl $72, %esp ;这里给p和a[10]分配空间

leal -72(%ebp), %eax ;将a数组的第一个元素的地址传递给p,(p的地址是%ebp-12,然后p的地址的内容是a数组第一个元素的地址)

movl %eax, -12(%ebp)

movl -12(%ebp), %eax

addl $8, %eax

movl $2, (%eax);现在eax里面放的就是a第3个元素的地址

movl $3, -60(%ebp)

leave

ret

在上面的例子里我们看到对p[2]的寻址过程是


1,首先得到p的地址.

2,得到p地址中的内容(a数组第一个数组的地址).

3,*p + sizeof(int)*2的计算得到数组a的第三个元素的地址.

4,向这个地址写数据.


但是对于a[3]的寻址却是 ebp-72 + sizeof(int)*3 计算得到ebp - 60比指针少一个从指针的地址取

数据的过程,这就是指针和数组的一个不同点.我们看下边的例子.

int p[3];

void main()

{

extern int *p;

p[2] = 4;

}

当编译这个程序的时候产生错误conflicting types for p .我们来假设能够运行会产生什么惊人的效果.

由于p在main中被声明为指针所以p[2]会按照指针的方式进行寻址

1,首先得到p的地址(假设为 a).

2,得到a中的内容(假设是b)作为数组的第一个元素的地址(b其实就是数组第一个元素在这里却被当成了数组中第一个元素的地址)由于这一步错了所以后边也就错了,最后4被写到了一个错误的地址中去.这就是为什么我们对于指针和数组的声明和定义的形式必需相同的原因了.但是为什么我们在函数中定义了int *p int a[10] 却可以在表达式和函数参数传递的时候混用呢,这是因为ansic规定

在表达试和参数传递中把数组名a作为指向第一个元素的指针,所以下边的代码就没有问题了.

void a(int *p)

{

p[0] = 1;

}

void main()

{

int p[2];

a(p);

*p = 2;

}

我们来看一下它的汇编代码

a:

pushl %ebp

movl %esp, %ebp

movl 8(%ebp), %eax

movl $1, (%eax)

leave

ret

main:

pushl %ebp

movl %esp, %ebp

subl $8, %esp

andl $-16, %esp

leal -8(%ebp), %eax ;ebp - 8就是我们的数组p第一个元素的地址了

subl $12, %esp

pushl %eax ;这里就是我们传递给a函数的p(在这里编译器把它转换成了指针)

call a

popl %eax

popl %edx

movl $2, -8(%ebp)

2,我们在写程序的时候经常这样来定义一个字符常量char *p = "hello world"如果这个时候想p[2] = 'C'就会产生错误,这又是为什么呢,我们看下边的代码.

void

main()

{

char *p = "hello world";

p[0] = 'a';

}

这个程序在运行的时候会产生段错误.为什么呢,我们来看一下它的汇编代码

.section .rodata.str1.1,"aMS",@progbits,1

.LC0:

.string "hello world";原来他们在定义的时候被放到了.rodata这个只读的段里面,所以我们在往这个地址些数据的时候产生了段错误

.text

.p2align 2,,3

.globl main

.type main, @function

main:

pushl %ebp

movl %esp, %ebp

subl $8, %esp

andl $-16, %esp

movb $97, .LC0

leave

ret

那我们下边的代码会产生什么后果呢

void

main()

{

int *p = {1,2,3};

p[0] = 8;

}

又产生了段错误不过这次产生段错误的原因可就大不一样了,让我们看看到底是怎么回事把

ain:

pushl %ebp

movl %esp, %ebp

subl $8, %esp

movl $8, 1 ;出现了一个1是不是感到很好奇,其实这就是P[0]= 8 让我们来分析一下.首先在初始话的时候p的地址的内容被初始化成了

1(在这里只给p分配了一个四个节的空间放p的内容,在这里会被付值成1.然而我们想让p所指向的数据却没有分配空间,而且他们也没有被放到.rodata中去,真可怜,看来只有char *能享受.radata VIP级别的待遇了)还记得我们前边说的指针怎么寻址了马,得到p的地址的内容就是数组的第一个元素的地址,p的地址的内容就是1所以会有这个1的出现,这个程序访问了不该访问的地址,它死定了,导致了段错误

andl $-16, %esp

leave

ret

3,p++ ++p的区别,这个估计大家都知道把,那我们来看看它到底为什么不一样,我们来看下边的代码


void main()

{

int a,b,c;


a = 1;

b = a++;

c = ++a;

}

我们来看一下汇编代码

main:

pushl %ebp

movl %esp, %ebp

subl $24, %esp

andl $-16, %esp

movl $0, %eax

subl %eax, %esp

movl $1, -4(%ebp) ;ebp - 4 里边放的是a. 这行是a = 1

movl -4(%ebp), %edx;把a的内容放到了edx其实就是b

movl %edx,-8(%ebp) ;这里马上就把b些到了ebp-8的地址里边也就是也到了b的地址里边所以 b也是1

leal -4(%ebp), %eax

incl (%eax);等付值完了以后在对a的内容进行加操作

leal -4(%ebp), %eax

incl (%eax);这里和上边正相反,这里是先对地址中的内容进行了加的操作然后才把它放到寄存器中去进行c = a 的付值运算

movl -4(%ebp), %eax

movl %eax, -12(%ebp)

leave

ret

上边的代码很清楚的表达了他们之间的区别b = a++ 是先方到寄存器中去参加运算然后改变存储器中的value c= ++a却是相反的.

这里就举这几个很简单的例子,其实还有很多这样的例子,比如说为什么函数不能返回在函数内部定义的数组等等,我的目的是抛砖引玉.如果对这个感兴趣完全可以自己来分析.我认为语言其实就是对机器的

一层一层的抽象,语言的目的不同,思想不同抽象的程度和方法就不同.但他们最后都要变成汇编代码,然后用链接器生成可执行文件.突然间想起了

the tao of programming 里边的话


The Tao gave birth to machine language. Machine language gave birth to the assembler.

道生机器语言,机器语言生汇编嚣。


The assembler gave birth to the compiler. Now there are ten thousand languages.

汇编器生编译器,最后产生上万种高级语言。


Each language has its purpose, however humble. Each language expresses the Yin and Yang of software.

Each language has its place within the Tao.

不论多么的微不足道,每种语言都有它自己的目的,每种语言都表达了软件的阴阳两极。每种语言都各得其道.

分享到:
评论

相关推荐

    现代C语言核心特性解析,C语言编程与宠物狗互动案例分析.pdf

    # 现代C语言核心特性解析,C语言编程与宠物狗互动案例分析 本文主要介绍现代C语言的核心特性和如何使用C语言与宠物狗进行互动。现代C语言是C语言的一种现代化发展,具有更加丰富的特性和更高的安全性。 在本文中,...

    编译原理C语言子集词法分析器.zip

    编译原理C语言子集词法分析器 编译原理中的C语言子集词法分析器是一个简化版的词法分析器,它只识别和处理C语言的一个子集。这样的分析器通常用于教学目的,帮助学生理解词法分析的基本概念和实现方法。以下是一个...

    C语言的词法分析和抽象语法树(AST)分析器.zip

    - 从一个简单的C语言子集开始,逐步增加支持的语法和语义特性。 - 使用单元测试和集成测试来验证分析器的正确性。 - 编写详细的文档,记录设计决策、实现细节和测试结果。 - 考虑使用版本控制系统(如Git)来管理...

    C语言半自动词法分析器和半自动语法分析器.zip

    - 从一个简单的C语言子集开始,逐步增加支持的语法和词法特性。 - 使用单元测试和集成测试来验证分析器的正确性。 - 编写详细的文档,记录设计决策、实现细节和测试结果。 - 考虑使用版本控制系统(如Git)来管理...

    同济大学编译原理课程作业,使用LR1分析实现类C语言的词法、语法分析器.zip

    - 从一个简单的类C语言子集开始,逐步增加支持的语法和语义特性。 - 使用单元测试和集成测试来验证分析器的正确性。 - 编写详细的文档,记录设计决策、实现细节和测试结果。 - 考虑使用版本控制系统(如Git)来管理...

    C:C++编写的类C语言词法分析器.zip

    - 从一个简单的类C语言子集开始,逐步增加支持的词法特性。 - 使用单元测试来验证分析器的正确性。 - 编写文档,记录设计决策和实现细节,便于项目维护和他人理解。 通过实现这样一个词法分析器,开发者可以深入理解...

    Java编写的一个类C语言编译器(词法分析,语法分析,语义分析和目标代码生成).zip

    - 从一个简单的类C语言子集开始,逐步增加支持的语法和语义特性。 - 使用单元测试和集成测试来验证分析器的正确性。 - 编写详细的文档,记录设计决策、实现细节和测试结果。 - 考虑使用版本控制系统(如Git)来管理...

    算法设计与分析PPT(C语言完整版)

    1.1.2算法及其要素和特性 1.1.3算法设计及基本方法 1.1.4从算法到实现 1.2算法描述 1.2.1算法描述简介 1.2.2算法描述约定 1.2.3一个简单问题的求解过程 1.3现代常用算法概览* 1.3.1压缩算法 1.3.2加密算法 1.3.3人工...

    谭浩强 C语言教程

    本书的一个鲜明特色就是结合大量示例描述c语言的重要特征,并对很多工作代码给出了逐步的分析,以这种独特的教学方法向读者解释新接触的编程元素及一些惯用法。 本书系统、完整,可作为C语言的参考手册,也非常适合...

    C语言解析教程(原书第4版)(美) 凯利.pdf

    《c语言教程(原书第4版)》的一个鲜明特色就是结合大量示例描述c语言的重要特征,并对很多工作代码给出了逐步的分析,以这种独特的教学方法向读者解释新接触的编程元素及一些惯用法。  《c语言教程(原书第4版)》...

    C语言入门经典(第4版)--源代码及课后练习答案

    该资料是《C语言入门经典(第4版)》的源代码及课后练习答案 对应的书籍资料见: C语言入门经典(第4版) 基本信息 原书名: Beginning C: From Novice to Professional, Fourth Edition 原出版社: Apress 作者: ...

    《C语言程序设计》课程期末样卷7份,2023第一学期《C语言程序设计》期末复习资料

    《C语言程序设计》是一门计算机科学与技术专业经典的必修课程,旨在培养学生的编程能力和解决问题的思维方式,本资源是《C语言程序设计》配套的...C语言应用实例:通过实际案例分析和编程实践,让学生掌握C语言的应用。

    U+L型通风特性分析

    U+L型通风特性分析,刘玉胜,周振,U+L型通风特性研究对处理工作面瓦斯有着重要的影响。本文分析了U+L型通风的瓦斯空间分布和来源,导出了描述U+L型通风方式风量配比的

    基于C语言编写forp PHP分析器

    总结的特性 : PHP7 编译时要使用(--enable-dtrace) PHP7 使用时需要设置环境变量(export USE_ZEND_DTRACE=1) 每个函数的时间和分配内存的测量 CPU 使用率 函数调用的文件和行号 输出为谷歌的跟踪事件格式 字幕功能 ...

    C语言程序设计课件(经典)

    绝对是新手的必备良师,,很好的分析了C语言的特性、、、

    c语言实现单表仿射密码.zip

    c语言来具体实现单表仿射密码的加密解密过程,首先要求输入要处理的密文或明文,接着要求输入密钥a和b的值,接着输入0来表示对所给明文加密,输入1来表示对所给密文解密。然后程序根据调用函数gcd判断输入的密钥a与...

    基于C语言的职工资源管理系统毕业,使用C语言的数据结构、指针、文件操作等特性来处理大量的职工信息和数据

    职工资源管理系统是一种用于管理公司或组织...我们将使用C语言的数据结构、指针、文件操作等特性来处理大量的职工信息和数据。同时,我们将运用图形界面库来构建用户友好的界面,提高用户体验。 总之,我们的目标是开

    C语言的双链表

    1.特性 内核链表是一种双向循环链表,内核链表的节点节点结构中只有指针域 使用内核链表的时候,将内核链表作为一个成员放入到一个结构体中使用 我们在链表中找到内核链表结构的地址,通过这个地址就可以找到外部...

    c语言c++项目源代码_c语言做的一个任务管理器.rar

    本文介绍一款用C语言编写的任务管理器,它提供实时进程监控、一键结束无用进程、系统资源占用分析等功能,帮助用户更有效地管理系统资源。 该任务管理器采用C语言编写,运行效率高,对系统资源的占用较低。它能够...

    C语言程序设计标准教程

     现在我们可以从函数定义、 函数说明及函数调用的角度来分析整个程序,从中进一步了解函数的各种特点。程序的第1行至第5行为max函数定义。进入主函数后,因为准备调用max函数,故先对max函数进行说明(程序第8行)。...

Global site tag (gtag.js) - Google Analytics