普通变量作为函数形参
#include <stdio.h> //&a和&b不同,说明a和b不是同一个变量(在内存中a和b是独立的2个内存空间) //但是a和b是有关联的,实际上b是a赋值得到的。 void func1 (int b) { //在函数内部,形参h的值等于实参a printf("b = %d\n",b); printf("in furc1, &b = %p\n",&b); } int main(void) { int a = 4; printf("&a = %p\n",&a); func1(a); return 0; }
VC++6.0编译如下
(1)函数传参时,普通变量作为参数时,形参和实参名字可以相回也可以不同,实际上都是用实参来替代相对应的形参的。
(2)在子函数内部,形参的值等于实参,原因是函数调用时把实参的值复制给了形参
(3)这就是很多书上写的“传值调用”(相当于实参做右值,形参做左值)
数组作为函数形参
#include <stdio.h> void func2(int a[]) { printf("sizeof(a) = %d\n",sizeof(a)); printf("in func2,a = %p\n",a); } int main(void) { int a[3] = {0}; printf("sizeof(a) = %d\n",sizeof(a)); printf("a = %p\n",a); func2(a); return 0; }
VC++6.0编译如下:
第一行的sizeof(a)是sizeof(数组名),得到的就是整个数组所占的内存空间,第三行的就是sizeof(数组a的起始地址),可见两个起始地址是一样的
(1)数组名作为形参传参时,实际传递是不是整个数组,而是数组的首元素的首地址(也就是整个数组的首地址。因为传参时是传值,所以这两个没区别)。所以在子函数内部,传进来的数组名就等于是一个指向数组首元素首地址的指针。所以sizoof得到的是4.
(2)在于函数内传参得到的数组首元素首地址,和外面得到的数组首元素首地址的值是相同的。很多人把这种特性叫做“传址调用”(所谓的传址调用就是调用子函数时传了地址(也就是指针),此时可以通过传进去的地址来访问实参。)
(3)数组作为函数形参时,[ ]里的数字是可有可无的。为什么?因为数组名做形参传递的实际只是个指针,根本没有数组长度这个信息。
指针作为函数形参
#include <stdio.h> void func3(int *a) { printf("in func3,sizeof(a) = %d\n",sizeof(a)); printf("in func3,a = %p\n",a); } void func2(int a[]) { printf("in func2,sizeof(a) = %d\n",sizeof(a)); printf("in func2,a = %p\n",a); } int main(void) { int a[3] = {0}; //printf("sizeof(a) = %d\n",sizeof(a)); printf("a = %p\n",a); printf("\n"); func2(a); func3(a); return 0; }
VC++6.0编译如下:
(1)只有一句话:和数组作为函数形参是一样的.这就好像指针方式访问数组元素和数组方式访问数组元素的结果是一样的。
结构体变量作为函数形参
#include <stdio.h> struct A { char a; int b; }; void func5(struct A a) { printf("in func5,sizeof(a) = %d\n",sizeof(a)); printf("in func5,a = &%x\n",&a); printf("in func5,a.b = %d\n",a.b); } int main(void) { struct A a = {4,5}; printf("sizeof(a) = %d\n",sizeof(a)); printf("&a = %p\n",&a); printf("\n"); func5(a); return 0; }
vc++6.0编译结果如下,可见两个地址不一样
(1)结构体变量作为函数形参的时候,实际上和普通变量(类似于int之类的)传参时表现是一模一一样的。所以说结构体变量其实也是普通变量而已。
(2)因为结构体一般都很大, 所以如果直接用结构体变量进行传参,那么雨数调用效率就会很低(因为在雨数传参的时候需要将实参赋值给形参,所以当传参的变量越大调用效率就会越低)怎么解决?思路只有一个那就是不要传变量了,改传变量的指针(地址)进去。
(3)结构体因为自身太大,所以传参应该用指针来传(但是程序员可以自己决定,你非要传结构体变量过去语言也是允许的,只是效率低了) :回想一下 数组,为什么C语言设计的时候数组传参默认是传的数组首元素首地址而不是整个数组?
最后再来说下传值调用和传址调用,最经典的就是swap函数
#include <stdio.h> void swap1(int a,int b) { int temp; temp = a; a = b; b = temp; } void swap2(int *a, int *b) { int temp; temp = *a; *a = *b; *b = temp; } void main() { int a = 5,b = 6; swap1(a,b); printf("a = %d,b = %d\n",a,b); swap2(&a,&b); printf("a = %d,b = %d\n",a,b); }
VC++6.0编译如下
(1)传值调用描述的是这样一种现象: a1和b1作为实参,自己并没有真身进入swap1函数内部,而只是拷贝了一份自己的副本(副本具有和自己一样的值,但是是不同的变量)进入子函数swap1,然后我们在子函数swap1中交换的实际是副本而不是a1、b1真身。所以在swap1内部确实是交换了,但是到外部的a1和b1根本没有受影响。
(2)在swap2中a2和b2真的被改变了(但是a2和b2真身还是没有进入swap2函数内,而是swap2函数内部跑出来把外面的a2和b2真身改了)。实际上实参a2和b2永远无法真身进入子函数内部(进去的只能是一份拷贝),但是在swap2我们把a2和b2的地址传进去给子函数了,于是乎在子函数内可以通过指针解引用方式从函数内部访问到外部的a2和b2真身,从而改变a2和b2。
(3)结论:这个世界上根本没有传值和传址这两种方式,c语言本身函数调用时一直是传值的,只不过传的值可以是变量名,也可以是变量的指针。