学习C语言(六)——函数

目录

1.函数的概念

2.库函数

3.自定义函数

3.1 函数的语法

3.2 函数的举例

4.形参和实参。

 4.1实参

4.2形参

4.3 实参和形参的关系

5.return语句

6.数组做函数参数

7.嵌套调用和链式访问

7.1 嵌套调用

7.2 链式访问

8.函数的声明和定义

8.1 单个文件

8.2 多个文件

8.3 static和extern

8.3.1 static 修饰局部变量

8.3.2 static修饰全局变量

8.3.3 static修饰函数


1.函数的概念

        数学中有函数的概念,在C语言中也引入了函数的概念,有些翻译为:子程序,子程序这种翻译更加准确一些。

        C语言中的函数就是一个完成某项特定的任务的一小段代码。这段代码是有特殊的写法和调用方法的。C语言的程序就是由无数个大小的函数组合而成,这些函数也是可以重复使用的。

        C语言中常见的两类函数:1.库函数   2.自定义函数

2.库函数

        C语言规定了C语言中的语法规则,但并不提供库函数。不同编译器厂商根据ANsi提供的C预压标准给出了一系列函数的实现,被称为库函数

·        之前的 printf、scanf都是库函数,也是现成的函数。库函数根据功能的划分,在不同的头文件中进行了声明,当我们使用这些库函数时,都需要调用这些声明。

        库函数头文件:C 标准库头文件 - cppreference.com

3.自定义函数

        自定义函数为程序员写代码创造了更多的可能性。

3.1 函数的语法

        自定义函数和库函数是一样的,形式如下:

函数返回类型  函数名(形式参数)
{
    函数体
}
  • 函数返回类型  表示函数计算结果的类型,有时候返回类型可以使void,表示什么都不返回。
  • 函数名  是为了方便使用函数;有了函数名后就方便调用,所以要函数名要尽量有意义。
  • 形式参数  函数的参数可以是void,表示函数没有参数。如果有参数,就需要交代清楚参数的类型、名字以及参数个数。
  • { } 括起来的部分被称为函数体,就是完成计算的过程。

3.2 函数的举例

        举个栗子。写一个加法函数,完成两个整型变量的加法操作。代码如下:

int ADD(int x ,int y)
{
	int sum = x + y;
	return sum;
	//  当然为了简化函数,可以直接写
	// return x+y;    返回的值就会返回到这个函数上
}
int main()
{
	int a = 0;
	int b = 0;
	scanf("%d %d",&a,&b);

	// 使用 两个变量代替函数中的两个形式参数
	int r = ADD(a, b);		//可以将函数返回值放入 r 中
	printf("两数相加之和是:%d\n", r);
	//也可以直接打印函数返回值
	printf("两数相加之和是:%d\n", ADD(a, b));
	//但是不可以打印 sum,sum是只在 ADD函数中生命的变量

	return 0;
}

        使用效果如下:       

4.形参和实参。

        在函数使用的过程中,我们把函数分为,实参形参。

        让我们以之前的代码为例:

 4.1实参

         在上面的代码中。第45-51行的代码是对 ADD函数的定义,有了函数,才能在59行和62行调用函数。

        在调用函数时,将参数 a 和 b 传递给函数,a 和 b 就是实际参数,简称实参。实参就是真实传递给函数的参数。

4.2形参

        在上面的代码中,第45行定义函数时,函数 ADD 后面括号里的 x 和 y ,被称为形式参数,简称形参

        如果只是定义了 ADD 函数,而不去调用的话。x 和 y只是形式上存在的,不会向内存申请空间,不会真是存在,所以叫形式参数。形式参数只有在函数被调用的过程中,为了存放实参传递过来的值,才会向内存申请空间,这个过程就是形式的实例化

4.3 实参和形参的关系

        虽然实参是传递给形参的,但是1实参和形参各自是存放在独立的内存空间的。但是形参可以用与实参相同的变量名字。

        x 和 y 的确的到了 a 和 b 的值,但是它的地址是不一样的。所以可以理解为 形参是实参的一份临时拷贝

5.return语句

        函数中经常会出现 return 语句,这里说一下 return 语句使用的注意事项。

  • return 后边可以是一个数值,也可以是一个表达式,如果是表达式,则新执行表达式,再返回表达式的结果。
  • return 后面也可以什么都没有,直接写 return 。这种写法适合函数返回类型是 void 的情况。
  • return 返回的值和函数返回类型不一致,系统会自动将返回值隐式转换为函数的返回类型。
  • return 语句执行后,函数就彻底返回,后边的代码不再执行。
  • 如果函数中存在 if 等分子语句,则要保证每种情况下都有 return 返回,否则会出现编译错误。

6.数组做函数参数

        在使用函数解决问题的时候,难免会将数组作为参数传递给函数,在函数内部对数组进行操作。

        例如:写一个函数对将一个整型数组的内容,全部置换为1,再写一个函数打印数组的内容。

        在阅读了题目之后,就会想到要写一个,将数组内容全部置换为-1的函数,和一个打印数组的函数。这就是一个大概的框架。

int main()
{
	int arr[] = { 1,2,3,4,5,6,7,8,9,10 };	
	set_arr();		//将数组内容全部置换为-1
	printf_arr();	//打印数组内容
	return 0;
}

        想要将数组内容全部置换为-1,可以使用循环遍历数组,再通过求数组元素的个数,来得知元素的下标。这样就可以通过下标,来将每个元素进行置换。将需要使用的数组和元素个数带入到函数中。

int main()
{
	int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
	int sz = sizeof(arr)/sizeof(arr[0]);	//求元素的个数
	set_arr(arr,sz);		//将数组内容全部置换为-1
	printf_arr(arr,sz);	//打印数组内容
	return 0;
}

        使用 for 循环来遍历数组,将每个元素置换为 -1。

void set_arr(int arr[], int sz)
{
	for (int i = 0; i < sz; i++)
	{
		arr[i] = -1;
	}
}

        这样就完成了将数组元素置换为 -1。接下来再使用 for 循环,将置换后的数组打印出来。

void printf_arr(int arr[], int sz)
{
	for (int i = 0; i < sz; i++)
	{

		printf("下标为%d的元素为:%d\n",i,arr[i]);
	}
}

        最后执行出来的效果如下图所示:

7.嵌套调用和链式访问

7.1 嵌套调用

        嵌套调用就是函数之间相互调用,就犹如一个积木零件,哪里需要哪里搬。

        假设我们需要计算某年某月有多少天?

        如果要使用函数实现,可以设计两个函数:1.is_leap_year() :判断是否为闰年   2.get_day_of_month() :在调用了 is_leap_year 函数判断是否为闰年之后,再使用这个函数来计算这个月的天数。

        is_leap_year 函数在 get_day_of_month函数中调用,所以在主函数中只需要调用 get_day_of_month 函数就可以了。这样我们就可以写出这个程序的框架。

        之后在 get_day_of_month 设置一个数组存放非闰年的天数,将月份与下标相对应。之后进行分支,判断是否为闰年并且是2月,是则在天数上+1,并且返回天数,否则直接返回天数。

        代码如下:

int is_leap_year(int year)
{
	if ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0)
		return 1;
	else
		return 0;
}

int get_day_of_month(int y, int m)
{
	int days[13] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };
	int day = days[m];
	if (is_leap_year&& m == 2)
		day += 1;
	return day;

}

int main()
{
	int y = 0;
	int m = 0;
	printf("请输入年份和月份:");
	scanf("%d %d",&y,&m);
	int d = get_day_of_month(y, m);
	printf("%d年%d月的天数是:%d\n",y,m,d);
	return 0;
}

7.2 链式访问

        链式访问就是将一个函数的返回值作为另一个函数的参数。就是像链条一样将函数串一起来就是函数的链式访问。

        以下面这个为例:

int main()
{
	printf("%d",printf("%d",printf("%d",43)));
	return 0;
}

        最里层的 printf 打印了 43,次外层的 printf 打印了最内层打印的 34 的数量,也就是2;最外层的 printf 打印次外层 printf 打印的数量,也是就是1。

8.函数的声明和定义

8.1 单个文件

        一般我们在使用函数的时候,直接将函数写出来就调用了。

        比如:我们要写一个判断一年是否为闰年

         代码第130行到136行为函数的定义,第143行是函数的调用

        但是当我们把函数的定义,放到函数调用的后面,就需要在函数的定义前面加上函数的声明,否则会报错。

        声明函数只要交代清楚:函数名、函数的返回类型和函数的参数。

       函数的调用一定要满足:先定义后使用。 函数的定义也是一种特殊的声明,所以如果函数定义放在调用之前也是可以的。

8.2 多个文件

        当写的代码比较多时,不会将所有的代码都放在一个文件中;我们往往会根据程序的功能,将代码拆分放在多个文件中。

        一般情况下,函数的声明、类型的声明放在头文件中(.h)中,函数的实现是放在源文件(.c)中

        当需要调用头文件中函数和类型的声明时,要使用  #include “XXX.h” 。这里的XXX.h就是要是用的文件的名称。

8.3 static和extern

        static和extern都是C语言中的关键字。

        static是 静态的 的意思,可以用来:

  • 修饰局部变量
  • 修饰全局变量
  • 修饰函数

        extern 是用来声明外部符号的。如果一个全局的符号在A文件中定义的,想在B文件中使用,就可以使用 extern 进行声明,然后使用。

        作用域  限定这个名字的可用性的代码范围,就是这个名字的作用域。

  • 局部变量的作用域是变量所在的剧局部范围
  • 全局变量的作用域是整个工程

        生命周期  指的是变量的创建(申请内存)到变量的销毁(收回内存)之间的一个时间段。

  • 局部变量的生命周期:进入作用域,生命周期开始;出作用域,生命周期结束。
  • 全局变量的生命周期:整个程序的声明周期。

8.3.1 static 修饰局部变量

        以下面这段代码为例,这样运行出来的结果是11111.

        但是当在 test 函数中的 int i 前面加上static 之后,输出的结果是12345。

        static修饰局部变量改变了变量的生命周期,声明周期改变的本质是改变了变量的存储类型。本来一个局部变量是存储在内存的栈区的,但是被 static 修饰后存储到了静态区。

        存储在静态区的变量和全局变量是一样的,生命周期和程序的生命周期一样了。只有程序结束,变量才销毁,内存才回收。但是作用域不变

         使用建议:未来一个变量除了函数后,我们还想保留值,等下次进入函数继续使用,就可以使用 static 修饰。

8.3.2 static修饰全局变量

        一个全局变量被 static 修饰,使得这个全局变量只能在本源文件内使用,不能在其他源文件内使用。

        全局变量默认是具有外部链接属性的。在使用 static 修饰之后,就变成了内部链接属性。

        使用建议:如果一个全局变量,只想在所在源文件内部使用,不想被其他文件使用,就可以使用 static 修饰。

8.3.3 static修饰函数

        static 修饰函数和修饰全局变量的效果是一模一样的。

        函数默认是具有外部链接属性的。在使用 static 修饰之后,就变成了内部链接属性。

        使用建议:一个函数只想在所在的源文件内部使用,不想被其他源文件使用,就可以使用 static 修饰。

        

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值