函数(3)函数原型和参数传递

发布时间 2023-11-18 23:09:27作者: 瑜阳

<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)函数原型

  1. 构成:函数头,以分号结尾;
  2. 目的:告诉编译器定义函数的形式;
  3. 细节:函数原型里面可以省略参数名
    —————即可以将void sum(int begin, int end)表示为void sum(int, int)
    原因:原型声明仅仅是告诉编译器定义函数的参数数量和类型,参数名并不影响,保留参数名是为了增强代码的可读性
  4. 涉及到三个方面
  • 名称
  • 参数(数量及类型)
  • 返回类型

——————区分技巧:有分号的为函数原型,没有分号的为实际函数头

<2>参数传递

(1)调用函数

  • 条件:如果函数有参数,那么调用函数时必须传递给它数量和类型正确的值;
  • 可以传递给函数的值是表达式的结果;
  • 表达式大概分为以下几个类型:
  1. 单个字面量;c=max(4,5)
  2. 单个变量;————c=max(a,b)
  3. 计算的结果;————c=max(23+45,b)
  4. 函数的返回值;————c=max(max(23,45),a)
  5. 赋值;

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

注意:为了防止对概念进行混淆,我们可以将形参实参的概念理解为参数和值的关系:

  • 即原来的实参为值,而形参则代表参数,整体可以理解为进行传值的操作