在数学上函数的概念就是:对于集合A中的每一个元素,按照某种确定的法则,都能在集合B中找到一个唯一确定的元素与之对应,那么这种对应关系就称为从集合A到集合B的函数。
在C语言中函数的概念和数学上函数的概念差不多,在C语言中,**函数就是一个完成某项特定的任务的一小段代码。**这段代码是有特定的写法和调用方法的。
在C语言中我们一般会看到两种函数
C语言标准中规定了C语言的各种语法规则,C语言并不提供库函数。C语言的国际标准ANSI C规定了一些常用的函数的标准,被称为标准库,而不同的编译厂商根据这个规则给出了函数的实现,被称为库函数。
比如:printf()和scanf()……,库函数也是函数,但是它们不需要我们编写代码用以实现其功能,我们只需直接使用就行了,
各种编译器的库函数实现内部可能有些许的差异,但是函数名和效果是一样的,这些库函数根据功能的划分,都在不同的头文件中进行了声明。
库函数相关头文件
库函数学习和查看的工具有很多,比如:
c/c++官方链接:网站
比如sqrt()的使用方法:
在这些参考网站中,库函数文档的一般格式:
自定义函数非常重要,它的书写给程序员写代码提供了更多的创造性
形式如下:
ret_type fun_name(形式参数){
}
我们可以将函数比作工厂,工厂加工原材料使其变成产品,而函数也是加工形式参数,变为结果进行输出
写一个加法函数,完成两个变量的加法操作
#include
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个值用于计算。
在上面代码中 int result = add(x, y);中的x和y就是实参,当我们调用add函数时,实际传递给函数的参数就是这个x和y,所谓实参就是指真实传递给函数的参数
在上面代码中 int add(int x, int y){……}中的x和y称为形式参数,简称形参。
为什么叫形式参数呢,这是因为如果不去调用add函数的话,add函数的参数x和y只是在形式上存在,而不是存在与地址中的,只有调用过程中传递过来了值,才向内存申请空间,所以这个过程被称为:实例化
虽然实参是传递给形参的,它们之间是有联系的,但是形参和实参是各自独立的内存单元
在代码中打印实参和形参的地址:
#include
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;
}
return语句的注意事项:
再使用函数解决问题的时候,难免会将数组作为参数传递给函数,在函数内部对数组进行操作
比如:
写一个函数将一个整型数组的内容全部置为-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;
}
嵌套调用就是函数之间的互相调用,也正是因为函数之间有效的互相调用,最后才能写出相对大型的程序
计算某年某月有多少天
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()
所谓链式访问就是将一个函数的返回值作为另一个函数的参数,像链条一样将函数串起来就是链式访问
比如:
#include
int main() {
int len = strlen("abcdef");
printf("%d\n", len);//输出6
return 0;
}
可以改为:
#include
int main() {
printf("%d\n", strlen("abcdef"));//输出6
return 0;
}
链式访问一定程度上降低了代码的书写量,但如果过长的链式访问就有可读性降低的风险了
一般在使用函数的时候,直接将函数写出来就可以使用了,像上面的代码,直接将自定义函数放在主函数的上面就可以了,但是如果我们将自定义函数放在主函数的下面,程序就会弹出警告。
#include
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;
}
但如果直接运行代码也不会报错,依然能够执行。
所以将自定义函数写在主函数的上面就叫做函数的定义。
其实解决方法就是函数的声明
上述箭头指向就是函数的声明,其实函数的定义也是一种特殊的函数的声明。
函数的调用一定要满足:先声明,再使用
一般在企业中写代码时,代码量很多,不会将所有代码放在一个文件中;我们往往会根据程序的功能,将代码拆分放在多个文件中
一般情况下:函数的声明,类型的声明放在头文(.h)中,函数的实现时放在源文件(.c)中.
写一个简单的加法计算器