【C 语言】C 语言 函数 详解 ( 函数本质 | 顺序点 | 可变参数 | 函数调用 | 函数活动记录 | 函数设计 ) [ C语言核心概念 ](一)

一. 函数本质

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.编译运行结果 :


【C 语言】C 语言 函数 详解 ( 函数本质 | 顺序点 | 可变参数 | 函数调用 | 函数活动记录 | 函数设计 ) [ C语言核心概念 ](一)






二. 参数 可变参数 顺序点 类型缺省认定





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.编译运行结果 :

【C 语言】C 语言 函数 详解 ( 函数本质 | 顺序点 | 可变参数 | 函数调用 | 函数活动记录 | 函数设计 ) [ C语言核心概念 ](一)

分析 :

函数参数计算说明 : fun(m, m ++); 进入函数体之前先计算 m 和 m++ 的值, m 和 m++ 是实参, 在计算完成之后才赋值给 i 和 j 形参;

顺序点 : 在进入函数体前是一个顺序点, 需要将计算完毕的实参 赋值给形参;

实参 m 赋值 : 赋值给 形参 i, 此处已经到达顺序点, m 自增操作已经反映到内存中, 因此 从 内存中获取的 i 的值是 2;

实参 m++ 赋值 : 赋值给 形参 j, m++ 表达式的计算结果是 1, 因此 j 的值是1;



上一篇:【Android 应用开发】Activity生命周期 与 Activity 之间的通信


下一篇:【Groovy】闭包 Closure ( 闭包类 Closure 简介 | this、owner、delegate 成员区别 | 静态闭包变量 | 闭包中定义闭包 )(二)