函数详解(C语言)

本文详细介绍了C语言中的函数概念,包括库函数的使用、自定义函数的语法、形参实参、return语句、数组作为参数、嵌套调用和链式访问,以及函数的定义与声明,特别是强调了自定义函数和多文件编程中的声明技巧。
摘要由CSDN通过智能技术生成

        今天我们要学习的是函数

        我们从初中开始就接触过函数,在数学中函数的形式是 y = F(x)这样的,我们给定一个x值,就会返回y的值,这是数学中的函数。

        在C语言中函数与数学有些相似,但区别又很大,我更倾向于把C语言中的函数称为:子程序其实从名字中就可以看出来,子程序(函数)是为了完成一个大程序中的其中一个小任务的程序。一个大的程序就是由许多小函数构成的。同时,如果我们要重复做某一件事情,我们就可以反复调用我们写好的程序,在下面的文章中我们就会体现。

        在C语言中,我们一般会见到两种函数:

  • 库函数
  • 自定义函数

        接下来我们就围绕着这两种函数展开。

目录

 一、库函数

1.标准库和头文件

2.库函数的使用方法

二、自定义函数

1.自定义函数的语法形式

2.自定义函数举例

 3.形参和实参

4.形参与实参的关系

4.return语句 

三、函数的进阶用法

1.数组做函数参数

2.嵌套调用和链式访问

        (1)嵌套调用 

(2)链式访问 

四、函数的定义与声明

        1.单文件声明

        2.多文件声明


 一、库函数

        库函数在我们之前的学习中就使用过,例如printf、scanf等都是库函数,那么究竟什么是库函数呢。

1.标准库和头文件

       C语⾔标准中规定了C语⾔的各种语法规则,C语⾔并不提供库函数。C语⾔的国际标准ANSI C规定了⼀ 些常⽤的函数的标准,被称为标准库,那不同的编译器⼚商根据ANSI提供的C语⾔标准就给出了⼀系列 函数的实现。这些函数就被称为库函数。

        库函数也是函数,只不过这些函数不需要我们自己去实现,编译器厂商以及帮我们实现好了,我们直接用即可,有了库函数可以提升我们编写程序的效率。

        库函数有很多种,根据功能的不同被分类放进了不同的头文件中,我们可以通过下面的网址进行查看。

        库函数相关头⽂件:https://zh.cppreference.com/w/c/header

2.库函数的使用方法

        库函数学习的方式有很多,例如:

        C/C++官⽅的链接:https://zh.cppreference.com/w/c/header

        cplusplus.com:https://legacy.cplusplus.com/reference/clibrary/

        在学习一个新的函数时我们应该看函数的功能、函数的头文件包含、函数的参数类型、函数的返回值等等。在此我们以sqrt()函数为例。

double sqrt (double x);
//sqrt 是函数名
//x 是函数的参数
//第一个double 是函数返回值的类型
//第二个double 是传给函数的值的类型 

        功能是计算x的平方根并返回其值。 

        头文件是<math.h>,如果不包含此头文件,该函数的使用就会出现问题。

        参数类型是double,我们在传参的时候要传符合函数参数类型的变量。  

        返回值是double,我们在接收返回值的时候,最好使用同类型的变量来接收。

在清楚上面几点后,我们就可以在代码中实践一下了。

#include <stdio.h>
#include <math.h>
int main()
{
    double a=4.0;
    double b=sqrt(a);
    printf("%lf",b);
    return 0;
}

显而易见其运行结果就是2.0。

二、自定义函数

        在了解了库函数之后,我们应该把我们的关注度更多的放在自定义函数,自定义函数的自由程度跟高,在我们写代码的时候有更多的创造性,可以实现更多的功能。

1.自定义函数的语法形式

        自定义的函数的语法形式与库函数是一样的,其形式如下:

ret_type fun_name (形式参数)
{

}

        ret_type是函数的返回值类型,其类型可以是任何形式,可以是int(返回整型),可以是void(不返回任何值)。

        fun_name是函数名,为了方便调用函数,在起名的时候,最好与功能有一定的相关性。

      ()中是形式参数,函数的参数也可以是各种类型,在穿参的时候,要表明函数的类型与个数。

        {}中是函数体,用来实现函数功能的。

2.自定义函数举例

例:写一个加法函数,完成两个整型变量的加法操作。

#include <stdio.h>

int Add(int x,int y)
{
    int z;
    z=x+y;
    return z;
}
int main()
{
    int a,b;
    scanf("%d %d",&a,&b);
    int c=Add(a,b);
    printf("a+b=%d",c);
    return 0;
}

其中Add函数也可以简化为:

int Add (int x,int y)
{
    return x+y;
}

        从举得例子中可以看出,我们在在自定义函数时,要交代清楚函数的名字,返回类型,参数的类型以及个数。 

        以上只是一个简单的例子,未来在编写代码的过程中,我们在自定义函数时函数名,返回类型,参数等都会更加的灵活。

 3.形参和实参

        在使用函数的过程中,我们把函数的参数,分为形参和实参。顾名思义,形参就是形式参数,实参就是实际参数。

        我们从之前的代码中进行分析:

#include <stdio.h>

int Add(int x,int y)
{
    int z;
    z=x+y;
    return z;
}
int main()
{
    int a,b;
    scanf("%d %d",&a,&b);
    int c=Add(a,b);
    printf("a+b=%d",c);
    return 0;
}

        在这段代码中,main函数中的Add(a,b)中的a,b就是实参,是实际真实传递给函数的参数。

        在Add(x,y)中x,y就是形参,所谓形参就是形式上存在,当我们定义了Add函数之后,我们不去调用Add函数时x,y只是形式上存在,并不会向内存中申请空间。没有真实存在,所以叫形式参数。 只有Add函数被调用时,为了存放数据,才会向内存中申请空间,这个过程被称为形式的实例化。

4.形参与实参的关系

        为了探讨形参与实参之间的关系,我们还是根据刚才的代码进行分析。它们之间是有联系的,但是不是同一块空间,我们可以借助调试工具来观察现象。

#include <stdio.h>

int Add(int x,int y)
{
    int z;
    z=x+y;
    return z;
}
int main()
{
    int a,b;
    scanf("%d %d",&a,&b);
    int c=Add(a,b);
    printf("a+b=%d",c);
    return 0;
}

        在调试的过程中我们发现,a与x,b与y的值确实一样,但是它们的地址是不一样的,通过这个现象我们就可以了解到,形式参数只是实参的一份拷贝

4.return语句 

在函数编写的过程中,经常会出现return语句。接下来来讲解一下return语句的注意事项。

(1)return语句后面可以跟一个数值,也可以跟一个表达式,如果是表达式,则先执行再返回数值。

(2)当函数返回类型是void时,return语句后也可以什么也不跟,直接写 return; 

(3)当return语句的返回类型与函数的返回类型不一样时,系统会将return的返回类型转换成函数的返回类型。

(4)当return语句执行时,函数就彻底结束,不再执行后面的语句。

(5)如果函数中存在分支语句时,确保每一个分支后面都跟有一个return语句,否则会出现编译错误。

三、函数的进阶用法

        在理解了函数的基础之后我们就可以利用函数实现一些功能了,接下来我们将用函数实现一些更加难的功能。 

1.数组做函数参数

        在用函数解决实际问题时,难免会把数组作为函数的参数进行传递,思考一下数组的传递与其他类型的参数传递时一样吗。比如说我们要写一个函数把一个数组中的所有元素全部设置成1,然后在写一个函数打印该数组的元素,我们应该如何实现?

        这里我们需要两个函数,一个重置内容,一个打印内容。在对数组进行操作时,我们需要遍历数组的所有元素,所以我们需要知道数组的元素个数,所以我们在传递参数的需要把数组传递给函数,也需要把数组的元素个数也传递给函数,这样一来我们就可以实现对数组进行操作。

        在设计函数之前,我们需要了解数组传参的几个知识点:

(1)函数的形式参数要和函数的实际参数的个数匹配。

(2)函数的实参是数组,形参也可以写成数组的形式。(实际上传递的是地址,后续讲指针时会讲到)

(3)形参如果是一维数组,数组的大小2不可以省略。

(4)形参如果是二维数组,数组的行可以省略,但列不行。

(5)数组传参形参是不会创建新的数组的,是在实参是直接进行操作的,形参和实参是同一个数组。(这个也与指针有关)

        在有了以上的知识之后,我们就可以设计函数了,其实现效果如下:

#include <stdio.h>

//设置数组元素
void set_arr (int arr [10],int sz)
{
    for(int i=0;i<sz;i++)
    {
        arr[i]=1;
    }
    
}

//打印数组元素
void print_arr (int arr [10],int sz)
{
    for(int i=0;i<sz;i++)
    {
        printf("%d",arr[i]);
    }
    
}
int main()
{
    int arr[10]={1,2,3,4,5,6,7,8,9,10};
    int sz=sizeof(arr)/sizeof(arr[0]);//求元素个数
    set_arr(arr,sz);
    print_arr(arr,sz);
    return 0;
}

2.嵌套调用和链式访问

        在设计程序的过程中函数并不是独立的,函数与函数之间存在着许多联系,一个函数中包含着另一个函数就是嵌套调用,一个函数的返回值作为另一个函数的参数就是链式访问,正是因为函数的之间的关联,之间的相互调用,才能写出相对较大的程序。 

        (1)嵌套调用 

        嵌套调用就是函数之间的互相调用,我们通过一个例子来理解。

例:判断某年的某个月有几天。

        当我们拿到一个要求时,我们要先对其进行分析。年份分为闰年与平年,闰年与平年的区别就在于二月的天数不同,所以我们在输入二月时要进行一次判断该年是否为闰年。剩下的就简单了,只需要判断月份即可。有了以上的判断,我们需要设计两个函数,一个函数是判断月份的,一个是来判断年份的。接下来我们就来实现这个程序。

#include <stdio.h>

int year (int y)
{
    if((y%4==0 && y%100 !=0)||(y%400==0))
    { 
        return 1;
    }    
    else
    {
        return 0;
    }

}

int day (int y,int m)
{
    int month[13]={0,31,28,31,30,31,30,31,31,30,31,30,31};//为了输入的月份与元素下标一致
    int d=month[m];
    if(year(y) && m==2)
    {
        d+=1;
    }
    return d;
}

int main()
{
    int y,m;
    scanf("%d %d",&y,&m);
    int d=day(y,m);
    printf("%d",d);    
    return 0;
}

        在这个程序中函数的互相调用体现了很多,例如main函数调用了scanf函数、day函数、printf函;day函数中调用了year函数。在未来写一些更大的函数时,嵌套调用会更加频繁。

(2)链式访问 

        所谓链式访问,就是把一个函数的返回值,作为另一个函数的参数,我们举个例子。

例: 

//这是不使用链式访问的方法
#include <stdio.h>
int main()
{
    int x=strlen('abcdef');//求一个字符串的长度
    printf("%d",x);
    return 0;
}
//这是使用链式访问的方法
#include <stdio.h>
int main()
{
    printf("%d",strlen('abcdef'));//直接把strlen函数的返回值当作printf函数的参数
    return 0;
}

        仔细思考一下,上面两个代码的区别,这就是链式访问。 

四、函数的定义与声明

        函数的声明方式在C语言中主要有两种,一种是我们只写在单个文件中,另一种是我们可以在多个文件写。

        1.单文件声明

        在我们之前写一些简单程序的时候,我们只在一个文件中进行各种操作,在同一个函数中声明函数我们已经很熟悉了。

#include <stdio.h>
//
int Add(int x, int y)
{
    int z;
    z = x + y;
    return z;
}
//
int main()
{
    int a, b;
    scanf("%d %d", &a, &b);
    int c = Add(a, b);
    printf("a+b=%d", c);
    return 0;
}

        就像这段函数一样, 被//框起来的是函数的定义,而在main函数中,是函数的调用。在这种情况下,函数定义在被调用之前,没啥问题。但是如果我们把函数定义放在函数调用之后会发生声明情况,如下:

#include <stdio.h>

int main()
{
    int a, b;
    scanf("%d %d", &a, &b);
    int c = Add(a, b);
    printf("a+b=%d", c);
    return 0;
}
int Add(int x, int y)
{
    int z;
    z = x + y;
    return z;
}

这段代码在编译器中就会出现下面的警告信息:

        这是因为因为,在C语言中,编译时是从第一行开始向下逐行编译的,当在main函数中,遇到Add时,发现Add函数并没有定义,就出现了上面的警告。

        那我们应该如何解决这个问题呢,我们只需要在调用函数之前,先对函数进行声明即可,函数声明要交代,函数的名字、返回值、参数类型及个数。

#include <stdio.h>

int Add(int x, int y);//函数声明

int main()
{
    int a, b;
    scanf("%d %d", &a, &b);
    int c = Add(a, b);
    printf("a+b=%d", c);
    return 0;
}
int Add(int x, int y)
{
    int z;
    z = x + y;
    return z;
}

        这时候程序就能正常编译了,int Add(int x, int y); 这就是函数声明,我们在自己写函数的时候,一定要满足,先定义再调用

        函数的定义也是一种特殊的声明,所以函数的定义写在调用之前也可以正常编译。

        2.多文件声明

        到此为止,我们已经弄明白了什么是函数声明了,我们在写一些复杂程序的时候,不会将所有代码都放到同一个文件中,往往我们会根据程序的功能,将代码放在不同的文件中。

        一般情况下,函数的声明、类型的声明放在头文件(.h)中,函数的实现放在源文件(.c)中。如下:

add.c

//函数的定义
int Add(int x, int y)
{
    int z;
    z = x + y;
    return z;
}

add.h

//函数的声明
int Add(int x, int y);

test.c

#include <stdio.h>
#include "add.h"

int main()
{
    int a=1;    
    int b=2;
    //函数调用
    int c=Add(a,b);
    printf("%d\n",c);
    return 0;
}

运行结果是: 

        到此为止,函数的一些基本概念就介绍完毕了,我认为比较重要的点在于自定义函数以及函数声明这两部分,要增加对其的理解。 

         

  • 30
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值