零基础非科班也能掌握的C语言知识10 函数(以及解释作者为什么快一周没更新博客了)

作者下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去声明外部函数
在这里插入图片描述

以上就是函数的全部内容,基本能包含的地方都包含,六千字的博客希望大家多多三连

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值