函数
- 函数是一个完成特定功能的代码模块,其程
序代码独立,通常要求有返回值,也可以是
空值。 - 一般形式如下:
<数据类型><函数名称>( <形数说明> )
{语句序列;return[(<表达式>)];} - 函数的声明就是指函数原型
- 其中,<形式参数声明>可以缺省声明的变量名称,但类型不能缺省
- 例如,
double Power(double x, int n);
double Power(double,int); //缺省写法,但这种写法只能在单独一句话函数声明时使用,不能在函数实现时缺省 - 函数的类型不为 void 的时候有返回值,这时要写 return 语句,反之无
- 函数要先声明再调用,不能放在 main 函数 后面声明,如果不想把大段的函数全部放在main函数的上方,那么用简短的语句在main函数上方进行声明,然后在main函数后面再实现也可以,例如:
#include<stdio.h>double power(double x, int n);//先进行声明,在后面实现也可以int main()
{printf("2的2次方为:%lf\n",power(3,3));return 0;
}double power(double x, int n)//函数具体的实现
{double product = 1;int i;for (i=0; i<n; i++){product *= x;}return product;
}
以上代码格式为 函数先声明,再调用,然后实现 的顺序
函数的参数传递
函数之间的参数传递方式:
- 全局变量
- 复制传递方式
- 地址传递方式
全局变量
- 全局变量就是在函数体外说明的变量,它们在程序中的每个函数里都是可见的
- 全局变量一经定义后就会在程序的任何地方可见。函数调用的位置不同,程序的执行结果可能会受到影响。不好维护,不建议使用。
- 例子:
#include<stdio.h>int x = 3, n = 2;//定义全局变量x,n
double power();int main()
{printf("%d的%d次方为:%lf\n",x,n,power());//这里无需再传参了return 0;
}double power()//这里不要参数也行了
{double product = 1;int i;for (i=0; i<n; i++){ //这里x和n直接使用全局变量product *= x;}return product;
}
复制(赋值)传递方式
- 调用函数将实参传递给被调用函数,被调用函数将创建同类型的形参并用实参初始化
- 形参是新开辟的存储空间,因此,在函数中改变形参的值,不会影响到实参,见代码2
- 复制传参例子(代码1):
#include<stdio.h>double power(double x, int n);int main()
{int x = 2;int n = 2;printf("2的2次方为:%lf\n",power(x,n)); //把x,n作为实参赋值给形参x,nreturn 0;
}double power(double x, int n)//形参x,n
{double product = 1;int i;for (i=0; i<n; i++){product *= x;}return product;
}
输出结果:
2的2次方为:4.000000
代码2:
#include<stdio.h>void exchange(int a, int b);int main()
{int c = 2;int d = 3;exchange(c,d);printf("c = %d, d = %d\n",c,d);//在main函数中打印return 0;
}void exchange(int a, int b)
{int t;t = a;a = b;b = t;
}
输出结果:
c = 2, d = 3
从上面这个结果可以看出实参 c 和 d 并没有发生交换,但是这具体是什么原因呢?
答案是:形参和实参在内存中的存储空间是不一样的,形参是另外开辟的存储空间,当 exchange 函数被调用时,c 的值被复制(赋值,下同)到形参 a 中,同样的,d 的值被复制到形参 b 中,也就是形参 a 和 b 中存的都是实参 c 和 d 的副本,而在 exchange 函数内部只会交换形参 a 和 b 的值,不会影响到实参 c 和 d。
如果想要实质性的交换需用指针,(PS:交换指针的值-地址没有用,除非直接在被调用函数中打印交换地址过后的 *a 和 *b )如果只是想看一下打印结果,可以把输出函数写到被调用函数(交换函数)中去,这里举一个用指针进行实质交换的例子:
#include<stdio.h>void exchange(int * a, int * b);//形参都为指针int main()
{int c = 2;int d = 3;exchange(&c,&d); //实参为 c 和 d 的地址printf("c = %d, d = %d\n",c, d); //在这里打印return 0;
}void exchange(int * a, int * b)
{int t;t = *a;*a = *b;*b = t; // * 取传入地址对应的值,然后通过 t 进行交换}
输出结果:
c = 3, d = 2
地址传递方式
- 按地址传递,实参为变量的地址,而形参为同类型的指针
- 被调用函数中对形参的操作,将直接改变实参的值(被调用函数对指针的目标操作,相当于对实参本身的操作)
- 例子就是上个代码
复制传递和地址传递的适用场景
在C语言中,参数传递可以通过值传递(复制传递)或引用传递(地址传递)来实现。选择何种方式取决于对函数的需求和要求。
-
复制传递(值传递):
使用复制传递时,函数接收到的是实际参数的副本,而不是实际参数本身。这意味着在函数内部对形式参数进行的修改不会影响到实际参数的值。复制传递适用于以下情况:
- 当函数不需要修改实际参数的值时。
- 当实际参数是基本数据类型(如整数、浮点数等)或小型结构体时,复制传递的开销相对较小。
示例代码中的
exchange
函数使用了复制传递,因为我们只是想在函数内部交换参数的值,并不需要修改实际参数的值。 -
地址传递(引用传递):
使用地址传递时,函数接收到的是实际参数的地址,可以通过指针操作实际参数的值。在函数内部对形式参数的修改会影响到实际参数的值。地址传递适用于以下情况:
- 当需要修改实际参数的值时。
- 当实际参数是大型结构体或数组时,避免复制大量的数据。
示例代码中的修改后的
exchange
函数使用了地址传递,我们通过传递指针来操作实际参数的地址,从而实现变量值的交换。
总而言之,在选择参数传递方式时,需要根据具体的需求和情况来决定。如果需要修改实际参数的值,或者实际参数是大型结构体或数组时,地址传递通常是更好的选择。而对于不需要修改实际参数的值,或者实际参数是基本数据类型或小型结构体时,复制传递是更简单和高效的方式。
函数的传参 - 数组
- 全局数组传递方式
- 复制传递方式(实参为数组的指针,形参为数组名(本质是一个指针变量))
- 地址传递方式(实参为数组的指针,形参为同类型的指针变量)
- 对于字符数组来说,传参的时候只穿数组名也行,因为程序可以根据 ‘\0’ 字符串结束符来判断结束,而 int 类型等类型的数组还必须要传进去数组元素个数,否则程序不知道数组的末尾在哪。