专题六-函数(C语言的核心)

来源:互联网 发布:淘宝店铺转让出售 编辑:程序博客网 时间:2024/06/14 10:38

认清函数的真面目

C语言中的函数
(1)函数的额由来
程序=数据+算法
C程序=数据+函数

函数的意义


C语言模块化



面向过程的程序设计
(1)面向过程是一种以过程为中心的编程思想
(2)首先将复杂的问题分解为一个个容易解决的问题
(3)分解过后的问题可以按照步骤一步步完成
(4)函数是面向过程在C语言中的体现
(5)解决问题的每个步骤可以用函数来实现


函数声明和定义
(1)程序中的声明可以理解为预先告诉编译器实体的存在,如:变量,函数,等等
(2)程序中的定义明确指示编译器实体的意义
声明和定义并不相同。



函数参数
(1)函数参数在本质上与局部变量相同,都是在栈上分配空间
(2)函数参数的初始值是函数调用时的实参值









为什么?

(1)函数参数的求值顺序依赖于编译器的实现!!!
(2)C语言中大多数运算符对其操作数求值的顺序都是依赖于编译器的实现。

int i = f()*g()     这个例子中f先被调用还是g先被调用,不一定!!!!!



程序中的顺序点
(1)程序中存在一定的顺序点
(2)顺序点指的是执行过程中修改变量值的最晚时刻
(3)在程序到达顺序点的时候,之前所做的一切操作必须反映到后续的访问中






++k是立马生效
k++和++k完全不同的,k++会延续到顺序点。


前面那个例子也是顺序点,不是编译器。



函数的缺省认定
(1)C语言会默认没有类型的函数参数为int







可变参数列表

问题:如何计算n个值的平均数?

那么有没有什么办法不使用数组就求出n个数的平均值呢?

可变参数
(1)C语言中可以定义参数可变的函数
(2)参数可变函数的实现依赖于stdarg.h头文件
(3)va_list变量与va_start,va_end和va_arg配合使用能够访问参数值

实例分析:可变参数的定义和使用

n是n个数,后面是具体数

va_list args;代表可变参数列表
va_start(args,n);初始化参数列表,使得这个表 被下面取值
va_arg(args,int);去参数列表里面取值


可变参数的限制
(1)可变参数必须从头到尾按照顺序逐个访问;
(2)参数列表中至少存在一个确定的命名参数;
(3)可变参数宏无法判断实际存在的参数的数量;
(4)可变参数宏无法判断参数的实际类型。
va_arg中如果指定了错误的类型,那么结果是不可预测的。


小结
(1)可变参数是C语言提供的一种函数设计技巧;
(2)可变参数的函数提供了一种更方便的函数调用方式;
(3)可变参数必须顺序访问;
(4)无法直接访问可变参数列表中的参数值。






李逵和李鬼





函数VS宏
(1)宏是由预处理直接替换展开的,编译器不知道宏的存在
(2)函数是由编译器直接编译的实体,调用行为由编译器实现
(3)多次使用宏会导致程序代码量的增加
(4)函数是跳转执行的,因此代码量不会增加
(5)宏的效率比函数要高,因为是直接展开的,无调用开销
(6)函数调用时会创建活动记录,效率不如宏


宏的优缺点
(1)宏的效率比函数高,但是其副作用巨大,容易出错

(2)函数存在实参到形参的传递,因此无任何副作用,但是函数需要建立活动对象,效率受影响。



宏无可替代的优势
(1)宏参数可以是任何C语言实体:宏编写的_MIN_参数类型可以是int,float等等。
(2)宏的参数可以是类型名。




小结
(1)宏和函数不是竞争对手;
(2)宏能够接受任何类型的参数,效率高,易出错;
(3)函数的参数必须是固定类型,效率稍低,不易出错;
(4)宏可以实现函数不能实现的功能。







函数的调用行为

活动记录
1.活动记录是函数调用时用于记录一系列相关信息的记录
(1)临时变量域;用来存放临时变量值,如k++的中间结果
(2)局部变量域:用来存放函数本次执行中的局部变量
(3)机器状态域:用来保存调用函数之前有关机器状态的信息,包括各种寄存器的当前值和返回地址等
(4)实参数域:用于存放函数的实参信息
(5)返回值域:为调用者函数存放返回值


参数入栈
既然函数参数的计算次序是依赖编译器实现的,那么函数参数的入栈次序是如何确定的呢?


调用约定
(1)当一个函数被调用时,参数会传递给被调用的函数,而返回值会被返回给调用函数。函数的调用约定就是描述参数是怎么传递到栈空间的,以及栈空间由谁维护。
(2)参数传递顺序
从右到左一次入栈:__stdcall,__cdecl.__thiscall
从左到右依次入栈:__pascal,__fastcall
(3)调用堆栈清理
调用者清除栈
被调用函数返回后清除栈

参数按什么顺序进栈可以约定


小结
(1)函数调用是C语言的核心机制;
(2)活动记录中保存了函数调用以及返回所需要的一切信息;
(3)调用约定是调用者和被调用者之间的调用协议,常用于不同开发者编写的库函数之间。




函数递归详解

递归概述
(1)递归是数学领域中概念在程序设计中的应用;
(2)递归是一种强有力的程序设计方法;
(3)递归的本质是函数内部在适当的时候调用自身。



小结
(1)C语言中的递归函数必然会使用判断语句
(2)递归函数在需要编写的时候定义函数的出口,否则 栈 会溢出
(3)递归函数是一种分而治之的思想





函数设计技巧

(1)不要在函数中使用全局变量,尽量让函数从意义上是一个独立的功能模块;
(2)参数名要能够体现参数的意义。

(3)如果参数是指针,且仅作为输入参数来使用,则应在类型前加const,以防止该指针在函数体内被意外修改。


(4)不要省略返回值类型,如果函数没有返回值,那么应该声明为void类型。
(5)在函数体的入口地方对参数的有效性进行检查,对指针的检查尤为重要。
(6)语句不可返回指向“栈内存”的“指针”,因为该内存在函数体结束时被自动销毁。
(7)函数体的规模要小,尽量控制在80行代码之内。
(8)相同的输入应当产生相同的输出,尽量避免函数带有“记忆”功能。
(9)避免函数有太多的参数,参数尽量控制在4个以内。

getchar的返回值是int



0 0
原创粉丝点击