【重拾C语言】九、再论函数(指针、数组、结构体作参数;函数值返回指针、结构体;作用域)
目录
前言
九、再论函数
9.1 参数
9.1.1 参数的传递规则
9.1.2 指针作参数
9.1.3 数组作参数
9.1.4 结构体作参数
a. 直接用结构体变量作函数参数
b. 用指向结构体变量的指针作函数参数
9.2 函数值
9.2.1 返回指针值
9.2.2 返回结构体值
a. 返回结构体值
b. 返回结构体指针
9.3 作用域
9.3.1 局部量和全局量
9.3.2 作用域
a. 文件作用域(全局作用域)
b. 函数作用域
c. 块作用域
d. 函数原型作用域
e. 代码示例
前言
【重拾C语言】五、模块化程序设计——函数(定义、调用、参数传递、结果返回、函数原型;典例:打印字符图形、验证哥德巴赫猜想)_QomolangmaH的博客-CSDN博客https://blog.csdn.net/m0_63834988/article/details/133580009?spm=1001.2014.3001.5501 前文介绍了函数的基础知识,包括如何定义函数、函数的调用形式和过程、函数结果的返回以及函数原型的使用等。本文将再论函数,主要介绍指针、数组、结构体等作参数;函数值返回指针、结构体,以及C语言作用域相关知识。
九、再论函数
9.1 参数
9.1.1 参数的传递规则
C语言只有一种参数类别——值参。值参意味着:
- 参数要求:赋值兼容
- 形实结合:
- 计算实参表达式的值;
- 把实参值按赋值转换规则,转换成形参的类型;
- 把转换后的实参值送入形参变量中。
- 执行函数:
- 值参表示形参本身,它是函数内的一个局部变量,已经与实参脱离关系(无关)了。在函数内对形参的赋值不影响实参。
- 结束返回: 实参值无任何变化,还是调用函数之前的值。
#include <stdio.h>void square(int num) {num = num * num; // 修改形参的值printf("Inside the function: %d\n", num);
}int main() {int number = 5;printf("Before the function call: %d\n", number);square(number); // 调用函数并传递实参printf("After the function call: %d\n", number);return 0;
}
9.1.2 指针作参数
一般意义上,如果函数的形参是指针类型,对应调用时,相应实参也应是指针类型表达式
#include <stdio.h>void square(int *num) {*num = (*num) * (*num); // 修改形参指针所指向的值printf("Inside the function: %d\n", *num);
}int main() {int number = 5;printf("Before the function call: %d\n", number);square(&number); // 调用函数并传递实参的地址printf("After the function call: %d\n", number);return 0;
}
输出:
使用指针作为形参来传递实参的地址。在函数内部,通过解引用指针并修改指针所指向的值,实现了对实参的修改。
9.1.3 数组作参数
在C语言中,数组名实际上是一个指针,表示数组首元素的地址。因此,当将数组名作为实参传递给函数时,实际上传递的是数组名的指针值。
在函数调用时,数组名作为实参传递给函数的形参,只传递了数组名的值,也就是数组的首地址。函数内部并不会为形参开辟数组的存储空间,而只会为形参开辟一个指针变量的空间,用于存储传递进来的实参数组的地址。
#include <stdio.h>void printArraySize(int arr[]) {printf("Size of the array inside the function: %lu\n", sizeof(arr));for (int i = 0; i < 5; i++) {printf("%d ", arr[i]);}printf("\n");
}int main() {int array[] = {1, 2, 3, 4, 5};printf("Size of the array in the main function: %lu\n", sizeof(array));printArraySize(array);return 0;
}
输出:
将数组array
作为实参传递给printArraySize
函数,可以看到,在printArraySize
函数内部,sizeof(arr)
的结果是8(64位系统上),而不是数组的实际大小。这是因为在函数调用过程中,只传递了数组名的指针值,而不是整个数组的值。
如上述代码所示,数组作为形参时,可以省略数组形式参数最外层的尺寸
错误示例:
void printMatrix(int matrix[][], int rows)
void printMatrix(int matrix[3][], int rows)
void printMatrix(int matrix[3][][3], int rows)
正确示例:
#include <stdio.h>void printMatrix(int matrix[][3], int rows) {for (int i = 0; i < rows; i++) {for (int j = 0; j < 3; j++) {printf("%d ", matrix[i][j]);}printf("\n");}
}int main() {int matrix[][3] = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};int rows = sizeof(matrix) / sizeof(matrix[0]);printf("Matrix elements in the main function:\n");printMatrix(matrix, rows);return 0;
}
9.1.4 结构体作参数
a. 直接用结构体变量作函数参数
#include <stdio.h>struct Point {int x;int y;
};void printPoint(struct Point p) {printf("Point coordinates: (%d, %d)\n", p.x, p.y);
}int main() {struct Point point = {3, 5};printf("Point coordinates in the main function:\n");printPoint(point);return 0;
}
Point
结构体,包含两个整型成员变量x
和y
。printPoint
函数,接收一个Point
类型的结构体作为参数,并在函数内部打印结构体的坐标值。main
函数中,创建一个名为point
的Point
结构体变量,并初始化其x
和y
成员变量的值。然后,调用printPoint
函数,将point
作为参数传递给它。- 输出:
b. 用指向结构体变量的指针作函数参数
#include <stdio.h>struct Point {int x;int y;
};void printPoint(struct Point* p) {printf("Point coordinates: (%d, %d)\n", p->x, p->y);
}int main() {struct Point point = {3, 5};printf("Point coordinates in the main function:\n");printPoint(&point);return 0;
}
printPoint
函数,接收一个指向Point
类型结构体的指针作为参数- 在
main
函数中,调用printPoint
函数,将&point
(point
的地址)作为参数传递给它 - 输出结果与方法a相同:
9.2 函数值
9.2.1 返回指针值
函数可以返回指针作为其返回值,这样可以在函数外部访问函数内部创建的变量或数据。
函数返回类型不允许是数组类型和函数类型、共用体类型,除此之外允许一切类型, 当然允许指针类型,带回指针值的函数的函数定义说明符形式是:
类型名 *函数名( 形参列表 )
#include <stdio.h>int* getArray() {static int arr[] = {1, 2, 3, 4, 5};return arr;
}int main() {int* ptr = getArray();for (int i = 0; i < 5; i++) {printf("%d ", *(ptr + i));}return 0;
}
输出:
9.2.2 返回结构体值
函数的计算结果可能是一个结构体值。在C语言中,有两种途径能够把该结构体值通过函数名字带回到主调函数。
a. 返回结构体值
- 函数的结果类型是结构体类型
- 直接把一个结构体值带回调用函数的主程序
#include <stdio.h>struct Point {int x;int y;
};struct Point createPoint(int x, int y) {struct Point point;point.x = x;point.y = y;return point;
}int main() {struct Point p = createPoint(3, 4);printf("Point coordinates: (%d, %d)\n", p.x, p.y);return 0;
}
b. 返回结构体指针
- 函数的结果类型是指向结构体类型变量的指针类型
#include <stdio.h>
#include <stdlib.h>struct Point {int x;int y;
};struct Point* createPoint(int x, int y) {struct Point* point = malloc(sizeof(struct Point));point->x = x;point->y = y;return point;
}int main() {struct Point* p = createPoint(3, 4);printf("Point coordinates: (%d, %d)\n", p->x, p->y);free(p);return 0;
}
- 函数
createPoint()
接受两个参数,并动态分配内存来创建一个Point
类型的结构体变量。然后,它将给定的坐标值分配给结构体的成员,并返回指向该结构体的指针。 - 在
main()
函数中,调用createPoint()
函数来创建一个Point
结构体,并使用指针访问结构体的成员来打印坐标值。最后,使用free()
函数释放了动态分配的内存,以避免内存泄漏。
9.3 作用域
9.3.1 局部量和全局量
- 局部变量(Local Variables)是在函数内定义的变量,包括形参。它们的作用范围限定在所属的函数内部。另外,定义在复合语句内部的变量的作用范围则限定在该复合语句内部。
- 全局变量(Global Variables)则是在函数以外定义的变量,它们不从属于任何特定的函数。全局变量的作用范围从定义处开始,一直延伸到整个源文件的结束,包括各个函数。
#include <stdio.h>// 全局变量
int globalVariable = 10;void myFunction()
{// 局部变量int localVar = 20;printf("局部变量:%d\n", localVar);printf("全局变量:%d\n", globalVariable);
}int main()
{myFunction();// 尝试访问局部变量和全局变量// 这里不能访问局部变量localVar,会导致编译错误printf("全局变量:%d\n", globalVariable);return 0;
}
9.3.2 作用域
作用域是指在程序中标识符有效的区域。在C语言中,每个源程序编译单位(例如源文件),每个函数定义、函数原型以及复合语句都构成一个作用域区域。在一个标识符的作用域内,可以使用该标识符,并且使用的是相应声明的标识符。这意味着在不同的作用域中可以使用相同名称的标识符,因为它们处于不同的作用域,互相之间不会产生冲突。
a. 文件作用域(全局作用域)
在函数之外定义的标识符具有文件作用域,它们在整个源文件中可见,在文件中的任何位置都可以使用这些标识符。
b. 函数作用域
在函数内部定义的标识符具有函数作用域,它们只在函数内部可见,在函数外部无法使用这些标识符。
c. 块作用域
在复合语句(代码块)内部定义的标识符具有块作用域,它们只在该代码块内可见。当代码块执行完毕后,其中定义的标识符就不再可见。
d. 函数原型作用域
函数原型中声明的标识符具有函数原型作用域,它们只在函数原型所在的作用域中可见。函数原型作用域主要用于函数声明中的参数。
e. 代码示例
#include <stdio.h>// 文件作用域(全局作用域)
int globalVariable = 10;void function1()
{// 函数作用域int localVariable = 20;printf("Local variable: %d\n", localVariable);printf("Global variable: %d\n", globalVariable);
}void function2()
{// 函数作用域int localVariable = 30;printf("Local variable: %d\n", localVariable);printf("Global variable: %d\n", globalVariable);
}int main()
{// 块作用域{int blockVariable = 40;printf("Block variable: %d\n", blockVariable);}function1();function2();return 0;
}
相关文章:

【重拾C语言】九、再论函数(指针、数组、结构体作参数;函数值返回指针、结构体;作用域)
目录 前言 九、再论函数 9.1 参数 9.1.1 参数的传递规则 9.1.2 指针作参数 9.1.3 数组作参数 9.1.4 结构体作参数 a. 直接用结构体变量作函数参数 b. 用指向结构体变量的指针作函数参数 9.2 函数值 9.2.1 返回指针值 9.2.2 返回结构体值 a. 返回结构体值 b. 返回…...

Spring WebClient 基于响应式编程模型的HTTP客户端
一、简介 WebClient是一个非阻塞的、可扩展的、基于Reactive Streams规范的HTTP客户端。它提供了一种简洁的方式来进行HTTP请求,并且可以很好地与其他Spring组件集成。WebClient支持同步和异步操作,使得它非常适合用于构建响应式应用程序。 WebClient允…...

IP真人识别方法与代理IP检测技术
随着互联网的发展,IP地址在网络安全和数据分析中扮演着重要的角色。为了维护网络的安全性和识别真实用户,IP地址的真实性和来源成为了一个关键问题。 什么是IP真人识别? IP真人识别是一种技术,旨在确定IP地址背后的用户是否为真实…...

MySQL 面试知识脑图 初高级知识点
脑图下载地址:https://mm.edrawsoft.cn/mobile-share/index.html?uuid18b10870122586-src&share_type1 sql_mode 基本语法及校验规则 ONLY_FULL_GROUP_BY 对于GROUP BY聚合操作,如果在SELECT中的列,没有在GROUP BY中出现ÿ…...

【数据结构】二叉树的链式结构及实现
目录 1. 前置说明 2. 二叉树的遍历 2.1 前序、中序以及后序遍历 2.2 层序遍历 3. 节点个数及高度等 4. 二叉树的创建和销毁 1. 前置说明 在学习二叉树的基本操作前,需先要创建一棵二叉树,然后才能学习其相关的基本操作。由于现在大家对二叉树结构…...

OpenCV4(C++)—— 创建窗口滑动条来调参
文章目录 创建滑动条 —— createTrackbar 创建滑动条 —— createTrackbar createTrackbar是OpenCV中的一个函数,用于创建一个可调节的滑动条(Trackbar),以便在图像处理过程中实时调整参数 int cv::createTrackbar(const String…...

深度学习基础知识 学习率调度器的用法解析
深度学习基础知识 学习率调度器的用法解析 1、自定义学习率调度器**:**torch.optim.lr_scheduler.LambdaLR2、正儿八经的模型搭建流程以及学习率调度器的使用设置 1、自定义学习率调度器**:**torch.optim.lr_scheduler.LambdaLR 实验代码: i…...
【JUC系列-12】深入理解PriorityQueue的底层原理和基本使用
JUC系列整体栏目 内容链接地址【一】深入理解JMM内存模型的底层实现原理https://zhenghuisheng.blog.csdn.net/article/details/132400429【二】深入理解CAS底层原理和基本使用https://blog.csdn.net/zhenghuishengq/article/details/132478786【三】熟练掌握Atomic原子系列基本…...

Paddle安装
Paddle安装参考 docs/tutorials/INSTALL_cn.md PaddlePaddle/PaddleDetection - Gitee.comhttps://gitee.com/paddlepaddle/PaddleDetection/blob/release/2.6/docs/tutorials/INSTALL_cn.md # 不指定版本安装paddle-gpu python -m pip install paddlepaddle-gpu# 测试安装 …...

配置XP虚拟机和Win 10宿主机互相ping通
文章目录 一、关闭虚机和宿主机的防火墙1、关闭虚拟机的防火墙1.1方式一1.2方式二 2、关闭宿主机的防火墙 二、设置XP和宿主机VMnet8的IP地址、网关和DNS1、获取VMWare的虚拟网络配置信息2、设置XP的VMnet8的IP地址、网关和DNS3、设置宿主机VMnet8的IP地址、网关和DNS 三、获取…...

【机器学习】sklearn对数据预处理
文章目录 数据处理步骤观察数据数据无量纲化缺失值处理处理分类型特征处理连续型特征 数据处理步骤 数据无量纲化缺失值处理处理分类型特征:编码与哑变量处理连续型特征:二值化与分段 观察数据 通过pandas读取数据,通过head和info方法大致查…...

【智慧燃气】智慧燃气解决方案总体概述--终端层、网络层
关键词:智慧燃气、智慧燃气系统、智慧燃气平台、智慧燃气解决方案、智慧燃气应用、智能燃气 智慧燃气解决方案是基于物联网、大数据、云计算、移动互联网等先进技术,结合燃气行业特征,通过智能设备全面感知企业生产、环境、状态等信息的全方…...

Tomcat隔离web原理和热加载热部署
Tomcat 如何打破双亲委派机制 Tomcat 的自定义类加载器 WebAppClassLoader 打破了双亲委派机制,它首先自己尝试去加载某个类,如果找不到再代理给父类加载器,其目的是优先加载 Web 应用自己定义的类。具体实现就是重写 ClassLoader 的两个方法…...

使用ffmpeg和python脚本下载网络视频m3u8(全网最全面)
网上给娃找了些好看的电影和一些有趣的短视频,如何保存下来呢?从网上找各种工具?都不方便。于是想到何不编程搞定,搞个脚本。对程序员来说这都不是事儿。且我有华为云服务器,完全可以把地址记下,后台自动下…...

【考研408常用数据结构】C/C++实现代码汇总
文章目录 前言数组多维数组的原理、作用稀疏数组 链表单向链表的增删改查的具体实现思路约瑟夫环问题(可不学)双向链表 树二叉搜索树中序线索二叉树哈夫曼树的编码与译码红黑树B树B树 堆顺序与链式结构队列实现优先队列排序算法(重点…...

Flink学习笔记(二):Flink内存模型
文章目录 1、配置总内存2、JobManager 内存模型3、TaskManager 内存模型4、WebUI 展示内存5、Flink On YARN 模式下内存分配6、Flink On Yarn 集群消耗资源估算6.1、资源分配6.2、Flink 提交 Yarn 集群的相关命令6.3、Flink On Yarn 集群的资源计算公式 1、配置总内存 Flink J…...

信息系统项目管理师第四版学习笔记——项目绩效域
干系人绩效域 干系人绩效域涉及与干系人相关的活动和职能。在项目整个生命周期过程中,有效执行本绩效域可以实现的预期目标主要包含:①与干系人建立高效的工作关系;②干系人认同项目目标;③支持项目的干系人提高了满意度…...

PyTorch 深度学习之加载数据集Dataset and DataLoader(七)
1. Revision: Manual data feed 全部Batch:计算速度,性能有问题 1 个 :跨越鞍点 mini-Batch:均衡速度与性能 2. Terminology: Epoch, Batch-Size, Iteration DataLoader: batch_size2, sheffleTrue 3. How to define your Dataset 两种处…...

小谈设计模式(26)—中介者模式
小谈设计模式(26)—中介者模式 专栏介绍专栏地址专栏介绍 中介者模式分析角色分析抽象中介者(Mediator)具体中介者(ConcreteMediator)抽象同事类(Colleague)具体同事类(C…...
7种设计模式
1. 工厂模式 优点:封装了对象的创建过程,降低了耦合性,提供了灵活性和可扩展性。 缺点:增加了代码的复杂性,需要创建工厂类。 适用场景:当需要根据不同条件创建不同对象时,或者需要隐藏对象创建…...

eNSP-Cloud(实现本地电脑与eNSP内设备之间通信)
说明: 想象一下,你正在用eNSP搭建一个虚拟的网络世界,里面有虚拟的路由器、交换机、电脑(PC)等等。这些设备都在你的电脑里面“运行”,它们之间可以互相通信,就像一个封闭的小王国。 但是&#…...

RocketMQ延迟消息机制
两种延迟消息 RocketMQ中提供了两种延迟消息机制 指定固定的延迟级别 通过在Message中设定一个MessageDelayLevel参数,对应18个预设的延迟级别指定时间点的延迟级别 通过在Message中设定一个DeliverTimeMS指定一个Long类型表示的具体时间点。到了时间点后…...

基于FPGA的PID算法学习———实现PID比例控制算法
基于FPGA的PID算法学习 前言一、PID算法分析二、PID仿真分析1. PID代码2.PI代码3.P代码4.顶层5.测试文件6.仿真波形 总结 前言 学习内容:参考网站: PID算法控制 PID即:Proportional(比例)、Integral(积分&…...
QMC5883L的驱动
简介 本篇文章的代码已经上传到了github上面,开源代码 作为一个电子罗盘模块,我们可以通过I2C从中获取偏航角yaw,相对于六轴陀螺仪的yaw,qmc5883l几乎不会零飘并且成本较低。 参考资料 QMC5883L磁场传感器驱动 QMC5883L磁力计…...

Swift 协议扩展精进之路:解决 CoreData 托管实体子类的类型不匹配问题(下)
概述 在 Swift 开发语言中,各位秃头小码农们可以充分利用语法本身所带来的便利去劈荆斩棘。我们还可以恣意利用泛型、协议关联类型和协议扩展来进一步简化和优化我们复杂的代码需求。 不过,在涉及到多个子类派生于基类进行多态模拟的场景下,…...
镜像里切换为普通用户
如果你登录远程虚拟机默认就是 root 用户,但你不希望用 root 权限运行 ns-3(这是对的,ns3 工具会拒绝 root),你可以按以下方法创建一个 非 root 用户账号 并切换到它运行 ns-3。 一次性解决方案:创建非 roo…...
如何为服务器生成TLS证书
TLS(Transport Layer Security)证书是确保网络通信安全的重要手段,它通过加密技术保护传输的数据不被窃听和篡改。在服务器上配置TLS证书,可以使用户通过HTTPS协议安全地访问您的网站。本文将详细介绍如何在服务器上生成一个TLS证…...

新能源汽车智慧充电桩管理方案:新能源充电桩散热问题及消防安全监管方案
随着新能源汽车的快速普及,充电桩作为核心配套设施,其安全性与可靠性备受关注。然而,在高温、高负荷运行环境下,充电桩的散热问题与消防安全隐患日益凸显,成为制约行业发展的关键瓶颈。 如何通过智慧化管理手段优化散…...
解决本地部署 SmolVLM2 大语言模型运行 flash-attn 报错
出现的问题 安装 flash-attn 会一直卡在 build 那一步或者运行报错 解决办法 是因为你安装的 flash-attn 版本没有对应上,所以报错,到 https://github.com/Dao-AILab/flash-attention/releases 下载对应版本,cu、torch、cp 的版本一定要对…...

JUC笔记(上)-复习 涉及死锁 volatile synchronized CAS 原子操作
一、上下文切换 即使单核CPU也可以进行多线程执行代码,CPU会给每个线程分配CPU时间片来实现这个机制。时间片非常短,所以CPU会不断地切换线程执行,从而让我们感觉多个线程是同时执行的。时间片一般是十几毫秒(ms)。通过时间片分配算法执行。…...