我们先用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语言指针在汇编级别的实现的更多相关文章

  1. C语言指针与数组的定义与声明易错分析

    部分摘自<C语言深度解剖> 1.定义为数组,声明为指针 在文件1中定义: char a[100]; 在文件2中声明: extern char *a; //这样是错误的 这里的extern告 ...

  2. C语言-指针深度分析

    1.变量回顾 程序中的变量只是—段存储空间的别名,那么是不 是必须通过这个别名才能使用这段存储空间? 2.思考 下面的程序输出什么?为什么? ;    int* p = &i;       p ...

  3. C语言指针-从底层原理到花式技巧,用图文和代码帮你讲解透彻

    这是道哥的第014篇原创 目录 一.前言 二.变量与指针的本质 1. 内存地址 2. 32位与64位系统 3. 变量 4. 指针变量 5. 操作指针变量 5.1 指针变量自身的值 5.2 获取指针变量 ...

  4. 【C语言】03-第一个C程序代码分析

    前面我们已经创建了一个C程序,接下来分析一下里面的代码. 项目结构如下: 一.代码分析 打开项目中的main.c文件(C程序的源文件拓展名为.c),可以发现它是第一个C程序中的唯一一个源文件,代码如下 ...

  5. 【C语言】01-第一个c程序代码分析

    创建了一个C程序,接下来分析一下里面的代码. 项目结构如下: 一.代码分析 打开项目中的main.c文件(C程序的源文件拓展名为.c),可以发现它是第一个C程序中的唯一一个源文件,代码如下: 1 #i ...

  6. C程序设计语言--指针和引用的区别

    在看了一篇文章以后,http://coolshell.cn/articles/7992.html,说的是C和C++之间的缺陷,当然这篇文章说的非常高深了.所以就找了一些资料,分析了这两者的区别 在&l ...

  7. C语言指针【转】

    一.C语言指针的概念 在计算机中,所有的数据都是存放在存储器中的.一般把存储器中的一个字节称为一个内存单元,不同的数据类型所占用的内存单元数不等,如整型量占2个单元,字符量占1个单元等,在前面已有详细 ...

  8. 常用 Java 静态代码分析工具的分析与比较

    常用 Java 静态代码分析工具的分析与比较 简介: 本文首先介绍了静态代码分析的基 本概念及主要技术,随后分别介绍了现有 4 种主流 Java 静态代码分析工具 (Checkstyle,FindBu ...

  9. 《C专家编程》第三章——分析C语言的声明

    前面一章我们已经说过C语言存在的一些问题和它晦涩的地方,让我们对这门神奇的语言有了更深的了解.现在这一章则集中精力来讨论C语言的声明,分为三块,首先是说明C语言声明晦涩难懂的原因和声明是如何形成的,其 ...

随机推荐

  1. Python全栈--7.3--模块补充configparser--logging--subprocess--os.system--shutil

    模块补充: 一.configparser用于处理特定格式的文件,其本质是利用open来操作文件 继承到2版本 configparser 实现了更多智能特征,更有壳预见性,新的应用更偏好这个版本, 处理 ...

  2. MVC学习地址

    http://www.cnblogs.com/n-pei/tag/Asp.net%20MVC/

  3. 对CLR基本原理概念&垃圾回收机制的简单理解

    前言,之前有说过C语言的函数&变量的一些基本概念,说得可能不是很好,先也把C#的.里相关的也说下,已成一统. 而说函数变量,其实主要就是GC,而GC又是CLR的主要内容,故就有了此文. CLR ...

  4. [PHP]Yaf + composer 引起大幅性能下降

    composer.json 文件可以用命令 composer init 创建,命令是交互式的. 也可以直接编辑一个 json 文件,如下: repositories 中 url 使用中国全量镜像地址. ...

  5. Oracle primary,unique,foreign 区别,Hibernate 关联映射

    Oracle primary,unique,foreign 区别 转:http://www.cnblogs.com/henw/archive/2012/08/15/2639510.html NOT N ...

  6. 本地调试webapi

    1.新建iis站点,路径关联到代码站点下D:\work\易解科技\程序源码\YQJ\trunk\YQJOpenAPI\YQJOpenAPI 2.vs以管理员身份启动 3.附加到进程 w3wp.exe ...

  7. maven 配置篇 之pom

    maven 配置篇 之pom.xml(一) 博客分类:  pm mavenXML配置管理项目管理junit      说完了settings.xml配置,下来说一下maven2的主要配置pom.xml ...

  8. P2320 [HNOI2006]鬼谷子的钱袋

    洛谷2320 06湖南 鬼谷子的钱袋 来源 题目描述 鬼谷子非常聪明,正因为这样,他非常繁忙,经常有各诸侯车的特派员前来向他咨询时政.有一天,他在咸阳游历的时候,朋友告诉他在咸阳最大的拍卖行(聚宝商行 ...

  9. Uploadify 上传文件插件详解

    Uploadify 上传文件插件详解 Uploadify是JQuery的一个上传插件,实现的效果非常不错,带进度显示.不过官方提供的实例时php版本的,本文将详细介绍Uploadify在Aspnet中 ...

  10. Android 内存监测工具 DDMS --> Heap

    一.什么是内存泄露    内存泄露是指程序中间动态分配了内存,但是在程序结束时没有释放这部分内存,从而造成那一部分内存不可用.导致系统运行变慢或应用程序崩溃.二.如何检测Android中的内存泄露   ...