C语言以数组和结构体传参
2016-05-21 10:33
447 查看
C语言函数调用时的传参操作在32位x86机器上依赖栈进行.而在x86_64的机器上使用了部分寄存器作为辅助,但如果参数过多,寄存器不够使用,此时也必须借助于栈操作实现传参.尽管C语言对函数传递参数的个数没有明确限制(依编译器实现而定:http://stackoverflow.com/questions/9034787/function-parameters-max-number),但过多的参数传递势必影响代码执行效率.
通常C语言函数传参是非常明确的,如下面这个函数:int test(int a,float b,int *pointer);注:以下例子均使用32位x86,gcc编译器说明.
但如果把数组或结构体作为参数传递呢?到底传递了多少参数,占用了多少栈空间?
typedef struct list{
int a;
int b;
int c;
int d;
int e;
}list;
int test1(int array[5]);
int test2(list listtest);
先看数组的例子:
array:
.LFB0:
pushl %ebp
movl %esp, %ebp
subl $8, %esp
movl 8(%ebp), %eax
addl $8, %eax
movl (%eax), %eax
subl $8, %esp
pushl %eax
pushl $.LC0
call printf
addl $16, %esp
nop
leave
ret
首先是老套的操作:保存ebp,同步ebp和esp,esp向下移动,以建立新函数栈环境.然后取8(%ebp).what is it?其实函数执行到这里经过了3次压栈:pushl %eax,call array,pushl %ebp.而每一次压栈都是32位,也就是4个字节.所以0(%ebp)是pushl %ebp的值,4(%ebp)是函数返回地址,8(%ebp)是pushl %eax的值,即数组地址.当然后面的addl $8,%eax就是tmp[2]的地址了,不再赘述.说了这么多,总之一句话,array(test)仅仅是传递了一个数组地址而已,并没有把整个数组元素一起作为参数传给子函数.
再来看结构体的例子:
#include<stdio.h>
typedef struct list{
int a;
int b;
int c;
int d;
}list;
void test(list tmp){
printf("%d\n",tmp.b);
}
int main(void){
list tmp={.a=10,.b=20,.c=30,.d=40};
test(tmp);
}
同样截取main函数参数传递片段:
可以看到,这下是实实在在把结构体中的成员全部压栈了.因此在开源项目代码中,常常不会看到以结构体作为参数的,而是传递一个指向该结构体的指针.这样无论结构体有多少成员,压栈也仅仅压入一个值了.如下面的代码:
通常C语言函数传参是非常明确的,如下面这个函数:int test(int a,float b,int *pointer);注:以下例子均使用32位x86,gcc编译器说明.
但如果把数组或结构体作为参数传递呢?到底传递了多少参数,占用了多少栈空间?
typedef struct list{
int a;
int b;
int c;
int d;
int e;
}list;
int test1(int array[5]);
int test2(list listtest);
先看数组的例子:
/*passing arguments test:array*/ #include<stdio.h> void array(int tmp[5]){ printf("%d\n",tmp[2]); } int main(void){ int test[]={1,2,3,4,5}; array(test); }编译成汇编代码,先截取main函数传参部分:
movl $1, -32(%ebp) movl $2, -28(%ebp) movl $3, -24(%ebp) movl $4, -20(%ebp) movl $5, -16(%ebp) leal -32(%ebp), %eax pushl %eax call array可以看到,在main函数中先将数组元素写入数组空间内,然后将数组地址(即元素a[0]的地址)保存在eax中,接着把eax压栈,最后调用array函数.再看看array函数:
array:
.LFB0:
pushl %ebp
movl %esp, %ebp
subl $8, %esp
movl 8(%ebp), %eax
addl $8, %eax
movl (%eax), %eax
subl $8, %esp
pushl %eax
pushl $.LC0
call printf
addl $16, %esp
nop
leave
ret
首先是老套的操作:保存ebp,同步ebp和esp,esp向下移动,以建立新函数栈环境.然后取8(%ebp).what is it?其实函数执行到这里经过了3次压栈:pushl %eax,call array,pushl %ebp.而每一次压栈都是32位,也就是4个字节.所以0(%ebp)是pushl %ebp的值,4(%ebp)是函数返回地址,8(%ebp)是pushl %eax的值,即数组地址.当然后面的addl $8,%eax就是tmp[2]的地址了,不再赘述.说了这么多,总之一句话,array(test)仅仅是传递了一个数组地址而已,并没有把整个数组元素一起作为参数传给子函数.
再来看结构体的例子:
#include<stdio.h>
typedef struct list{
int a;
int b;
int c;
int d;
}list;
void test(list tmp){
printf("%d\n",tmp.b);
}
int main(void){
list tmp={.a=10,.b=20,.c=30,.d=40};
test(tmp);
}
同样截取main函数参数传递片段:
movl $10, -24(%ebp) movl $20, -20(%ebp) movl $30, -16(%ebp) movl $40, -12(%ebp) pushl -12(%ebp) pushl -16(%ebp) pushl -20(%ebp) pushl -24(%ebp) call test
可以看到,这下是实实在在把结构体中的成员全部压栈了.因此在开源项目代码中,常常不会看到以结构体作为参数的,而是传递一个指向该结构体的指针.这样无论结构体有多少成员,压栈也仅仅压入一个值了.如下面的代码:
#include<stdio.h> typedef struct list{ int a; int b; int c; int d; }list; void test(list *tmp){ printf("%d\n",tmp->b); } int main(void){ list tmp={.a=10,.b=20,.c=30,.d=40}; test(&tmp); }
相关文章推荐
- C语言实现带自定义超时时间的telnet端口连通性检测功能
- leetcode #18 in cpp
- 值得推荐的开源C/C++框架和库
- delete一个void*可能会造成泄漏内存
- Box2D C++ 教程-开发环境设置(iPhone)
- Box2D C++ 教程-连接器-平移
- Box2D C++ 教程-连接器-旋转
- Box2D C++ 教程-连接器-概述
- Box2D C++ 教程-幽灵顶点
- Box2D C++ 教程-跳跃问题
- Box2D C++ 教程-安全地移除物体
- c++学习笔记(5)——关于三种内存分配方法
- VC++图片透明技术原理
- 【算法总结系列-3】-- 简 单 队 列 --c++
- C++实现K-means,聚类原理解析(并用在图片像素点聚类)
- [C++]using语义使用说明
- c++数组越界相关
- mfc连接ACCESS2010及以上版本,部分“MicrosoftC++异常”处理
- C++抽象类
- C++代理