目录
前言:
一、C语言中函数的概念
二、库函数
2.1 库函数的概念
2.2 怎样自学库函数——以sqrt函数为例
三、自定义函数
3.1 自定义函数的概念
3.2 自定义函数的语法形式
3.3 函数的实参
3.4 函数的形参
3.5 实参与形参的关系
3.6 return语句
3.7 数组传参
四、函数的其他方面
4.1 函数的嵌套调用
4.2 函数的链式访问
4.3 函数的定义和声明
4.3.1 单文件中
4.3.2 多文件中
4.4 关键字static和extern
4.4.1 static修饰局部变量
4.4.2 static修饰全局变量
4.4.3 static修饰函数
4.4.4 关于函数声明的小总结
前言:
本篇主要讨论以下问题:
关于函数概念部分:
1. C语言中的函数是什么,在C语言中使用函数有哪些好处,C语言中的函数分为哪两大类
2. 标准库是什么,C语言中的头文件通常用于存放什么的
关于库函数部分:
3. 什么是库函数,我们应该怎样自学库函数
关于自定义函数部分:
4. 自定义函数的语法形式是怎样的,它的返回类型和形参部分有什么需要注意的地方
5. 如何区分实参和形参,实参和形参的关系是怎样的
6. 在自定义函数中写return语句需要注意哪5点
7. 当数组作为函数的参数时,我们该如何传参,定义函数时形参部分该怎样去接收数组
关于函数的其他方面:
8. 什么是函数的嵌套调用和链式访问,能否举个例子来说明一下
9. 函数在单个文件和多个文件中如何定义和声明
10. 模块化划分有哪些好处
11.关键字static和extern的功能分别是什么
12.关于函数的声明能否总结一下
一、C语言中函数的概念
1. C语言中的函数又被称为子程序,是完成特定任务的一小段代码。
2. 使用函数的好处:①逻辑清晰,方便拆分任务 ②可以重复使用,提高开发效率。
3. C语言中的函数分为两大类:库函数、自定义函数。
4. C语言规定了一系列标准,这些标准构成了标准库。
5. C语言的头文件(.h)通常用于存放函数的声明、符号的声明。
二、库函数
2.1 库函数的概念
1. 库函数是一系列程序员可直接调用的现成函数,调用这些函数前要在当前(.c)文件的头部用#include包含相关的头文件。(C语言标准只规定了有关库函数的一些语法规则,并没有提供现成的库函数,这些库函数的实现需要由不同的编译器厂商根据库函数相关的语法来实现,虽然各厂商实现的细节可能会有所差异,但对程序员使用来说没什么影响。)
2. C语言图书馆(查询库函数的地方):C library - C++ Reference (cplusplus.com)
2.2 怎样自学库函数——以sqrt函数为例
在自学库函数时需要观察的方面:
1. 函数的名称
2. 函数的语法形式
3. 函数的功能
4. 函数的形参
5. 函数的返回类型
6. 函数使用的代码举例,(可观察该函数所需包含的头文件),代码运行的结果
7. 与该函数有关联的其他库函数
三、自定义函数
3.1 自定义函数的概念
1. 自定义函数可用于提供程序员根据需要,自定义函数的参数和实现过程,达到实现某一特定功能函数的效果。
3.2 自定义函数的语法形式
//语法形式:ret_type fun_name(形式参数)
{
}
1. ret_type:函数的返回类型。(什么类型都行,也可以什么也不返回写上void,如果返回类型不写则会默认返回类型为int类型。)
2. fun_name:函数的名称。
3. (形式参数):形式参数个数可以为0~n个。(各形参的类型可以各不相同,且什么类型都行。当函数形参个数为0时,形式参数部分有两种不同的写法,写法一:写void,明确表示函数不需要任何参数,如果在函数调用时强制传参,则会报错;写法二:什么都不写,这种写法如果在函数调用时强制传参,函数会被正常调用,所以不推荐这种写法。)
4. { } 代码块里的函数体部分,用于写自定义函数实现的过程。
3.3 函数的实参
1. 在函数调用时,传递给函数的参数被称为实参,传参时不用注明变量的类型。
3.4 函数的形参
1. 在函数定义时( )中的参数被称为形参,形参在函数定义时要注明各参数是什么类型的。
3.5 实参与形参的关系
1. 形参是实参的一份临时拷贝。
2. 对形参的修改不会影响到实参。
3. 形参和实参可同名。
4. 只有在函数被调用后才会为形参向内存申请空间用于存放复制过来实参的值。
3.6 return语句
1. return语句后面可以是一个数值,也可以是一个表达式,如果是表达式则会先执行表达式再返回表达式的值。
2. return语句后面可以什么也没有写成return;适合函数返回类型为void的情况。
3. return返回的值与自定义函数的返回类型相冲突时,以自定义函数返回类型为尊,返回值将会被隐形强制转换。
4. return语句执行后,自定义函数中return语句后面的代码将不会再被执行。
5. 如果自定义函数的返回类型不为空,函数中存在分支语句,且分支语句中的某个分支中有return语句,那么自定函数所能执行的其他情况都要有return语句来保证函数逻辑的完整性,否则会出现编译错误。如果自定义函数返回类型为空,那么该函数可以有return;来提前终止代码,也可以没有,且函数即使存在分支语句,分支语句中的某个分支中有return语句,此时也不需要去考虑函数执行的其他情况下函数的返回结果的逻辑是否完整了。
3.7 数组传参
1.数组传参,函数调用时实参的部分写数组名和数组的大小即可,接收数组名的形参部分可以用数组的形式,也可以用指针的形式。
2. 如果接收数组名的形参部分用的是数组的形式,无论实参和形参部分的数组名是否相同,我们操作的数组始终都是实参的那个数组。
3. 形参如果是⼀维数组,数组⼤⼩可以省略不写。
4. 形参如果是⼆维数组,⾏的大小可以省略,但列的大小不能省略。
//采用数组传参,用Print函数分装打印数组的功能
#include <stdio.h>
void Print(int arr[], 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, 0 };
int sz = sizeof(arr) / sizeof(arr[0]);
Print(arr, sz);
return 0;
}
四、函数的其他方面
4.1 函数的嵌套调用
1. 函数的嵌套调用就是函数中调用函数。
2. 函数之间可以嵌套调用,但不可以嵌套定义。
//【计算某年某⽉有多少天】
#incluide <stdio.h>
//判断是否是闰年
int is_leap_year(int y)
{
if (((y % 4 == 0) && (y % 100 != 0)) || (y % 400 == 0))
return 1;
else
return 0;
}
//判断某月有多少天
int get_days_of_month(int y, int m)
{
int days[] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
int day = days[m];
if (is_leap_year(y) && m == 2)//函数调用
day += 1;
return day;
}
int main()
{
int y = 0;
int m = 0;
scanf("%d %d", &y, &m);
int d = get_days_of_month(y, m);//函数调用
printf("%d\n", d);
return 0;
}
4.2 函数的链式访问
1. 函数的链式访问就是将一个函数的返回值作为另一个函数的参数,实现像链条一样将函数串起来的效果。
#include <stdio.h>
#include <string.h>
int main()
{
printf("%zd\n", strlen("abcdef"));//6,strlen不计算\0
printf("%d", printf("%d\n", printf("%d", printf("%d", 43))));//4321\n2
return 0;
}
4.3 函数的定义和声明
4.3.1 单文件中
1. 只用到单个文件写代码时,一般会将每定义的一个新的自定义函数向上累加,即实现默认将函数定义写在调用函数之前的效果,这样在调用自定义函数时,就可以不需要对调用的自定义函数进行额外的声明了,因为函数的定义是一种特殊的函数声明形式。
2. 如果把函数定义写在调用函数之后了,这样在调用自定义函数时,和上面的写法相比会多一步,我们需要额外对被调用的自定义函数进行声明,函数的声明写在当前(.c)文件的头部即可,声明的格式是函数定义的第一行后面加一个封号。(总之,函数必须先声明后使用)
//【判断⼀年是不是闰年】
#include <stido.h>
//示例1:定义在前
int is_leap_year(int y)//声明
{
if(((y%4==0)&&(y%100!=0)) || (y%400==0))
return 1;
else
return 0;
}
int main()
{
int y = 0;
scanf("%d", &y);
int r = is_leap_year(y);//调用
if(r == 1)
printf("闰年\n");
else
printf("⾮闰年\n");
return 0;
}
//【判断⼀年是不是闰年】
#include <stido.h>
//示例2:定义在后
int is_leap_year(int y);//声明
int main()
{
int y = 0;
scanf("%d", &y);
int r = is_leap_year(y);//调用
if(r == 1)
printf("闰年\n");
else
printf("⾮闰年\n");
return 0;
}
//判断⼀年是不是闰年
int is_leap_year(int y)//声明
{
if(((y%4==0)&&(y%100!=0)) || (y%400==0))
return 1;
else
return 0;
}
4.3.2 多文件中
1. ⼀般在企业中写代码时候,代码可能⽐较多,不会将所有的代码都放在⼀个⽂件中;我们往往会根据程序的功能,将代码进行模块化拆分,每个模块都有着自己的(.h)和(.c)⽂件。
2. 头⽂件(.h)中会存放每个模块中的函数的声明、符号的声明,源⽂件(.c)中是每个模块写函数的定义的地方。
3. 如下图中所示,假设企业要完成一个计算器的实现程序,分为了4个模块,每个模块又有对应的(.c)和(.h)文件,(.c)文件中实现函数的定义,(.h)文件中实现函数的声明,在有main函数的(.c)文件中如果想要调用这些不同模块的函数的话,在文件开头用#include包含对应的头文件即可,但要注意包含自己的头文件时< >得换为" ",例如,#include "Add.h"。(头文件的包含本质是将头文件中的内容拷贝到当前的(.c)文件中,实现对相关函数的声明。)
4. 使用模块化划分的好处:①逻辑清晰 ②方便协作 ③把一个模块分为(.c)和(.h)文件,方便代码的隐藏(未来可以只给别人看(.h)文件)
4.4 关键字static和extern
概念:
① static是“静态的”意思,可以用来修饰局部变量、全局变量、函数。
② extern是用来修饰外部符号的,这里的外部指的是一个程序中不同(.c)文件(extern可以声明外部全局变量,也可以声明外部定义的自定义函数,格式例如,extern int arr;extern int Add (int a, int b); 声明外部的自定义函数时也可以直接写成例如,int Add (int a, int b); 因为函数的声明本身就自带extern,但最好还是写上)
补充:
① 作用域:⼀段程序代码中所⽤到的名字并不总是有效的,⽽限定这个名字的可⽤性的代码范围就是这个名字的作⽤域。
1. 局部变量的作⽤域是变量所在的局部范围
2. 全局变量的作⽤域是整个⼯程(项⽬)
② 生命周期:指的是变量的创建(申请内存)到变量的销毁(收回内存)之间的⼀个时间段。
1. 局部变量的⽣命周期是:进⼊作⽤域变量创建,⽣命周期开始,出作⽤域⽣命周期结束。 2. 全局变量的⽣命周期是:整个程序的⽣命周期。
4.4.1 static修饰局部变量
概念:static修饰局部变量改变了变量的⽣命周期,⽣命周期改变的本质是改变了变量在内存中的存储位置,本来⼀个局部变量是存储在内存的栈区的,但是被 static 修饰后存储到了静态区。存储在静态区的变量和全局变量是⼀样的,⽣命周期就和程序的⽣命周期⼀样了,只有程序结束,变量才销毁,但被修饰的局部变量的作⽤域不变。
使⽤建议:未来⼀个变量出了作用域后,我们还想保留它的值,等下次进⼊函数继续使⽤,就可以使⽤static 修饰,通常情况static修饰的局部变量都是自定义函数中的局部变量,main函数中的局部变量用static修饰一般没什么意义,代码是不断向下编程的。
总之,static修饰局部变量,这个局部变量的生命周期与全局变量的生命周期一样了,但作用域还是原来的作用域。
4.4.2 static修饰全局变量
概念:⼀个全局变量被static修饰,使得这个全局变量只能在本源⽂件内使⽤,不能在其他源⽂件内使⽤。本质原因是全局变量默认是具有外部链接属性的,在外部的⽂件中想使⽤,只要适当的声明就可以使⽤;但是全局变量被 static 修饰之后,外部链接属性就变成了内部链接属性,只能在⾃⼰所在的源⽂件内部使⽤了,其他源⽂件,即使声明了,也是⽆法正常使⽤的。
使⽤建议:如果⼀个全局变量,只想在所在的源⽂件内部使⽤,不想被其他⽂件发现,就可以使⽤ static修饰。
总之,static修饰全局变量,这个全局变量本来可以在整个程序的所有文件(.c)中用extern声明一下就可以自由使用了,但被static修饰后只能在自己所在的(.c)文件中使用,其他的(.c)文件中即使声明了也没用。
4.4.3 static修饰函数
概念:static 修饰函数和 static 修饰全局变量是⼀模⼀样的,⼀个函数在整个⼯程想使用都可以使⽤,只需要适当声明就可以,但被static修饰后,只能在本⽂件内部使⽤,其他⽂件⽆法正常的链接使⽤了。本质是因为函数默认是具有外部链接属性,具有外部链接属性,使得函数在整个⼯程中只要适当的声 明就可以被使⽤。但是被 static 修饰后变成了内部链接属性,使得函数只能在⾃⼰所在源⽂件内部 使⽤。
使⽤建议:⼀个函数只想在所在的源⽂件内部使⽤,不想被其他源⽂件使⽤,就可以使⽤ static 修饰。
总之,static修饰函数,函数和全局变量一样本来可以只需要进行相关的声明或extern即可在整个程序的所有(.c)文件中自由使用的,但被static修饰后只能在自己所在的(.c)文件中使用,在其他的(.c)文件中即使声明了也没用。
4.4.4 关于函数声明的小总结
1. 如果只用到一个(.c)文件写代码,函数的定义采用向上累加的方式。
2. 如果用到多个文件写代码,想调用其他(.c)文件中的函数时,方法一:如果有包含该函数声明的头文件,直接用#include包含对应的头文件即可;方法二:如果没有包含该函数声明的头文件,采用extern的形式声明一下该函数即可(也可以直接声明不写extern)。注意,如果该函数已被static修饰过,即使采用上面的方法实现函数的声明,函数也是无法被正常调用的。
本篇文章已完结,谢谢支持!!!