作者下origin的时候给电脑搞重装了,自己修的时候又把电源搞坏了,这两周又要金工实习比较忙,但是我会尽量把博客的内容都补上的。以后保证每天都更新一篇博客
函数
1.函数的概念
简单来说一个程序是由若干个函数组成的,也就是若干子程序组成的(更助于理解)。
在C语言中我们将函数分为:
- 库函数
- 自定义函数
2.库函数
2.1 标准库和头文件
简单来说就是ANSI这个机构规定了一些常用的函数的标准,但是具体实现是看你的编译器厂商,不同厂商
同时库函数种类繁多,一个个都记忆下来是不实在,因此我们往往在面对具体问题时去查表获取相应的库函数
库函数相关头⽂件:https://zh.cppreference.com/w/c/header
2.2库函数文档的一般格式
如上图,我们可以看到函数名是abs,函数功能的介绍计算整数的绝对值,函数的参数是整形,返回值n的绝对值,实例和输出,引用等等
通过上述网站我们能自学很多函数
3.自定义函数
实际上用户的需求是非常广泛,这就常常需要我们针对需求写自定义函数。
3.1 函数的语法形式
ret_type fun_name(形式参数)
{
}
- ret_type 是函数的返回类型包括int,double,float,size_t,甚至你不返回void,或者你返回的是一个指针int*都行
- fun_name是函数名,你调用函数时通过函数的名字或者更准确的说通过指针去调用函数
- 括号体放的是形式参数,包括int,double,float,size_t,void表示不返回或者直接不写,甚至你返回的是一个指针int*等等都行
3.2函数的举例
int add(int x, int y)
{
return x + y;
}
int sub(int x, int y)
{
return x - y;
}
int mul(int x, int y)
{
return x * y;
}
int div(int x, int y)
{
return x / y;
}//这里包括了加减乘除所有函数,当然这里写的返回值和形参都是int类型,具体情况肯定要相应的更改。
尽管我这里并没有相应的实例,但是往往自定义函数包含函数名,返回类型,形参,具体实现和返回值即可,当然大家最好在实现完函数去调试一下不然等全部函数写完出现错误再去找bug就会困难许多。
4.形参和实参
形参使用在函数的定义中,实参用在函数的调用中
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
void init (int arr[], int r)//函数的定义
{
for (int i = 0; i < r; i++)
{
arr[i] = i;
}
}
void print(int arr[],int r)函数的定义
{
for (int i = 0; i < r; i++)
{
printf("%d ", arr[i]);
}
printf("\n");
}
void reverse(int arr[],int r)函数的定义
{
for (int right = r - 1; right >= 0; right--)
{
printf("%d ", arr[right]);
}
printf("\n");
}
int main()
{
int arr[10] = { 0 };
int r = sizeof(arr) / sizeof(arr[0]);
init(arr,r);函数的调用
print(arr,r);函数的调用
reverse(arr, r);函数的调用
return 0;
}
在函数的声明中除了形参我们还要定义形参的类型,而在实参中我们只需要将实参输入即可
4.1形参
可以看到在没有调用函数时,形参是未被定义的
当我调用函数,将实参传递给形参时,形参才实例化
4.2形参和实参的关系
通过上图我们在调试的时候可以观察到,arr1和r1确实得到了arr和r的值,arr和arr1的地址相同而r和r1的地址是不⼀样的,所以我们可以理解为当传值时形参是实参的⼀份临时拷⻉。
这就是传值调用和传址调用的不同之处,后面会细讲,同时大家发现没有当我去调用函数时实参就不允许去改动了
5.返回语句
6.数组做函数参数
在使⽤函数解决问题的时候,难免会将数组作为参数传递给函数,在函数内部对数组进⾏操作。
int main()
{
int arr[10] = { 0 };
int r = sizeof(arr) / sizeof(arr[0]);
init(arr,r);//实现输入
print(arr,r);//实现打印
reverse(arr, r);//实现逆序输出
return 0;
}
void init (int arr1[], int r1)
{
for (int i = 0; i < r1; i++)
{
arr1[i] = i;
}
}
void print(int arr[],int r)
{
for (int i = 0; i < r; i++)
{
printf("%d ", arr[i]);
}
printf("\n");
}//注意这里我把函数放后面了方便读者阅读,但这个时候函数就要声明了,读者复制代码的时候建议把函数放在主函数前面或者对进行声明。
void reverse(int arr[],int r)
{
for (int right = r - 1; right >= 0; right--)
{
printf("%d ", arr[right]);
}
printf("\n");
}
7.嵌套调用和链式访问
7.1嵌套调用
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
int leap_year(int year)
{
if ((year % 4 == 0 && year % 400 != 0) || (year % 400 == 0))
return 1;
else
return 0;
}
int get_days_of_month(int year,int month)
{
int days[] = { 31,28,31,30,31,30,31,31,30,31,30,31 };
if (leap_year(year) == 1)
days[1]++;
return days[month - 1];
}
int main()
{
int year = 0;
int month = 0;
while (scanf("%d%d", &year, &month) != EOF)
{
int day = get_days_of_month(year, month);
printf("%d\n", day);
}
}
大一些的程序都是函数的相互调用,但是是不能嵌套定义
7.2链式访问
所谓链式访问就是将⼀个函数的返回值作为另外⼀个函数的参数,像链条⼀样将函数串起来就是函数的链式访问。
比如:
#include <stdio.h>
int main()
{
printf("%d", printf("%d", printf("%d", 43)));
return 0;
}
这个代码的关键是弄清楚printf的返回值
*int printf( const char format, … );,返回的是整形
8.函数的定义与访问
8.1单个文件
⼀般我们在使⽤函数的时候,直接将函数写出来就使⽤了。
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
void leap_year(int a)
{
if ((a % 4 == 0 && a % 400 != 0) || (a % 400 == 0))
printf("%d是闰年", a);
}//函数的定义
int main()
{
int a = 0;
while (scanf("%d", &a) != EOF)
leap_year(a);//函数的调用
return 0;
}
但如果我们将两者的顺序颠倒呢?
这是因为C语⾔编译器对源代码进⾏编译的时候,从第⼀⾏往下扫描的,当遇到leap_year函数调⽤的时候,并没有发现前⾯有leap_year的定义,就报出了上述的警告。
把怎么解决这个问题呢?就是函数调⽤之前先声明⼀下leap_year这个函数,声明函数只要交代清楚:函数名,函数的返回类型和函数的参数。
如:int leap_year(int year);这就是函数声明,函数声明中参数只保留类型,省略掉名字也是可以的。
代码变成这样就能正常编译了。
函数的调⽤⼀定要满⾜,先声明后使⽤;
函数的定义也是⼀种特殊的声明,所以如果函数定义放在调⽤之前也是可以的。
8.2多个文件
⼀般在企业中我们写代码时候,代码可能⽐较多,不会将所有的代码都放在⼀个⽂件中;我们往往会
根据程序的功能,将代码拆分放在多个⽂件中。
⼀般情况下,函数的声明、类型的声明放在头⽂件(.h)中,函数的实现是放在源⽂件(.c)⽂件中。
如下:
此时我们要调用函数时,只需包含我们自己的头文件 #include(文件名.h)
8.3static和extern
作用域类似函数的定义域,生命周期就从上图理解
8.3.1 static修饰局部变量
举例:
//代码1
#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;
}
//代码2
#include <stdio.h>
void test()
{
//static修饰局部变量
static int i = 0;
i++;
printf("%d ", i);
}
int main()
{
int i = 0;
for(i=0; i<5; i++)
{
test();
}
return 0;
}
使⽤建议:未来⼀个变量出了函数后,我们还想保留值,等下次进⼊函数继续使⽤,就可以使⽤static
修饰。
8.3.2static修饰全局变量
extern是⽤来声明外部符号的,如果⼀个全局的符号在A⽂件中定义的,在B⽂件中想使⽤,就可以使
⽤ extern 进⾏声明,然后使⽤。
如:我们在A文件定义了全局变量int number=117;,此时我们现在B文件时使用应声明也就是extern int number=117;
8.3.3static修饰函数
调用其他源文件中的函数我们一般是在头文件里声明,然后让源文件包含头文件。不好意思这里我也不清楚能不能用extern去声明函数,我问问明天给大家答复。
可以用extern去声明外部函数
以上就是函数的全部内容,基本能包含的地方都包含,六千字的博客希望大家多多三连