1、普通变量作为函数形参
这种情况使用最多,也比较好理解,贴段代码如下:
#include <stdio.h>
void func(int x)
{
printf("in fun : x = %d , &x = %p . \n",x,&x);
}
int main(void)
{
int a = 10;
printf("in main : a = %d, &a = %p. \n", a , &a);
func(a);
return 0;
}
运行结果:
root@ubuntu:/mnt/hgfs/share/code/c_advance/pointer# ./a.out
in main : a = 10, &a = 0xbf84750c.
in fun : x = 10 , &x = 0xbf8474f0 .
结果分析:
(1) a和x是两个独立的变量,在内存中分别占用两个地址,这点很重要。
(2) 当func函数被调用的时候,传参会将a传递给x,所以x=a;相当于完成了一次赋值操作,这其中赋值操作是会浪费CPU的运行时间和内存的。
(3) 这就是大家所说的“传值调用”。
2、数组作为函数的形参
这种情况在上一章节中讨论过,为了内容的连贯性,我们再举个例子来讨论一下:
void func(int x[5], int len)
{
printf("int fun : sizeof(x) = %d; len = %d \n", sizeof(x), len);
printf("in fun : x[0] = %d , x = %p . \n",x[0], x);
printf("in fun : x[0] = %d , &x[0] = %p . \n",x[0], &x[0]);
}
int main(void)
{
int a[10] = {0};
printf("in main : a[0] = %d, a = %p. \n", a[0] , a);
printf("in main : a[0] = %d, &a[0] = %p. \n", a[0] , &a[0]);
func(a,sizeof(a));
return 0;
}
运行结果:
root@ubuntu:/mnt/hgfs/share/code/c_advance/pointer# ./a.out
in main : a[0] = 0, a = 0xbff837a8.
in main : a[0] = 0, &a[0] = 0xbff837a8.
int fun : sizeof(x) = 4; len = 40
in fun : x[0] = 0 , x = 0xbff837a8 .
in fun : x[0] = 0 , &x[0] = 0xbff837a8 .
结果分析:
(1) 函数的形参为数组名时,实际传递的不是整个数组,而是数组的首元素的首地址(也是整个数组的首地址)。 这种方式我们也称之为 “传址调用”。我们只是将数组的首地址传递给了函数,也就是说在函数内部操作的数组的内存空间是同一个。那么在函数中就可以操作数组中的值,所以在很多大型程序设计时,有一些数组的内容会被莫名奇妙的改掉,这个时候就需要注意,是否一些函数的形参是数组名,同时在调用此函数时将这个数组作为实参传递给了该函数。
(2)如果需要将整个数组都传递过去,那么在传参时加一个变量int,传入sizeof(数组),这样就将数组长度和数组的首地址都传进去了。
申明时:
void func(int x[5], int len);
调用时:
func(a,sizeof(a)/sizeof(int ));
(3) 数组作为形参调用时,数组的大小可以是空:x[] , 也可以是任意数字。因为我们只是将一个地址给他传递进去,数组大小对此毫无意义。
3、指针作为函数形参
和数组作为函数形参是一模一样的。本质上我们只是传递个地址给它。
4、结构体作为函数形参
结构体作为函数形参,和普通变量作为函数形参是一样的。写一段代码验证一下:
#include <stdio.h>
#include <string.h>
typedef struct Temp
{
char name[20];
int len;
}temp_type;
void func(temp_type x)
{
printf("in func :sizeof(x) = %d; \n", sizeof(x));
printf("in func :&x = %p; \n", &x);
printf("in func :&(x.name)= %p; \n ", &(x.name));
strcpy(x.name,"abcdefg");
printf("in func :a,name = %s,\n", x.name);
}
void func1(temp_type *y)
{
printf("in func1 :sizeof(y) = %d; \n", sizeof(y));
printf("in func1 :y = %p; \n", y);
printf("in func1 :&y = %p; \n", &y);
strcpy(y->name , "nihao zhongguo");
printf("in func1 :%s,\n", y->name);
}
int main (void )
{
temp_type a;
a.len = 10;
strcpy(a.name,"hello world");
printf("in main : sizeof(a) = %d; \n", sizeof(a));
printf("in main : &a = %p; \n", &a);
printf("in main : &(a.name)= %p; \n ", &(a.name));
printf("in main : a.name = %s,\n", a.name);
func(a);
printf("in main :a.name = %s,\n", a.name);
func1(&a);
printf("in main :a.name = %s,\n", a.name);
return 0;
}
运行结果如下:
root@ubuntu:/mnt/hgfs/share/code/c_advance/pointer# ./a.out
in main : sizeof(a) = 24;
in main : &a = 0xbfbe6154;
in main : &(a.name)= 0xbfbe6154;
in main : a.name = hello world,
in func :sizeof(x) = 24;
in func :&x = 0xbfbe6130;
in func :&(x.name)= 0xbfbe6130;
in func :a,name = abcdefg,
in main :a.name = hello world,
in func1 :sizeof(y) = 4;
in func1 :y = 0xbfbe6154;
in func1 :&y = 0xbfbe6130;
in func1 :nihao zhongguo,
in main :a.name = nihao zhongguo,
结论:
(1) 结构体作为形参传递时,和普通变量的传递是一模一样的。
(2) 之前的数组不可以整个传递,我们可以将数组封装在结构体中,然后用结构体传递,这样整个数组就传过去了。
(3) 通常情况下结构体作为形参直接传递的做法是不可取的,因为一般情况下,结构体都比较大,占用的内存比较多。在函数调用时,直接传递结构体会造成严重的内存浪费,所以一般做法是传递结构体指针进去。如本例子中的fun1。
(4) 传递结构体指针作为形参,会改变结构体的内容,我们将这种形参传递叫输出型参数,下一节详细介绍。
总结
函数形参的传递其实也不复杂,我们只需要弄清楚传递给函数的是值还是址。 但是本质上来讲,我们都是操作内存,是内存的复制还是用指针的方式直接访问内存呢?就需要根据具体的情况具体对待。这其中还会牵扯出输入型参数和输出型参数,具体在以后篇幅中介绍。