一. 函数本质
1. 函数意义
(1) 函数来源
(2) 模块化程序设计
2. 面向过程的程序设计
(1) 程序结构
3. 函数的声明和定义
(1) 声明 和 定义 的区别
(2) 代码示例 ( 函数 声明 和 定义区别 )
二. 参数 可变参数 顺序点 类型缺省认定
1. 函数参数
(1) 参数分析
(2) 代码示例 ( 函数参数 求值顺序 )
2. 程序中的顺序点
(1) 顺序点简介
3. C 语言 函数 的 缺省认定
(n) 标题3
4.可变参数 的 定义 和 使用
(1) 简介
(2) 代码示例 ( 定义 使用 可变参数 )
三. 函数 与 宏
1. 函数 与 宏 对比案例
(1) 函数 和 宏 的案例
2. 函数 和 宏 的分析
(1) 函数 和 宏 分析
3. 函数 与 宏 的 利弊
(1) 宏 优势 和 弊端
(2) 函数 的 优势 和 弊端
(3) 宏的无可替代性
4. 总结
(1) 宏 定义 和 函数 总结
四. 函数的调用约定
1. 函数的活动记录 分析
(1) 函数的活动记录
2. 函数的调用约定概述
(1) 参数入栈 问题描述
(2) 参数传递顺序的调用约定
五. 函数设计技巧
一. 函数本质
1. 函数意义
(1) 函数来源
C 程序结构 由 数据 和 函数 组成;
函数是由汇编跳转发展而来的 :
1.汇编操作 : 汇编语言中由一系列的指令组成, 这些指令从上到下顺序执行,
2.跳转操作 : 汇编中需要做分支循环操作的时候, 就是使用跳转指令;
3.指令代码模块 : 在汇编中有一组指令代码, 总是需要执行这一组代码, 需要时跳转到该代码处执行, 执行完毕后在跳转回去, 这就是一个函数的雏形;
4.发展 : 跳转过来 和 跳转回去 相当于函数的 入栈 和 出栈;
(2) 模块化程序设计
模块化程序设计 :
1.思想 : 复杂问题拆解, 将一个复杂问题拆解成一个个的简单问题, 这些简单问题就可以作为一个个的函数来编写;
2.C语言程序 : 将一个复杂的程序拆解成一个个模块 和 库函数;
一个复杂的 C 语言程序有几十上百万行代码, 这些代码可以分解成若干模块来实现, 即分解成一个个的函数来实现.
2. 面向过程的程序设计
(1) 程序结构
面向过程程序设计思想 :
1.中心 : 整体的设计 以 过程 为中心;
2.问题分解 : 将复杂问题分解为若干容易解决的问题;
3.函数体现 : 面向过程 的思想在 C 语言 中的核心就是 函数;
4.分解函数 : 复杂问题 分解后的过程可以分为一个个函数一步步实现;
3. 函数的声明和定义
(1) 声明 和 定义 的区别
声明和定义的区别 :
1.声明 : 程序中 声明 只是告诉编译器 某个 实体 存在, 这个实体可以是 变量 或者 函数 等;
2.定义 : 程序中定义 指的就是 某个实体 ( 函数 或 变量 ) 的实际意义;
在 test_1.c 中定义变量 int i = 10; 这是定义了 int 类型的变量, 需要为该变量分配内存空间;
在 test_2.c 中声明变量 extern int i; 这是声明了 int 类型的变量, 变量定义在了别的文件中, 不必为该变量分配内存空间;
(2) 代码示例 ( 函数 声明 和 定义区别 )
代码示例 :
1.代码 test_1.c :
#include <stdio.h> //声明 : 声明外部变量, 该值是在其它文件中定义的 extern int global_int; //声明 : 声明函数 plus, 该函数定义在下面 int plus(int i, int j); int main() { //声明 : 声明函数 square, 如果不声明编译时会报错, 该声明只在 main 函数中有效果, 在main函数之外使用该方法就会报错 int square(int i); //使用函数 square, 如果没有声明, 编译会报错 global_int = 3; printf("%d\n", square(global_int)); //使用函数 plus, 如果没有声明编译会报错 printf("%d\n", plus(1, 2)); return 0; } //定义 : 定义函数 plus int plus(int i, int j) { return i + j; } //定义 : 定义函数 square int square(int i) { return i * i; } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
2.代码test_2.c :
//定义 : 定义变量, 在这里需要为变量分配内存空间 int global_int;
1
2
3.编译运行结果 :
二. 参数 可变参数 顺序点 类型缺省认定
1. 函数参数
(1) 参数分析
函数参数分析 :
1.本质 : 函数参数的本质 与 局部变量 基本相同, 这两种数据都存放在栈空间中 ( 中间隔着 返回地址 寄存器 EBP 数据 ) 详情参考上一篇博客内存管理 ;
2.参数值 : 函数调用的 初始值 是 函数调用时的实参值 ;
函数参数的求值顺序 (盲点) :
1.实现 : 函数参数的求值顺序 依赖 编译器的实现;
2.操作数顺序没有在规范中 : C 语言规范中没有规定函数参数必须从左到右进行计算赋值;
3.运算符编程注意点 : C语言中大多数的运算符的操作数求值顺序也是不固定的, 依赖于编译器的实现;
4.示例 : 如 int ret = fun1() * fun2(); fun1 和 fun2 函数哪个先执行, 哪个后执行 不一定;
编程时尽量不要编写的代码依赖于操作数的实现顺序;
(2) 代码示例 ( 函数参数 求值顺序 )
代码示例 :
1.代码 :
#include <stdio.h> int fun(int i, int j) { printf("%d, %d\n", i, j); } int main() { int m = 1; fun(m, m ++); printf("%d\n", i); /* 打印出来的结果是 2, 1 \n 2 分析 : 函数的参数的求值顺序 不是 从左到右的, 是不固定的 这个顺序是编译器制定的, 不同编译器该顺序不同 */ return 0; } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
2.编译运行结果 :
分析 :
函数参数计算说明 : fun(m, m ++); 进入函数体之前先计算 m 和 m++ 的值, m 和 m++ 是实参, 在计算完成之后才赋值给 i 和 j 形参;
顺序点 : 在进入函数体前是一个顺序点, 需要将计算完毕的实参 赋值给形参;
实参 m 赋值 : 赋值给 形参 i, 此处已经到达顺序点, m 自增操作已经反映到内存中, 因此 从 内存中获取的 i 的值是 2;
实参 m++ 赋值 : 赋值给 形参 j, m++ 表达式的计算结果是 1, 因此 j 的值是1;