函数空间
1、函数的真面目
程序 = 数据 + 算法 ==》C程序 = 数据 + 函数
模块化程序设计:
C语言中的模块化:
面向过程的程序设计
面向过程是一种以过程为中心的编程思想 首先将复杂的问题分解为一个个容易解决的问题 分解过后的问题可以按照步骤一步步完成 函数是面向过程在C语言中的体现 解决问题的每个步骤可以用函数来实现 |
声明和定义:
程序中的声明可理解为预先告诉编译器实体的存在,如:变量,函数,等等 程序中的定义明确指示编译器实体的意义 |
函数参数
函数参数在本质上与局部变量相同,都是在栈上分配空间 函数参数的初始值是函数调用时的实参值 函数参数的求值顺序依赖于编译器的实现! C语言中大多数运算符对其操作数求值的顺序都是依赖于编译器的实现的 |
程序中的顺序点
程序中存在一定的顺序点 顺序点指的是执行过程中修改变量值的最晚时刻 在程序达到顺序点的时候,之前所做的一切操作必须反映到后续的访问中 每个完整表达式结束时 |
函数的缺省认定
C语言会默认没有类型的函数参数为int |
2、可变参数分析与宏分析
可变参数
C语言中可以定义参数可变的函数 参数可变函数的实现依赖于stdarg.h头文件 va_list变量与va_start, va_end和va_arg配合使用能够访问参数值 |
可变参数的限制
可变参数必须从头到尾按照顺序逐个访问 参数列表中至少要存在一个确定的命名参数 可变参数宏无法判断实际存在的参数的数量 可变参数宏无法判断参数的实际类型 警告: va_arg中如果指定了错误的类型,那么结果是不可预测的 |
函数和宏的对比
宏是由预处理直接替换展开的,编译器不知道宏的存在 函数是由编译器直接编译的实体,调用行为由编译器决定 多次使用宏会导致程序代码量增加 函数是跳转执行的,因此代码量不会增加 宏的效率比函数要高,因为是直接展开,无调用开销 函数调用时会创建活动记录,效率不如宏 |
宏的优点和缺点
宏的效率比函数稍高,但是其副作用巨大,容易出错 函数存在实参到形参的传递,因此无任何副作用,但是函数需要建立活动对象,效率受影响 宏参数可以是任何C语言实体 |
3、函数调用行为
活动记录
活动记录是函数调用时用于记录一系列相关信息的记录 临时变量域:用来存放临时变量的值,如k++的中间结果 局部变量域:用来存放函数本次执行中的局部变量 机器状态域:用来保存调用函数之前有关机器状态的信息,包括各种寄存器的当前值和返回地址等; 实参数域: 用于存放函数的实参信息 返回值域: 为调用者函数存放返回值 |
参数入栈
既然函数参数的计算次序是依赖编译器实现的,那么函数参数的入栈次序是如何确定的呢? |
调用约定
当一个函数被调用时,参数会传递给被调用的函数,而返回值会被返回给调用函数。函数的调用约定就是描述参数是怎么传递到栈空间的,以及栈空间由谁维护。 参数传递顺序 从左到右依次入栈:__pascal,__fastcall 调用堆栈清理 调用者清除栈。 被调用函数返回后清除栈 |
4、函数递归与函数设计技巧
递归概述
递归是数学领域中概念在程序设计中的应用 递归是一种强有力的程序设计方法 递归的本质为函数内部在适当的时候调用自身 C递归函数有两个主要的组成部分: |
函数设计技巧
不要在函数中使用全局变量,尽量让函数从意义上是一个独立的功能模块 参数名要能够体现参数的意义 void str_copy (char *str1, char *str2); void str_copy (char *str_dest, char *str_src); 如果参数是指针,且仅作输入参数用,则应在类型前加const,以防止该指针在函数体内被意外修改 void str_copy (char *str_dest, const char *str_src); 不要省略返回值的类型,如果函数没有返回值,那么应声明为void类型 在函数体的“入口处”,对参数的有效性进行检查,对指针的检查尤为重要 函数体的规模要小,尽量控制在80行代码之内 有时候函数不需要返回值,但为了增加灵活性,如支持链式表达,可以附加返回值 函数名与返回值类型在语义上不可冲突 char c; if(EOF == c) } |
转载于:https://blog.51cto.com/yinsuifeng/2328020