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

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


image.png






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





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

image.png

分析 :

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

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

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

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



目录
相关文章
|
4天前
|
C语言
c语言调用的函数的声明
被调用的函数的声明: 一个函数调用另一个函数需具备的条件: 首先被调用的函数必须是已经存在的函数,即头文件中存在或已经定义过; 如果使用库函数,一般应该在本文件开头用#include命令将调用有关库函数时在所需要用到的信息“包含”到本文件中。.h文件是头文件所用的后缀。 如果使用用户自己定义的函数,而且该函数与使用它的函数在同一个文件中,一般还应该在主调函数中对被调用的函数做声明。 如果被调用的函数定义出现在主调函数之前可以不必声明。 如果已在所有函数定义之前,在函数的外部已做了函数声明,则在各个主调函数中不必多所调用的函数在做声明
19 6
|
17天前
|
存储 算法 程序员
C语言:库函数
C语言的库函数是预定义的函数,用于执行常见的编程任务,如输入输出、字符串处理、数学运算等。使用库函数可以简化编程工作,提高开发效率。C标准库提供了丰富的函数,满足各种需求。
|
23天前
|
机器学习/深度学习 C语言
【c语言】一篇文章搞懂函数递归
本文详细介绍了函数递归的概念、思想及其限制条件,并通过求阶乘、打印整数每一位和求斐波那契数等实例,展示了递归的应用。递归的核心在于将大问题分解为小问题,但需注意递归可能导致效率低下和栈溢出的问题。文章最后总结了递归的优缺点,提醒读者在实际编程中合理使用递归。
53 7
|
23天前
|
存储 编译器 程序员
【c语言】函数
本文介绍了C语言中函数的基本概念,包括库函数和自定义函数的定义、使用及示例。库函数如`printf`和`scanf`,通过包含相应的头文件即可使用。自定义函数需指定返回类型、函数名、形式参数等。文中还探讨了函数的调用、形参与实参的区别、return语句的用法、函数嵌套调用、链式访问以及static关键字对变量和函数的影响,强调了static如何改变变量的生命周期和作用域,以及函数的可见性。
29 4
|
20天前
|
存储 C语言
【c语言】字符串函数和内存函数
本文介绍了C语言中常用的字符串函数和内存函数,包括`strlen`、`strcpy`、`strcat`、`strcmp`、`strstr`、`strncpy`、`strncat`、`strncmp`、`strtok`、`memcpy`、`memmove`和`memset`等函数的使用方法及模拟实现。文章详细讲解了每个函数的功能、参数、返回值,并提供了具体的代码示例,帮助读者更好地理解和掌握这些函数的应用。
18 0
|
20天前
|
C语言
【c语言】qsort函数及泛型冒泡排序的模拟实现
本文介绍了C语言中的`qsort`函数及其背后的回调函数概念。`qsort`函数用于对任意类型的数据进行排序,其核心在于通过函数指针调用用户自定义的比较函数。文章还详细讲解了如何实现一个泛型冒泡排序,包括比较函数、交换函数和排序函数的编写,并展示了完整的代码示例。最后,通过实际运行验证了排序的正确性,展示了泛型编程的优势。
18 0
|
23天前
|
算法 C语言
factorial函数c语言
C语言中实现阶乘函数提供了直接循环和递归两种思路,各有优劣。循环实现更适用于大规模数值,避免了栈溢出风险;而递归实现则在代码简洁度上占优,但需警惕深度递归带来的潜在问题。在实际开发中,根据具体需求与环境选择合适的实现方式至关重要。
21 0
|
C语言 Java C++
C语言为何不会过时?你需要掌握多少种语言?
云栖号资讯:【点击查看更多行业资讯】在这里您可以找到不同行业的第一手的上云资讯,还在等什么,快来! 【云栖号在线课堂】每天都有产品技术专家分享!课程地址:https://yqh.aliyun.com/live 立即加入社群,与专家面对面,及时了解课程最新动态!【云栖号在线课堂 社群】https://c.
C语言为何不会过时?你需要掌握多少种语言?
|
1月前
|
C语言 C++
C语言 之 内存函数
C语言 之 内存函数
32 3
|
24天前
|
存储 缓存 C语言
【c语言】简单的算术操作符、输入输出函数
本文介绍了C语言中的算术操作符、赋值操作符、单目操作符以及输入输出函数 `printf` 和 `scanf` 的基本用法。算术操作符包括加、减、乘、除和求余,其中除法和求余运算有特殊规则。赋值操作符用于给变量赋值,并支持复合赋值。单目操作符包括自增自减、正负号和强制类型转换。输入输出函数 `printf` 和 `scanf` 用于格式化输入和输出,支持多种占位符和格式控制。通过示例代码详细解释了这些操作符和函数的使用方法。
33 10