c语言中的函数的定义以及相关的调用、嵌套、递归以及和数组的关系

来源:互联网 发布:大数据属于哪个专业 编辑:程序博客网 时间:2024/05/17 04:01

1、函数就是一个可以被多次调用的功能模块。
1)一个c程序有多个功能模块组成,一个模块可以有多个源程序文件。
(分别:编写、编译、提高效率)
2)一个源文件可以是一个或多个函数组成以及其他有关的内容组成。
(编译是一个以源文件为单位进行编译的)
3)c程序从main函数开始并且从main函数结束
4)所有函数都是平行的,相互独立(进行)。
(即为:一个函数并不从属另外一个函数,即为:函数不能够嵌套定义)
【函数间可以调用,但是不可以调用main函数,main函数是运行所有我们编写程序的入口,是由系统调用的】
5)用户的角度看,函数有两种
(1)标准函数(库函数),系统提供的;用户不必要定义而是直接使用它们。
(许多基本的函数是共同的,但是不同的编译系统有些库函数的数量和功能会有所不同)
(2)用户自定的函数。(用户从来解决问题写的函数)
6)从函数的形参来看,分为两类
(1)无参函数
(2)有参函数


2、函数的定义的一般形式
1)无参数定义的一般形式
格式:
类型标识符 函数名()
{
声明部分
语句部分
}


2)有参数定义的一般形式1
类型标识符 函数名(形式参数表列)
{
声明部分
语句部分
}


eg:
int max(int x,int y)
{
int z;
z = x>y?x:y;
return (z);
}
说明:定义函数的时候不指定函数类型,系统会隐含指定函数类型为int型。
所以,int max(int x,int y )可以写成: max(int x,int y)


3)空函数
类型说明符 函数名()
{  }
eg:
void dummy(){

}
//即为:调用这个函数没有什么作用。等待以后添加实现的内容


3、函数参数和函数值
3.1、形式参数 和实际参数
1)形式参数 和 实际参数
实参 ----- 调用函数
形参 ----- 被调用函数
2)
(1)定义函数指定的形参,在函数没有调用的时候,他们并不会被分配内存(也就不占用内存),只有在发生函数调用的时候,函数中的形参才会被分配内存单元。调用结束,给函数分配的内存单元将会释放。
#include <stdio.h>
#include <stdlib.h>
void change1(int x,int y)
{
        int temp = x;
        x  = y;
        y = temp;
}
void change2(int* x,int *y){
        int temp;
        temp = *x;
        x  = y;
        *y = temp;
}
void change3(int* x,int *y){
        int temp;
        temp = *x;
        *x  = *y;
        *y = temp;
}
int main()
{
        int x = 3,y= 5;
        change1(x,y);
        printf("%d,%d \n",x,y);


        change3(&x,&y);
        printf("%d,%d \n",x,y);


        change2(&x,&y);
        printf("%d,%d \n",x,y);
}
看看下面的函数,可以看到相应的内容。说明了函数中的形参变量在函数结束了之后就会消失。
(2)实参可以是常量、变量 或表达式
max(3,a+b);
(3)被定义的函数中必须执行函数中的形似参数的类型。
(4) 实参与形参的类型应相同或赋值兼容。
(5)c语言中,实参向形参的数据从传递的是“值传递”,单向传递,只由实参传给形参,而不能够由形式参数传向实参。
原理:
调用函数的时候:
给形参分配存储单元,并将实参对应的值传递给形参,调用结束后形参单元别释放,实参单元仍保留并维持原值。【所以,在执行一个被调用函数时,形参的值得改变不会改变住调用函数的实参的值】。


3.2函数返回值
通过调用一个函数之后,确定最后的值,也就是返回值。
1)函数的返回值是通过return语言来获得。
2)函数值的类型。指定函数返回值的类型,在定义函数的时候指定函数值得类型
(凡是没有指定函数类型的都是按照int类型处理)
3)定义函数是指定的函数类型一般应该和return 语句中的表达式类型一致。
【若是函数类型和return的返回值的类型不一致的时候,我们以函数类型为准,自动转换】即为:函数类型指定返回值的类型。
4)没有返回值的类型的函数,应当使用void来定义。void为无类型,不需要return。

4、函数调用
1)函数调用的一般形式
函数名(实参表列)
2)函数的调用方式
(1)函数语句  没有响应的函数值返回
printStar();
(2)函数表达式 有相应的返回值
c = 2*max(a,b);
(3)函数参数  作为函数的参数进行传递
m max(a,max(b,c));


3)被调用函数的声明和核函数原型
(1)调用者必须要存在函数(自定义函数 、库函数)
(2)若是使用库函数,必须要有#include 进入相应的库函数的头文件
(3)若是被调用函数在调用函数的后面,需要在函数调用之前进行相应的声明。
【函数声明可以只是写相应的参数类型,不用写参数的名字】
因为编译器在编译函数的时候就只是检查相应的函数的类型和函数的的相应的个数,不检查函数参数的名称。
eg:
float add(int x,int y);
or
float add(int ,int )
后者是前者的“函数原型”。这个概念有点重要。


说明:
函数的定义和函数的声明不是一回事
函数定义:包括函数名、函数函数类型、函数的参数名以及函数体的实现,也就是函数的基本的单元的额实现。
函数的声明;就是函数的头部进行声明,也就是将函数的类型、函数的名称、函数参数的类型个数以及相应的顺序告示编译器,在函数进行调用的时候,系统就会按照这个规则来进行检查。


从上面可以知道,函数的声明有两种方式:
函数原型
函数的头部


说明:
(1)旧版本的c语言只声明函数的名和函数的类型,不采用函数原型
新版本已经兼容,不过提倡的干事就是我们一般声明函数的头部。
(2)若是被调用函数在调用函数之前,可以不用声明。
(3)程序的代码较多的时候,我么一般会在头文件中声明,也就是我么在实现部分不用再一次声明。
(4)若是被调用的函数类型为整型,c语言允许在调用函数前不必做函数原型声明。
【这个有点特殊】
也就是系统在编译的时候,无法对函数中的参数的个数以及类型进行检查,在编译的时候无法报错,在运行时会出现错误。

5、函数的嵌套调用
函数具有平行性,也就是不可交叉。
单数函数允许的是:函数允许函数体内进行相应的函数的嵌套的包含,也就是函数内尅包含函数的完整定义。
【有点类似不同的中断的调用,其实函数实现的原理在低层面上是使用中断来实现的】

6、函数的递归调用
——> 函数在调用的时候,会出现函数间接或者直接调用函数自身的过程。
(一般函数递归的调用的内部都是一个if语句来进行判断的,从而实现了递归调用的停止)
递归的过程:
(1)第一阶段是:回推
(2)第二阶段是:递推
典型的递归的问题:
汉诺塔问题
阶乘

7、数组作为函数参数
同样,函数中的参数是用变量来表示的,所以,数组中的元素完全是可以作为函数的参数进行传递;同样数组名也是可以作为函数的实参和形参的,传递的是数组的首元素的地址。
1)数组中的元素作为实参,也就是数组中的一般的变量是一样的,实参和形参的类型一致就好(值传递)。这里没有什么好注意的,我们经常在排序的时候用到了相应的交换的函数,进场会用到函数的调用。
2)数组名作为函数参数
用“数组名”作为函数参数(实参),此时的形参应当用数组名或用指针变量。

相应的代码示例:
#include <stdio.h>
#include <stdlib.h>
float average(float array[10])
{
    int i;
    float aver,sum = array[0];
    for(i=0;i<10;i++)
        sum = sum +array[i];
    aver = sum/10;
    return(aver);
}
int main()
{
    float score[10],aver;
    int  i;
    printf("input 10 scores: \n");
    for(i=0;i <10; i++){
        scanf("%f",&score[i]);
    }
   printf("\n");
    aver = average(score);
    printf("average score is %5.2f \n",aver);
    return 0;
}

(1)用数组名函数的参数,应该在主调用函数和被调用函数都是应该定义为数组
eg:实参score是数组的数组名,形参应该定义为:int array[] 这样的方式类定义参数。
(2)实参数组和形参数组的类型应该一致。
(3)被调用函数中声明形参数组的大小为10,实际上,指定其大小并没有起到作用,因为c语言在编译的时候对数组的大小没有做相应的检查,只是将数组的首地址的首元素的地址作为参数传递给形参数组。
因此:
数组score[0]和array[0]具有同一个地址,他们共同占有一个存储单元,sore[0]、array[0]指的是统一单元。score[n]和array[n]具有相同的值。
(4)形参数组可以不指定大小,在定义数组名后跟一个空的方括号,eg:int array[]
有的时候我们需要知道数组的大小,我们另外添加一个元素来传递数组的大小。
eg:
int getLength(int array[],int leng){……}
(5)数组作为函数实参的时候,不是包数组的元素的值传递给形参数,而是把实参数组首元素地址传递给形参数数组,这样两个数组就共占同一段内存单元了。
(所以不管是调用函数还是别调用函数的 对应的数组的里面的值,另外一个函数的对应的数组的值也会相应的变化。)

3、多为数组名作为函数的参数
(1)多维数组元素可以作为函数的参数,这个就是一般的传值的过程。
(2)多维数组名作为函数的实参和形参,在被调用函数中的形参数组的定义可以指定一维数组的大小,也可以省略。
eg:
int array[3][10]
or:
int array[][10]
两者等价。
下面是不合法的:
int array[][];

说明:由于数组在内存中是按照行来进行排序的,因此,必须指定数组中的列的大小,所以他们就有相同的长度的一维数组。
同时,不能只是指定一维数组而省略二维(列数)
c语言编译系统不检查第一维的大小。
若是两个都制定的话,那么形式参数所指向的数组的大小和实参指定的大小是相等的。


1 0
原创粉丝点击