目录
1.函数的概念
在数学上函数的概念就是:对于集合A中的每一个元素,按照某种确定的法则,都能在集合B中找到一个唯一确定的元素与之对应,那么这种对应关系就称为从集合A到集合B的函数。
在C语言中函数的概念和数学上函数的概念差不多,在C语言中,**函数就是一个完成某项特定的任务的一小段代码。**这段代码是有特定的写法和调用方法的。
在C语言中我们一般会看到两种函数
- 库函数
- 自定义函数
2.库函数
2.1.标准库和头文件
C语言标准中规定了C语言的各种语法规则,C语言并不提供库函数。C语言的国际标准ANSI C规定了一些常用的函数的标准,被称为标准库,而不同的编译厂商根据这个规则给出了函数的实现,被称为库函数。
比如:printf()和scanf()……,库函数也是函数,但是它们不需要我们编写代码用以实现其功能,我们只需直接使用就行了,
各种编译器的库函数实现内部可能有些许的差异,但是函数名和效果是一样的,这些库函数根据功能的划分,都在不同的头文件中进行了声明。
库函数相关头文件
2.2.库函数使用方法
库函数学习和查看的工具有很多,比如:
c/c++官方链接:网站
比如sqrt()的使用方法:
在这些参考网站中,库函数文档的一般格式:
- 函数原型
- 函数功能介绍
- 参数和返回类型说明
- 代码举例
- 代码输出
- 相关知识链接
3.自定义函数
自定义函数非常重要,它的书写给程序员写代码提供了更多的创造性
3.1.函数的语法形式
形式如下:
ret_type fun_name(形式参数){
}
- ret_type是函数的返回类型,有时返回类型可以是void,表示什么都不返回
- fun_name是函数的函数名,一般来说,起名都要有意义
- 括号中放的是形式参数,有时也可以是void,表示没有参数传进来
- {}括起来的是函数体,就是完成计算的过程
我们可以将函数比作工厂,工厂加工原材料使其变成产品,而函数也是加工形式参数,变为结果进行输出
3.2.函数举例
写一个加法函数,完成两个变量的加法操作
#include<stdio.h>
int add(int x, int y) {
return x + y;
}
int main() {
int x = 10;
int y = 23;
int result = add(x, y);
printf("%d\n", result);//输出33
return 0;
}
这里自定义函数add完成了加法操作,而在main函数中调用了这个函数并传了2个值用于计算。
- 参数的个数也是自定义的,根据实际情况来确定,可以有0个参数,也可以有许多个参数
4.形参和实参
在函数调用过程中,把函数分为实参和形参
4.1.实参
在上面代码中 int result = add(x, y);中的x和y就是实参,当我们调用add函数时,实际传递给函数的参数就是这个x和y,所谓实参就是指真实传递给函数的参数
4.2.形参
在上面代码中 int add(int x, int y){……}中的x和y称为形式参数,简称形参。
为什么叫形式参数呢,这是因为如果不去调用add函数的话,add函数的参数x和y只是在形式上存在,而不是存在与地址中的,只有调用过程中传递过来了值,才向内存申请空间,所以这个过程被称为:实例化
4.3.实参和形参的关系
虽然实参是传递给形参的,它们之间是有联系的,但是形参和实参是各自独立的内存单元
在代码中打印实参和形参的地址:
#include<stdio.h>
int add(int x, int y) {
printf("形参x为:%p 形参y为:%p\n", &x, &y);
return x + y;
}
int main() {
int x = 10;
int y = 23;
printf("实参x为:%p 实参y为:%p\n", &x, &y);
int result = add(x, y);
printf("%d\n", result);//输出33
return 0;
}
所以,形参是实参的一份临时拷贝
5.return语句
return语句的注意事项:
- return后面可以是一个数值也可以是一个表达式,如果是表达式则先执行表达式,再返回表达式的结果。
- 可以直接写return,这种写法适用于函数返回值类型是void的时候
- return返回值和返回类型不一致,系统会自动将返回结果强制转换为返回类型
- return语句执行后,函数就彻底返回,后面的代码不再执行
- 如果函数中存在if等分支语句,则要保证每种情况下都要有return返回,否则会出现编译错误
6.数组作为函数参数
再使用函数解决问题的时候,难免会将数组作为参数传递给函数,在函数内部对数组进行操作
比如:
写一个函数将一个整型数组的内容全部置为-1,再写一个函数打印数组的内容
void set_arr(int arr[], int sz) {
for (int i = 0; i < sz; i++) {
arr[i] = -1;
}
}
void printf_arr(int arr[], int sz) {
for (int i = 0; i < sz; i++) {
printf("%d ", arr[i]);
}
printf("\n");
}
int main() {
int arr[] = {
1,2,3,4,5 };
int sz = sizeof(arr) / sizeof(arr[0]);
printf_arr(arr, sz);
set_arr(arr, sz);//将数组元素置为1
printf_arr(arr, sz);//打印数组
return 0;
}
数组传参的几个重要知识点:
- 函数的形式参数和实际参数个数要匹配
- 函数的实参是数组,形参也可以是数组
- 形参如果是一维数组,数组的大小可以不写
- 形参如果是二维数组,行可以省略,列不能省略
- 数组传参,形参是不会创建新的数组的
- 形参操作的数组和实参的数组是同一个数组,也就是说两个数组是同一个数组,地址是一样的
void set_arr(int arr[], int sz) {
for (int i = 0; i < sz; i++) {
arr[i] = -1;
}
}
void printf_arr(int arr[], int sz) {
for (int i = 0; i < sz; i++) {
printf("%d ", arr[i]);
}
printf("\n");
printf("地址:%p", &arr[0]);
}
int main() {
int arr[] = {
1,2,3,4,5 };
int sz = sizeof(arr) / sizeof(arr[0]);
printf("地址:%p", & arr[0]);
//printf_arr(arr, sz);
printf("\n");
set_arr(arr, sz);//将数组元素置为1
printf_arr(arr, sz);//打印数组
return 0;
}
7.嵌套调用和链式访问
7.1.嵌套调用
嵌套调用就是函数之间的互相调用,也正是因为函数之间有效的互相调用,最后才能写出相对大型的程序
计算某年某月有多少天
is_leap_year():根据年份确定是否是闰年
get_days_of_month():调用is_leap_year()确定是闰年后,再根据月计算天数
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 arr[] = {
0,31,28,31,30,31,30,31,31,30,31,30 };//在数组前加一个0,这样每个天数就可以和月份以及下标对应
int day = arr[m];
if (is_leap_year(y) == 1 && m == 2) {
day += 1;
}
return day;
}
int main() {
printf("请输入年份:>");
int year = 0;
scanf("%d", &year);
printf("\n请输入月份:>");
int month = 0;
scanf("%d", &month);
printf("\n该月份有%d天\n", get_days_of_month(year, month));
return 0;
}
其中就在get_days_of_month()函数中嵌套了函数is_leap_year()
7.2.链式访问
所谓链式访问就是将一个函数的返回值作为另一个函数的参数,像链条一样将函数串起来就是链式访问
比如:
#include<string.h>
int main() {
int len = strlen("abcdef");
printf("%d\n", len);//输出6
return 0;
}
可以改为:
#include<string.h>
int main() {
printf("%d\n", strlen("abcdef"));//输出6
return 0;
}
链式访问一定程度上降低了代码的书写量,但如果过长的链式访问就有可读性降低的风险了
8.函数的声明和定义
8.1.单个文件
一般在使用函数的时候,直接将函数写出来就可以使用了,像上面的代码,直接将自定义函数放在主函数的上面就可以了,但是如果我们将自定义函数放在主函数的下面,程序就会弹出警告。
#include<stdio.h>
int main() {
int x = 10;
int y = 23;
printf("实参x为:%p 实参y为:%p\n", &x, &y);
int result = add(x, y);
printf("%d\n", result);//输出33
return 0;
}
int add(int x, int y) {
printf("形参x为:%p 形参y为:%p\n", &x, &y);
return x + y;
}
但如果直接运行代码也不会报错,依然能够执行。
所以将自定义函数写在主函数的上面就叫做函数的定义。
其实解决方法就是函数的声明
上述箭头指向就是函数的声明,其实函数的定义也是一种特殊的函数的声明。
函数的调用一定要满足:先声明,再使用
8.2.多个文件
一般在企业中写代码时,代码量很多,不会将所有代码放在一个文件中;我们往往会根据程序的功能,将代码拆分放在多个文件中
一般情况下:函数的声明,类型的声明放在头文(.h)中,函数的实现时放在源文件(.c)中.
写一个简单的加法计算器