<1>函数原型
(1)函数先后关系
我们将自己定义的函数放在主函数main之前—————是因为:C的编译器自上而下顺序分析代码,这样在主函数中调用自定义函数才合理
1.我们以一段代码为例
#include<stdio.h>
void sum(int begin, int end){
int i;
int sum =0;
for ( i=begin; i<=end; i++ ) {
sum += i;
}
printf("%d到%d的和是%d\n",begin,end,sum);
}
———————上方为自定义函数,下方为主函数————————————
int main(){
sum(1,10);——————我们以第一次调用为例:
* 在编译器运行到这一步时,它需要知道sum函数形式,所以我们必须在主函数之前对sum函数的形式进行定义(也就是需要了解sum有几个参数,每个参数的类型,以及返回的类型)
sum(20,30);
sum(35,45);
return 0;
}
2.以此类推,我们分析一下自定义函数在主函数下方的情况
1)以一段代码为例:
#include<stdio.h>
int main(){
sum(1,10);——————//编译器会在此处猜测我们的函数类型为int sum(int ,int)
sum(20,30);
sum(35,45);
return 0;
}
void sum(int begin, int end){
int i;
int sum =0;
for ( i=begin; i<=end; i++ ) {
sum += i;
}
printf("%d到%d的和是%d\n",begin,end,sum);
}
———我们得出的编译结果显示,sum函数的隐含申明无效(编译器猜测的函数类型与实际定义的函数类型不符),编译失败;
2)原型声明
解决方法:————针对第二种编译失败的代码进行原型声明
我们以一段代码为例:
#include<stdio.h>
void sum(int begin, int end);——————//声明
int main(){
sum(1,10);————//int sum(int,int)我们在之前对函数进行了声明,这里编译器便不会继续猜测函数的类型,而是根据声明来判断对sum的调用是否正确
sum(20,30);
sum(35,45);
return 0;
}
void sum(int begin, int end){——————//定义
//值得注意的是,在此处函数会再次判断定义与声明是否一致//
int i;
int sum =0;
for ( i=begin; i<=end; i++ ) {
sum += i;
}
printf("%d到%d的和是%d\n",begin,end,sum);
}
(2)函数原型
- 构成:函数头,以分号结尾;
- 目的:告诉编译器定义函数的形式;
- 细节:函数原型里面可以省略参数名
—————即可以将void sum(int begin, int end)表示为void sum(int, int)
原因:原型声明仅仅是告诉编译器定义函数的参数数量和类型,参数名并不影响,保留参数名是为了增强代码的可读性 - 涉及到三个方面:
- 名称
- 参数(数量及类型)
- 返回类型
——————区分技巧:有分号的为函数原型,没有分号的为实际函数头
<2>参数传递
(1)调用函数
- 条件:如果函数有参数,那么调用函数时必须传递给它数量和类型正确的值;
- 可以传递给函数的值是表达式的结果;
- 表达式大概分为以下几个类型:
- 单个字面量;c=max(4,5)
- 单个变量;————c=max(a,b)
- 计算的结果;————c=max(23+45,b)
- 函数的返回值;————c=max(max(23,45),a)
- 赋值;
(2)类型不匹配
- 调用函数时给的值与参数的类型不匹配是C语言传统上最大的漏洞;
- 编译器总是悄悄替你把类型转换好,但是这很可能不是你所期望的;
- 后续的语言,C++/Java在这方面很严格;
1).类型不匹配出现的情况:
我们以一段代码为例:
#include<stdio.h>
void cheer (int i){
printf("cheer %d\n",i);
}
int main(){
cheer(2.4);—————注意:函数原型声明i为整型,而此处cheer中i的值为double型
return 0;
}
类型不匹配的结果:编译器仅可能给出警告,但是仍然能给出结果(编译器会自动进行类型的转换)
(3)调用函数时传递过去的是什么
- C语言在调用函数时,只能传递值给函数
我们以一段代码为例:
#include<stdio.h>
void swap(int a,int b);
int main(){
int a=5;
int b = 6;
swap(a,b);
printf("a=%d b=%d\n",a,b);
return 0;
}
void swap(int x,int y){
int t=x;
x=y;
y=t;
}
——1)出现问题:在这串代码的调试过程中我们发现我们得出x,y的值时便不能再输出a,b的值;得出a,b的值,便不能继续输出x,y的值
——2)情况分析:
我们对代码进行一定程度的修改:
#include<stdio.h>
void swap(int a,int b);
int main(){
int a=5;
int b = 6;
swap(a,b);
printf("a=%d b=%d\n",a,b);
return 0;
}
void swap(int a,int b){
int t=a;
a=b;
b=t;
}在进行交换时,我们将swap中a的值5交给swap中int里面的a,将swap中b的值6交给交给swap中int里面的b;swap里面的a,b和main里面的a,b完全没有关系,尽管他们同名,但他们处在不同的位置上,是没有关系的变量
唯一的联系是在调用函数时传递了值
——————所以这串代码不能交换a,b的值。
1.引申定义:传值(对——1)中出现的问题进行解释)
- 每个函数都有自己的变量空间,参数也位于这个独立空间中,和其它函数没有关系,这也就解释了a,b的值与x,y的值不能同时被输出的问题
2.引申定义:形式参数与实际参数
- 函数参数表中的参数为形式参数,例如:void swap(int a,int b)中的int a与int b;调用函数时给的值为实际参数,例如:swap(a,b)
注意:为了防止对概念进行混淆,我们可以将形参实参的概念理解为参数和值的关系:
- 即原来的实参为值,而形参则代表参数,整体可以理解为进行传值的操作