【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;



目录
相关文章
|
3月前
|
存储 C语言
`scanf`是C语言中用于按格式读取标准输入的函数
`scanf`是C语言中用于按格式读取标准输入的函数,通过格式字符串解析输入并存入指定变量。需注意输入格式严格匹配,并建议检查返回值以确保读取成功,提升程序健壮性。
985 0
|
4月前
|
存储 编译器 程序员
C语言常见概念
C语言是一门基础的编程语言,通过编译器将源代码转换为计算机可执行的二进制程序。本文介绍了C语言的基本概念,包括其作为人与计算机交流的工具、编译与链接的过程、常用编译器的选择(如VS2022)、main函数的作用、库函数与关键字、字符与ASCII编码、字符串与转义字符等内容。同时,还讲解了如何在VS2022中创建C语言项目、编写第一个程序,以及常见的语法错误和调试方法。适合初学者了解C语言核心概念与开发环境搭建。
271 1
|
5月前
|
安全 C语言
C语言中的字符、字符串及内存操作函数详细讲解
通过这些函数的正确使用,可以有效管理字符串和内存操作,它们是C语言编程中不可或缺的工具。
317 15
|
10月前
|
人工智能 Java 程序员
一文彻底搞清楚C语言的函数
本文介绍C语言函数:函数是程序模块化的工具,由函数头和函数体组成,涵盖定义、调用、参数传递及声明等内容。值传递确保实参不受影响,函数声明增强代码可读性。君志所向,一往无前!
376 1
一文彻底搞清楚C语言的函数
|
10月前
|
监控 Linux PHP
【02】客户端服务端C语言-go语言-web端PHP语言整合内容发布-优雅草网络设备监控系统-2月12日优雅草简化Centos stream8安装zabbix7教程-本搭建教程非docker搭建教程-优雅草solution
【02】客户端服务端C语言-go语言-web端PHP语言整合内容发布-优雅草网络设备监控系统-2月12日优雅草简化Centos stream8安装zabbix7教程-本搭建教程非docker搭建教程-优雅草solution
321 20
|
11月前
|
存储 C语言
【C语言程序设计——函数】递归求斐波那契数列的前n项(头歌实践教学平台习题)【合集】
本关任务是编写递归函数求斐波那契数列的前n项。主要内容包括: 1. **递归的概念**:递归是一种函数直接或间接调用自身的编程技巧,通过“俄罗斯套娃”的方式解决问题。 2. **边界条件的确定**:边界条件是递归停止的条件,确保递归不会无限进行。例如,计算阶乘时,当n为0或1时返回1。 3. **循环控制与跳转语句**:介绍`for`、`while`循环及`break`、`continue`语句的使用方法。 编程要求是在右侧编辑器Begin--End之间补充代码,测试输入分别为3和5,预期输出为斐波那契数列的前几项。通关代码已给出,需确保正确实现递归逻辑并处理好边界条件,以避免栈溢出或结果
562 16
|
11月前
|
存储 编译器 C语言
【C语言程序设计——函数】分数数列求和2(头歌实践教学平台习题)【合集】
函数首部:按照 C 语言语法,函数的定义首部表明这是一个自定义函数,函数名为fun,它接收一个整型参数n,用于指定要求阶乘的那个数,并且函数的返回值类型为float(在实际中如果阶乘结果数值较大,用float可能会有精度损失,也可以考虑使用double等更合适的数据类型,这里以float为例)。例如:// 函数体代码将放在这里函数体内部变量定义:在函数体中,首先需要定义一些变量来辅助完成阶乘的计算。比如需要定义一个变量(通常为float或double类型,这里假设用float。
441 3
|
11月前
|
存储 算法 安全
【C语言程序设计——函数】分数数列求和1(头歌实践教学平台习题)【合集】
if 语句是最基础的形式,当条件为真时执行其内部的语句块;switch 语句则适用于针对一个表达式的多个固定值进行判断,根据表达式的值与各个 case 后的常量值匹配情况,执行相应 case 分支下的语句,直到遇到 break 语句跳出 switch 结构,若没有匹配值则执行 default 分支(可选)。例如,在判断一个数是否大于 10 的场景中,条件表达式为 “num> 10”,这里的 “num” 是程序中的变量,通过比较其值与 10 的大小关系来确定条件的真假。常量的值必须是唯一的,且在同一个。
353 2
|
10月前
|
监控 关系型数据库 MySQL
【01】客户端服务端C语言-go语言-web端PHP语言整合内容发布-优雅草网络设备监控系统-硬件设备实时监控系统运营版发布-本产品基于企业级开源项目Zabbix深度二开-分步骤实现预计10篇合集-自营版
【01】客户端服务端C语言-go语言-web端PHP语言整合内容发布-优雅草网络设备监控系统-硬件设备实时监控系统运营版发布-本产品基于企业级开源项目Zabbix深度二开-分步骤实现预计10篇合集-自营版
291 0
|
11月前
|
存储 算法 C语言
【C语言程序设计——函数】素数判定(头歌实践教学平台习题)【合集】
本内容介绍了编写一个判断素数的子函数的任务,涵盖循环控制与跳转语句、算术运算符(%)、以及素数的概念。任务要求在主函数中输入整数并输出是否为素数的信息。相关知识包括 `for` 和 `while` 循环、`break` 和 `continue` 语句、取余运算符 `%` 的使用及素数定义、分布规律和应用场景。编程要求根据提示补充代码,测试说明提供了输入输出示例,最后给出通关代码和测试结果。 任务核心:编写判断素数的子函数并在主函数中调用,涉及循环结构和条件判断。
589 23