目录
一、C语言中函数的概念
二、库函数
2.1 库函数的概念
2.2 怎样自己学习库函数_以sqrt函数为例
三、自定义函数
3.1 自定义函数的语法形式
3.2 自定义函数的举例
3.3 自定义函数的实参
3.4 自定义函数的形参
3.5 自定义函数的实参和形参的关系
3.6 自定义函数中的return语句
3.7 数组做自定义函数的参数
3.8 函数的嵌套调用
3.9 函数的链式访问
四、函数的定义、声明和使用
4.1 单文件中
4.2 多文件中
五、关键字static和extern
5.1 static修饰局部变量
5.2 static修饰全局变量
5.3 static修饰函数
一、C语言中函数的概念
1. C语言是由一个个函数构成的,每一个函数用于完成程序中某个特定的任务。
2. 采用函数的好处:①可以拆分任务 ②可以重复使用。
3. C语言中的函数分为库函数和自定义函数两大类。
二、库函数
2.1 库函数的概念
1. 库函数是一些可以现成使用的函数,在使用库函数时要提前对该库函数所包含的头文件进行声明。
2. 补充:C语言规定了库函数的语法标准,这些标准组成了标准库,但C语言并不提供现成的库函数,这些库函数的实现由不同的编译器厂商实现,函数的实现细节可能有差异,但程序员在使用时基本不影响。
3. 库函数的官网:C library - C++ Reference (cplusplus.com)
2.2 怎样自己学习库函数_以sqrt函数为例
从上至下:
① 函数的语法形式
② 函数的功能、
③ 函数的参数说明
④ 函数返回值说明
⑤ 函数使用时的代码举例(含所需包含的头文件)
⑥ 与之类型的其他库函数
三、自定义函数
3.1 自定义函数的语法形式
//语法形式:ret_type fun_name(形式参数)
{
}
1. ret_type:函数的返回类型(什么类型都可以,也可以什么也不返回void,如果返回类型不写则会默认返回类型为int类型)。
2. fun_name:函数的名称。
3. (形式参数):形式参数可以没有(空或void),也可以有1个或多个,这些形参采用什么类型都可以(如果形参为空,在函数调用时强制传参,该函数也会被正常调用)。
4. { } 代码块里写自定义函数实现的过程。
3.2 自定义函数的举例
//用Add函数分装加法功能
#include <stdio.h>
int Add(int x, int y)//x y为形参
{
return x + y;
}
int main()
{
int a = 0, b = 0;
scanf("%d %d", &a, &b);
int ret = Add(a, b);//a b为形参
printf("%d\n", ret);
return 0;
}
3.3 自定义函数的实参
1. 在函数调用时,传递给函数的参数被称为实参,传参时不用注明变量的类型。
3.4 自定义函数的形参
1. 在函数定义时( )中的参数被称为形参,形参在函数定义时要注明各各参数是什么类型的。
2. 补充:函数在没有被调用时,自定义函数的形参变量并没有在内存中申请空间,只有在自定义函数被调用后为了复制实参的值才会向内存申请空间。
3.5 自定义函数的实参和形参的关系
1. 形参是实参的一份临时拷贝,对形参的修改并不会改变实参的数值,形参和实参可同名。
3.6 自定义函数中的return语句
1. 自定义函数中如果返回类型为void可以省略return语句,也可以写成void;
2. return语句后面可以是一个数值,也可以是一个表达式,如果是表达式则会先执行表达式再返回表达式的值。
3. return返回的值与自定义函数的返回类型相冲突时,自定义函数返回类型为尊,返回值将会被隐形强制转换。
4. return语句执行后,自定义函数中return语句后面的代码将不会再被执行。
5. 如果自定义函数中存在分支语句,则要保证每个分支下都有return语句,否则会出现编译错误。
3.7 数组做自定义函数的参数
1. 数组传参,形参是不会创建新数组的,形参操作的数组和实参的数组是同⼀个数组。
2. 函数的形参个数要和函数的实参个数匹配,在传参时把数组的大小一并传过去。
3. 数组传参时,实参部分写成数组名,形参写成数组的形式即可(数组名其实是数组首元素的地址,数组传参的本质是传址调用而不是传值调用)。
4. 形参如果是⼀维数组,数组⼤⼩可以省略不写。
5. 形参如果是⼆维数组,⾏的大小可以省略,但列的大小不能省略。
//用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;
}
3.8 函数的嵌套调用
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;
}
3.9 函数的链式访问
1. 函数的链式访问就是将一个函数的返回值作为另一个函数的参数,实现像链条一样将函数串起来的效果。
#include <stdio.h>
#include <string.h>
//示例1
int main()
{
printf("%zd\n", strlen("abcdef"));
return 0;
}
#include <stdio.h>
#include <string.h>
//示例2
int main()
{
printf("%d", printf("%d", printf("%d", 43)));
return 0;
}
四、函数的定义、声明和使用
4.1 单文件中
1. 只需用到.c单个文件写代码时,一般将自定义函数的函数定义写在被调用时所在的函数之前,这样在调用自定义函数时,就可以不需要对调用的自定义函数进行声明,因为函数的定义是一种特殊的函数声明形式。
2. 如果把自定义函数的函数定义写在被调用时所在的函数之后了,这样在调用自定义函数时,就必须要对调用的自定义函数进行声明,在整个文件开头声明就行,声明的格式是函数定义的第一行后面加一个封号。
//【判断⼀年是不是闰年】
#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.2 多文件中
1. ⼀般在企业中写代码时候,代码可能⽐较多,不会将所有的代码都放在⼀个⽂件中;我们往往会根据程序的功能,将代码拆分放在多个(.h)和(.c)⽂件中。
2. ⼀般情况下,函数的声明、类型的声明放在头⽂件(.h)中,函数的定义是放在源⽂件(.c)
⽂件中。
3. 如下图中所示,假设企业要完成一个计算器的实现程序,分为了4个模块,每个模块又有对应的(.c)和(.h)文件,(.c)文件中实现函数的定义,(.h)文件中实现函数的声明,在有main函数的(.c)文件中如果想要调用这些不同模块的函数的话,在文件开头写上包含对应的头文件即可,但< >要换为" ",例如,#include "Add.h",头文件的包含本质上也是一种函数的声明。
4. 优点:①逻辑清晰 ②方便协作 ③把一个模块又分为(.c)和(.h)文件后也方便代码的隐藏(未来可以只给别人看(.h)文件)
五、关键字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. 全局变量的⽣命周期是:整个程序的⽣命周期。
5.1 static修饰局部变量
概念:static修饰局部变量改变了变量的⽣命周期,⽣命周期改变的本质是改变了变量的存储类型,本来⼀个局部变量是存储在内存的栈区的,但是被 static 修饰后存储到了静态区。存储在静态区的变量和全局变量是⼀样的,⽣命周期就和程序的⽣命周期⼀样了,只有程序结束,变量才销毁,但被修饰的局部变量的作⽤域不变。
使⽤建议:未来⼀个变量出了函数后,我们还想保留值,等下次进⼊函数继续使⽤,就可以使⽤static 修饰。
总之,static修饰局部变量,这个局部变量的生命周期与全局变量的生命周期一样了,但作用域还是原来的作用域。
5.2 static修饰全局变量
概念:⼀个全局变量被static修饰,使得这个全局变量只能在本源⽂件内使⽤,不能在其他源⽂件内使⽤。本质原因是全局变量默认是具有外部链接属性的,在外部的⽂件中想使⽤,只要适当的声明就可以使⽤;但是全局变量被 static 修饰之后,外部链接属性就变成了内部链接属性,只能在⾃⼰所在的源⽂件内部使⽤了,其他源⽂件,即使声明了,也是⽆法正常使⽤的。
使⽤建议:如果⼀个全局变量,只想在所在的源⽂件内部使⽤,不想被其他⽂件发现,就可以使⽤ static修饰。
总之,static修饰全局变量,这个全局变量本来可以在整个程序的所有文件(.c)中用extern声明一下就可以用了,但被static修饰后只能在自己所在的(.c)文件中使用,其他的(.c)文件中即使声明了也没用。
5.3 static修饰函数
概念:static 修饰函数和 static 修饰全局变量是⼀模⼀样的,⼀个函数在整个⼯程想使用都可以使⽤,只需要适当声明就可以,但被static修饰后,只能在本⽂件内部使⽤,其他⽂件⽆法正常的链接使⽤了。本质是因为函数默认是具有外部链接属性,具有外部链接属性,使得函数在整个⼯程中只要适当的声 明就可以被使⽤。但是被 static 修饰后变成了内部链接属性,使得函数只能在⾃⼰所在源⽂件内部 使⽤。
使⽤建议:⼀个函数只想在所在的源⽂件内部使⽤,不想被其他源⽂件使⽤,就可以使⽤ static 修饰。
总之,static修饰函数,函数和全局变量一样本来也可以在整个程序的所有文件(.c)中用extern声明一下,或者在(.h)文件中声明,再在想调用它的(.c)文件中声明一下这个函数所在的(.h)就可以用了,但被static修饰后只能在自己所在的(.c)文件中使用,其他的(.c)文件中即使声明了也没用。
本篇已完结。。。。。。