通过swap代码分析C语言指针在汇编级别的实现
我们先用C语言写一个交换两个数的代码:
void swap(int *a, int *b){
    int temp = *a;
    *a = *b;
    *b = temp;
}
int main(void)
{
    int x = 12;
    int y = 34;
    swap(&a, &b);
    return 0;
}
我们使用下面的命令进行编译,得到汇编文件:
gcc -o 1.s -S 1.c -m32
查看汇编文件,这里去掉了许多.开头的符号:
swap:
	pushl	%ebp
	movl	%esp, %ebp //
	subl	$16, %esp
	movl	8(%ebp), %eax // a -> eax
	movl	(%eax), %eax  // *a -> eax
	movl	%eax, -4(%ebp) // *a -> temp
	movl	12(%ebp), %eax // b -> eax
	movl	(%eax), %edx // *b -> edx
	movl	8(%ebp), %eax // a -> eax
	movl	%edx, (%eax) // *b -> *a
	movl	12(%ebp), %eax // b -> eax
	movl	-4(%ebp), %edx // temp -> edx
	movl	%edx, (%eax) // edx -> *b
	leave
	ret
main:
	leal	4(%esp), %ecx
	andl	$-16, %esp
	pushl	-4(%ecx)
	pushl	%ebp
	movl	%esp, %ebp
	pushl	%ecx
	subl	$20, %esp
	movl	%gs:20, %eax
	movl	%eax, -12(%ebp)
	xorl	%eax, %eax
	movl	$12, -20(%ebp) // x
	movl	$34, -16(%ebp) // y
	leal	-16(%ebp), %eax // &y -> eax
	pushl	%eax // &y 入栈
	leal	-20(%ebp), %eax // &x -> ebx
	pushl	%eax // &x 入栈
	call	swap
	addl	$8, %esp
	movl	$0, %eax
	movl	-12(%ebp), %edx
	xorl	%gs:20, %edx
	je	.L4
	call	__stack_chk_fail
	movl	-4(%ebp), %ecx
	leave
	leal	-4(%ecx), %esp
	ret
我们先分析main中这几行代码:
	movl	$12, -20(%ebp) // x
	movl	$34, -16(%ebp) // y
	leal	-16(%ebp), %eax // &y -> eax
	pushl	%eax // &y 入栈
	leal	-20(%ebp), %eax // &x -> ebx
	pushl	%eax // &x 入栈
	call	swap
首先前面两行代码分别将12、34压入栈,也就是main中的x和y。
后面有一句leal	-16(%ebp), %eax,leal的意思是将源操作数的地址传给有操作数,所以这句的作用是取y的地址赋给eax。
下一句将eax也就是y的地址压入栈,这个其实是swap的最后一个形参b。
后面两句类似,将x的地址压栈,也就是swap的形参a。
我们看到,函数参数的压栈顺序是从右向左。
然后我们分析swap的代码:
	movl	8(%ebp), %eax // a -> eax
	movl	(%eax), %eax  // *a -> eax
	movl	%eax, -4(%ebp) // *a -> temp
	movl	12(%ebp), %eax // b -> eax
	movl	(%eax), %edx // *b -> edx
	movl	8(%ebp), %eax // a -> eax
	movl	%edx, (%eax) // *b -> *a
	movl	12(%ebp), %eax // b -> eax
	movl	-4(%ebp), %edx // temp -> edx
	movl	%edx, (%eax) // edx -> *b
在这里注意,每当发生函数调用时,先将形参准备好入栈,然后依次是eip、ebp。
由于栈的地址是由高到低增长,所以,在swap中12(%ebp)指的是b,8(%ebp)指的是a,-4(%ebp)指temp。
所以上面代码执行的步骤就是:
	movl	8(%ebp), %eax // a -> eax
	movl	(%eax), %eax  // *a -> eax
	movl	%eax, -4(%ebp) // *a -> temp
分别是将a赋值给eax,然后对a解引用,赋给eax,此时eax中就是*a,也就是x的值。第三行将x的值赋给temp。
	movl	12(%ebp), %eax // b -> eax
	movl	(%eax), %edx // *b -> edx
	movl	8(%ebp), %eax // a -> eax
	movl	%edx, (%eax) // *b -> *a
将b也就是y的地址赋给eax,然后解引用,y的值赋给edx。然后a也就是x的地址赋给eax,最后一行将y的值赋给a指向地址,此时x的值变为y。
	movl	12(%ebp), %eax // b -> eax
	movl	-4(%ebp), %edx // temp -> edx
	movl	%edx, (%eax) // edx -> *b
将b也就是y的地址赋给eax,temp的值赋给temp。
最后一句是将temp的值赋给b指向的位置,也就是temp赋给y。
所以上面总结起来就是:
1. x -> temp
2. y -> x
3. temp -> y
所以x和y的值被交换了。
综合上面,C语言的地址调用没有任何神秘之处。在这里我们更加确定,C语言没有所谓的传址,一切都是传值。
通过swap代码分析C语言指针在汇编级别的实现的更多相关文章
- C语言指针与数组的定义与声明易错分析
		
部分摘自<C语言深度解剖> 1.定义为数组,声明为指针 在文件1中定义: char a[100]; 在文件2中声明: extern char *a; //这样是错误的 这里的extern告 ...
 - C语言-指针深度分析
		
1.变量回顾 程序中的变量只是—段存储空间的别名,那么是不 是必须通过这个别名才能使用这段存储空间? 2.思考 下面的程序输出什么?为什么? ; int* p = &i; p ...
 - C语言指针-从底层原理到花式技巧,用图文和代码帮你讲解透彻
		
这是道哥的第014篇原创 目录 一.前言 二.变量与指针的本质 1. 内存地址 2. 32位与64位系统 3. 变量 4. 指针变量 5. 操作指针变量 5.1 指针变量自身的值 5.2 获取指针变量 ...
 - 【C语言】03-第一个C程序代码分析
		
前面我们已经创建了一个C程序,接下来分析一下里面的代码. 项目结构如下: 一.代码分析 打开项目中的main.c文件(C程序的源文件拓展名为.c),可以发现它是第一个C程序中的唯一一个源文件,代码如下 ...
 - 【C语言】01-第一个c程序代码分析
		
创建了一个C程序,接下来分析一下里面的代码. 项目结构如下: 一.代码分析 打开项目中的main.c文件(C程序的源文件拓展名为.c),可以发现它是第一个C程序中的唯一一个源文件,代码如下: 1 #i ...
 - C程序设计语言--指针和引用的区别
		
在看了一篇文章以后,http://coolshell.cn/articles/7992.html,说的是C和C++之间的缺陷,当然这篇文章说的非常高深了.所以就找了一些资料,分析了这两者的区别 在&l ...
 - C语言指针【转】
		
一.C语言指针的概念 在计算机中,所有的数据都是存放在存储器中的.一般把存储器中的一个字节称为一个内存单元,不同的数据类型所占用的内存单元数不等,如整型量占2个单元,字符量占1个单元等,在前面已有详细 ...
 - 常用 Java 静态代码分析工具的分析与比较
		
常用 Java 静态代码分析工具的分析与比较 简介: 本文首先介绍了静态代码分析的基 本概念及主要技术,随后分别介绍了现有 4 种主流 Java 静态代码分析工具 (Checkstyle,FindBu ...
 - 《C专家编程》第三章——分析C语言的声明
		
前面一章我们已经说过C语言存在的一些问题和它晦涩的地方,让我们对这门神奇的语言有了更深的了解.现在这一章则集中精力来讨论C语言的声明,分为三块,首先是说明C语言声明晦涩难懂的原因和声明是如何形成的,其 ...
 
随机推荐
- Activity生命周期(一) 暨 帮助文档的使用
			
--------siwuxie95 首先创建一个ActivityLifeCircle 选择API:21 Android 5.0 (截止目前:2016/12/21,承上启下,兼容更好) 选择空活动 ...
 - No module named yum错误的解决办法
			
今天用yum安装软件的时候出现如下错误: There was a problem importing one of the Python modules required to run yum. Th ...
 - Java核心知识点学习----线程中如何创建锁和使用锁 Lock,设计一个缓存系统
			
理论知识很枯燥,但这些都是基本功,学完可能会忘,但等用的时候,会发觉之前的学习是非常有意义的,学习线程就是这样子的. 1.如何创建锁? Lock lock = new ReentrantLock(); ...
 - 如何MSHTML命名空间解析HTML文件(MSHTML::IHTMLDocument2Ptr 提示错误)
			
1.创建Win32或MFC工程. 2.在预编译或需要使用MSHTML命名空间的头文件中添加以下语句: #include <atlbase.h> #include <Mshtml ...
 - 問題排查:行動裝置網頁前端 UI 設計 (1)
			
這是最近開始接手的一個微信公眾平台專案, 在重整後端程式碼時,因為也需要透過前端來看效果, 所以就因此在前端的部分遇到了不少問題, 畢竟這是以前沒接觸過的領域 (早年的網頁應用程式開發沒有那麼多分工) ...
 - 咏南IOCP中间件
			
咏南IOCP中间件 特大好消息,咏南中间件系列新增加——咏南IOCP中间件.咏南IOCP中间件完全兼容咏南DATASNAP中间件的远程方法接口. 中间件DELPHI7~DELPHI XE10.1.1都 ...
 - Web开发中的主要概念
			
一.Web开发中的主要概念1.静态资源:一成不变的.html.js.css2.动态资源:JavaWeb.输出或产生静态资源.(用户用浏览器看到的页面永远都是静态资源) 3.JavaEE:十三种技术的集 ...
 - Gossip protocol(zz)
			
Gossip protocol 这是一系列用于P2P的通信协议.简单来说,就是模拟人类社会中流言传播的方式.每个节点随机地把消息发给它的邻居,接到消息的节点,如果之前没收到这个消息,则会继续随机地转发 ...
 - html5 文件上传 带进度条
			
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/ ...
 - Python3学习(1)-基础篇
			
Python3学习(1)-基础篇 Python3学习(2)-中级篇 Python3学习(3)-高级篇 安装(MAC) 直接运行: brew install python3 输入:python3 --v ...