一:库函数
二:自定义函数
ret_type fun_name(形式参数)
{
}
ret_type表示函数返回值的类型;fun_name为函数名称;形式参数要写清楚数量,类型。
三:形参与实参
1:形参
以一个相加函数来举例
#include <stdio.h>
int main(){
int add(int x,int y){
return x + y;
}
int a,b;
scanf("%d %d",&a,&b);
int c = add(a,b):
printf("%d",c);
return 0;
}
其中x与y为形参。如果add函数不被调用,那么x与y是不会占用空间的。
形参是为了在函数被调用时,存放实参传递过来的值,才会占用空间。这个过程叫做形参的实例化。
2:实参
上面程序中,a与b为实参,实参就是真正传递给函数的值。
3:形参与实参的关系
当调用函数时,形参与实参内容相同,但是其地址却不相同,所以说形参是实参的一份临时拷贝。
四:return语句
1:return后边可以是⼀个数值,也可以是⼀个表达式,如果是表达式则先执⾏表达式,再返回表达式的结果。
2:return后边也可以什么都没有,直接写 return; 这种写法适合函数返回类型是void的情况。
int main(){
void example(){
printf(“haha”);
return;
}
example();
return 0;
}
当然,void类型函数可以不写return语句。但也只有void类型函数可以不写return语句。
3:return返回的值和函数返回类型不⼀致,系统会⾃动将返回的值隐式转换为函数的返回类型。
4:return语句执行后,函数就彻底返回,后边的代码不再执行。
//计算某年某月有多少天
#include <stdio.h>
int cal_days(int YEAR, int MONTH) {
if (MONTH == 2) {
if (YEAR % 400 == 0 || (YEAR % 4 == 0) && (YEAR % 100 != 0)) {
if (MONTH == 2) {
return 29;
}
}
else {
return 28;
}
}
else if (MONTH % 2 == 0) {
return 30;
}
else {
return 31;
}
}
int main() {
int year = 0;
int month = 0;
scanf("%d %d", &year, &month);
int days = cal_days(year, month);
printf("%d年%d月有%d天", year, month, days);
return 0;
}
五:数组做函数参数
数组也可以作为函数的参数,即实参可以是数组,同时,形参也要是数组。并且此时,形参与实参完全相等。
示例:
//设置数组内容为-1
#include <stdio.h>
void set_arr(int arr[],int sz){
for(int i = 0;i < sz;i++){
arr[i] = -1;
}
}
int main()
{
int arr[] = {1,2,3,4,5,6,7,8,9,10};
int sz = sizeof(arr) / sizeof(int);
set_arr(arr,sz);//注意这里是arr不是arr[]
return 0;
}
六:函数的链式访问:
函数的链式访问就是把一个函数的返回值作为另一个函数的参数。
示例:
#include <stdio.h>
int main()
{
printf("%d", printf("%d", printf("%d", 43)));
return 0;
}
结果为4321
这里搞清楚printf函数的返回值就明白了。
printf返回的值为其包含的字符个数。
七:函数的声明与调用
如果要调用一个函数,那么一定要在调用前声明这个函数
1:单个文件:
1.1、对于单个文件,最常见的就是把自定义函数放在需要调用这个函数的函数前面。那么,对于这个函数的自定义就等于声明了这个函数。
1.2、特殊声明:如果将自定义函数放在需要调用这个函数的函数后面,那么需要在调用前进行声明。格式就是重新写一遍自定义函数,但是不用包括这个函数的内容。
2:多个文件:
在代码比较多时,我们通常不会把全部的代码放在一个文件中。会根据程序的功能,将代码拆分放在多个文件中。一般情况下,函数的声明、类型的声明放在头文件(.h)中,函数的定义的在源文件(.c)中
比如:
add.c
//函数的定义
int Add(int x, int y)
{
return x+y;
}
add.h
//函数的声明
int Add(int x, int y);
test.c
#include <stdio.h>
#include "add.h"
int main()
{
int a = 10;
int b = 20;
//函数调⽤
int c = Add(a, b);
printf("%d\n", c);
return 0;
}
其中函数的定义与调用都是在.c文件中。必须要相同才能实现,不能一个放在.c一个放在.c++。并且,调用时,需要调用头文件。
#include "add.h"
这样的好处是使得代码变得更加方便。
八:static与extern
作用域:一个变量所作用的范围
1、 局部变量的作用域:变量所在的局部范围;
2、 全局变量的作用域:整个程序
生命周期:一个变量从开始申请空间到结束收回空间。
1、 局部变量的生命周期:进入局部变量的作用域开始到离开局部变量的作用域结束
2、 全局变量的生命周期:整个程序的生命周期
1:static修饰局部变量
//无static
#include <stdio.h>
void test()
{
int i = 0;
i++;
printf("%d ", i);
}
int main()
{
int i = 0;
for(i=0; i<5; i++)
{
test();
}
return 0;
}
//结果为11111
//有static
#include <stdio.h>
void test()
{
static int i = 0;
i++;
printf("%d ", i);
}
int main()
{
int i = 0;
for(i=0; i<5; i++)
{
test();
}
return 0;
}
//结果为12345
两者不同,是因为第二种test()中的 int i用了static来定义。简单来说,就是使得static int i = 0 相当于全局变量,因此static int i = 0只执行一次,下一次执行这个test()函数时,i保持上一次执行的结果。
总结:有了static的作用,他将局部变量从原来的栈区(专门存放局部变量、函数参数的区域)移动到了静态区(专门存放全局变量、静态变量的区域)。使得修饰的局部变量的生命周期变为整个程序的生命周期,但是作用域不变。
适用于:定义一个函数,给出一个变量时,想要下一次再进入这个函数,变量保持上一次的结果。
2:static修饰全局变量或函数
情况一:
add.c
//函数的定义
int Add(int x, int y)
{
return x+y;
}
test.c
#include <stdio.h>
int main()
{
int a = 10;
int b = 20;
extern int Add(int x,int y);//函数调⽤
int c = Add(a, b);
printf("%d\n", c);
return 0;
}
//这里没有使用头文件来调用
这里只包含两个文件,没有头文件的参与。使用extern来调用。
extern的专门用来调用外部(同一类型)文件中的变量、函数的。在A文件中声明,在B文件中可以使用extern来调用。
情况二:
add.c
//函数的定义
static int Add(int x, int y)
{
return x+y;
}
test.c
#include <stdio.h>
int main()
{
int a = 10;
int b = 20;
extern int Add(int x,int y);//函数调⽤
int c = Add(a, b);
printf("%d\n", c);
return 0;
}
//这里没有使用头文件来调用
当使用static修饰函数时,程序执行会报链接性错,使用extern也没法调用这个函数了。
结论:对于一个全局变量,是具有外部链接属性的,其他文件想要使用,只需声明便可。但是有了static修饰,这个变量便没有了外部链接属性,只有内部链接属性,因此,其他文件无法调用。