C语言以数组和结构体传参

本文探讨C语言在32位和64位机器上如何通过栈和寄存器进行函数参数传递,特别是当涉及到数组和结构体时的处理方式。虽然C语言对函数参数数量没有明确限制,但过多参数会影响执行效率。通过实例分析了数组和结构体作为参数时的内存占用和传递机制。

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);

先看数组的例子:
/*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);
}
### 回答1: 可以通过指针传递结构体数组作为参数,例如: void func(struct myStruct *arr, int size) { // do something with the array } 其中,myStruct 是结构体类型,arr 是结构体数组的指针,size 是数组的大小。在函数内部,可以通过指针访问结构体数组的元素,进行相应的操作。 ### 回答2: 在C语言中,可以使用结构体数组作为参数进行函数的传递。 结构体是一种用户自定义的复合数据类型,它可以包含多个不同类型的成员变量。结构体数组则是由多个结构体实例组成的数组。 当我们定义一个函数时,可以在参数列表中声明结构体数组作为参数。例如,可以使用如下的方法声明一个接受结构体数组作为参数的函数: void func(struct Person arr[], int length); 在调用这个函数时,可以将一个结构体数组作为实参传递给这个函数。例如,可以使用如下的方法调用上述函数: struct Person arr[3] = {{"Tom", 20}, {"Jerry", 25}, {"Alice", 18}}; func(arr, 3); 在函数内部,可以通过结构体数组的下标访问修改其中的每个结构体成员。例如,可以通过如下的方法访问结构体数组中的第一个结构体的成员变量名: arr[0].name; 在函数内部,可以使用循环来遍历整个结构体数组,对其中的每个结构体进行处理。例如,可以使用如下的方法遍历整个结构体数组: for(int i = 0; i < length; i++){ printf("%s\n", arr[i].name); } 通过以上说明可知,结构体数组可以作为参数传递给函数,使得我们可以在函数内部对其中的结构体进行操作处理,提高程序的灵活性可维护性。 ### 回答3: C语言中,可以通过结构体数组来传递参数。结构体数组是一组相同结构类型的元素的集合,通过数组名可以访问数组中的不同元素。 传递结构体数组作为参数时,可以使用以下两种方式: 1. 将结构体数组的地址作为参数传递: void func(struct Person *array, int size); 在函数内部,可以通过指针来访问结构体数组的元素。可以通过“数组名[index]”的方式来访问具体的元素,其中index表示元素在数组中的位置。 2. 使用结构体数组类型作为参数类型: void func(struct Person array[], int size); 在函数内部,同样可以通过“数组名[index]”的方式来访问具体的元素。 传递结构体数组的好处是可以改变原始数组的值,因为传递的是数组的地址。这种传参方式可以减少内存的开销,因为传递的是地址,而不是数组本身的副本。 在函数内部修改结构体数组的元素时,可以通过指针或者数组的方式进行操作。通过指针进行操作可以节省内存空间,因为只需要传递地址。通过数组的方式进行操作更加直观,但会占用更多的内存空间。 需要注意的是,在函数内部操作结构体数组时,要确保不访问越界的元素,否则会导致内存错误。此外,传递结构体数组时,可以通过传递数组的大小作为参数,以避免数组越界的情况。 总之,通过结构体数组传递参数可以方便地操作改变数组的值,同时也减少了内存的开销。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值