【C语言初学总结】-- C语言函数

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/L19002S/article/details/102571395

【C语言初学总结】 C语言函数

函数

1.函数的定义

函数定义就是函数体的实现,函数体就是一个代码块,他在函数调用执行时,与函数定义相反,函数声明出现在函数被调用的地方。

1.1函数注意点:
每条语句后边必须要有;号(条件语句等特殊情况除外)
C语言中本身带了很多库函数,并分门类的放在了不同的头文件中,使用时只要引用对应正确的头文件即可。
除了C语言自带函数之外,我们也可以编写自己的函数,称之为自定义函数(User-Defined-Function).自定义函数和库函数没有本质的区别,表现形式和使用方法一样,只是开发者不同。

1.2函数定义基本的语法:
类型 函数名(形式参数)
代码块:代码块就是一对花括号,里面包含了一些声明和语句,因此最简单的函数大致如下:
例:

//定义一个最简单函数
function_name()
{

}

当这个函数被调用时,他简单的返回,然而,它可以实现一种有用的存根目的,给那些尚未是实现功能的的代码保留位置。

形式参数列表包括变量名和他们的类型声明。代码块包含了局部变量的声明和函数调用时需要执行的语句。

return 语句
当执行流执行到函数定义的末尾的时候,函数将返回(return),换句话说,执行流返回到函数被调用的地方。return语句允许你从函数体的任何为位置返回,语句如下:

//函数返回
return expression;

表达式expression是可选的,如果函数无需返回值expression就可以被忽略。这种在没有返回值的函数中在声明的时候应该把声明类型声明为void

在C语言中,子程序无论是否存在返回值,均被称之为函数,调用一个函数(即返回一个值的函数),从表达式调用一个过程类型函数(无返回值)是一种严重的错误,因为这样表达式的求值中会产生一个不可预测的值。

2.函数的声明

2.1原型

向编译器提供一些关于函数的特定信息更为安全,两种方法实现,

首先,如果同一源文件的前面已经出现了该函数的定义,编译器就会记住它的参数数量和类型,以及函数的返回值类型,接着编译器就可以该函数的所有后续调用(在同一个源文件),确保它们是正确的。

第二种向编译器提供函数信息的是方法是使用函数原型,函数原型总结了函数定义起始部分的声明,向编译器提供有关该函数应该如何调用完整的信息。
函数原型例子如下:

int* Find_int(int num1, int array[], int num2)

注意最后的分号,它区分了函数原型和函数定义的起始部分。编译器见过原型之后,就可以检查该函数的调用,确保参数正确,返回值无误。

从几个方面看,这个技巧更好:

1.现在函数原型具有文件作用域,所以原型的一份拷贝可以作用于整个源文件,较之在该函数每次调用前单独书写一份函数原型容易的多。

2.现在函数原型只书写一次

3.如果函数的定义进行了修改,只需要修改函数原型并且重新编译所有包含了该函数的原型的源文件即可。

4.如果函数的原型同时也被#include指令包含到定义函数的文件中,编译器就可以确认函数原型与函数定义的匹配。

一个没有参数的函数的原型应该下面这样:

int* func(void)

2.2函数的缺省认定

当程序调用一个无法见到原型的函数时,编译器就认为该函数返回一个整型值,这个可能引起错误,因为有些函数并不返回整型值。

所以所有的函数都应该具有原型,尤其是那些返回值不是整型的函数。

3.函数的参数

C函数所有参数均以“传值调用”方式进行传递,这意味函数将获得参数值的一份拷贝。

C的规则很简单:所有参数都是传值调用,但是如果被传递的参数是一个数组名,并且在函数中使用下标引用该数组的参数,那么在函数中对数组元素的修改实际上修改的是调用程序中的数组元素。函数将访问调用程序的数组元素,数组并不会复制,这个行为称为“传址调用”。
数组名的值实际上是一个指针,传递给函数的是这个指针的一份拷贝。下标引用实际上是间接访问的另一种形式,它可以对指针指向间接访问操作,访问指针指向的内存位置。

所以:
1.传递给函数的标量参数是传值调用的

2.传递给函数的数组在行为上就像它们是通过传址调用那样。

4.ADT和黑盒

C可以用于设计和实现抽象数据类型,因为它可以限制函数和数据定义的作用域,这个技巧也可以被称为黑盒。抽象数据类型的基本想法很简单——模块具有功能说明和接口说明,前者说明模块所执行的任务,后者定义模块的使用,模块的用户并不需要知道模块实现的任何细节。

#5.递归

C通过运行时堆栈支持递归函数的实现。递归函数就是直接或间接调用自身的函数。

//递归求斐波那契数
#include<stdio.h>
#include<stdlib.h>
 
//递归实现求第n个斐波那契数。 
//斐波那契数是1,1,2,3,5...
//规律:前两两项之和等于后一项即n = (n - 1) + (n - 2)
 
int FeiBo(int n)
{
	if (n == 1 || n == 2)
	{
		return 1;
	}
	return FeiBo(n - 1)+FeiBo(n - 2);
}
int main()
{
	printf("%d\n", FeiBo(5));
	system("pause");
	return 0;
}

遇到递归问题需要根据规律解决。

1、找到所谓的终止条件,即让递归停止的条件

2、找到递推的关系式

3、递归的方向要搞清楚,一般是向最终的终止条件不断递归

4、递归 分为回推和递推两个阶段,当递推到终止条件时,程序会反向推回(回推)。

6.可变参数列表

在函数原型中,列出了函数期望接受的函数,但是原型只能显示固定数目的参数,所以就出现了可变参数

6.1stdarg宏

可变参数列表是通过宏来实现的,这些宏定义于stdarg.h头文件,它是标准库的一部分,这个头文件声明了一个类型va_list三个宏——va_start、va_arg、va_end。我们可以声明一个类型为va_list的变量,配合使用

为了访问参数,需要使用va_arg,这个宏接受两个参数:va_list变量和参数列表中下一个参数的类型,在这个例子中,所有的可变参数都是整型,在有些函数中,可能需要通过前面获得的数据来判断下一个参数的类型,va_arg返回这个参数的值,并使用va_arg指向下一个可变参数。
最后,当访问完毕最后一个可变参数列表之后,我们需要调用va_end。

6.2可变参数的限制

注意,可变参数必须从头到尾按照顺序逐个访问,如果访问了几个参数之后不想访问了可以不再访问,但是如果一开始就访问参数列表中间的参数那是不行的。而且所有作为可变参数传递给函数的值都 将执行缺省参数类型提升。

//计算指定数量的平均值

//宏定义求交换a和b
#define swap(a,b) {a = a + b; b = a - b; a = a - b;}

这是我的第一篇博客,虽然在大一刚来就已经学习了C语言,但现在对很多基础的名词解释甚至语法都不太清楚了,想通过这样的方式,以自己的理解和平时遇到的情况加上查阅资料经行分析和梳理,如果有说的不对的地方,请各位大佬多多指导。

猜你喜欢

转载自blog.csdn.net/L19002S/article/details/102571395