C++ 语法之函数和函数指针
在上一章中 C++ 语法之 指针的一些应用说明-CSDN博客
我们了解了指针变量,int *p;取变量a的地址这些。
那么函数同样也有个地址,直接输出函数名就可以得到地址,如下:
#include<iostream>
using namespace std;
void fun()
{int a = 5;}
int main()
{cout << "fun函数地址:" << fun;
}
返回函数地址:
这个就是系统为这个函数代码分配的内存空间的首地址。
既然有这个内存地址,那相对的,跟变量指针一样,也有函数指针,我们要怎么定义函数指针变量呢?
像int *p; char *cptr; 可以看到,定义指针变量需要提前进行类型区分。
所以函数指针,也是如此,必须说明这个函数的返回值,以及参数类型,几个参数。
如下定义:
void (*pFun)();
如果fun函数是这样
void fun(int a);
则对应的函数指针:
void (*pFun)(int);
你们可以在编译器尝试一下,是必须对应一致的,函数指针不能指向定义不一样的函数。
就像int *p指针不能指向char变量地址一样,只能是int类型的。
现在我们来一个例子学习一下:
#include<iostream>
using namespace std;
void (*pFun)();
void fun1()
{cout << "我是fun2";
}void fun()
{cout << "我是fun\n";pFun = fun1; //在这里 函数指针 指向另一个函数 pFun;
}int main()
{pFun = &fun; //为了便于阅读用&fun, 事实上直接pFun=fun也可以,下面加*号同理 可省略(*pFun)();(*pFun)();
}
结果:
上面的例子说明了,pFun指针指向fun,就是fun函数,指向fun1就是fun1函数。
其实跟变量指针一个道理。
函数的生命周期
我们知道在函数内正常定义的变量叫局部变量,函数执行完了,这些变量的内存空间就被释放了。
也就是函数每执行一次,比如有个int a=5;就会给a分配内存空间,执行完了,就会释放。
这种由系统分配,系统释放的内存空间,就是内存中的栈空间。
也就是说,这种变量在栈中申请空间。由系统管理释放。
而堆中,是由程序员自己申请的内存空间:
比如C的malloc函数(需free释放)
C++的new
例: int* ptr = new int; (需delete释放)
像这些,申请的空间,系统不会帮你自动释放,所以就需要你自己手动释放,否则,这块空间即使不使用了,也会被程序一直占用。它并没有栈中内存空间的功能,自动释放。
这个内存堆栈空间是系统定义的,而物理上内存并没有此种划分,需要明白。
下面我们用一些例子来证明:
#include <iostream>using namespace std;
int i = 0;
void fun() {int a = 5;cout << "第" << i << "次地址:" << &a << endl;
}int main() {for (; i < 4; i++) {fun();}return 0;
}
每次调用fun函数,输出局部变量a的地址,理论上应该是不同的地址,因为每次函数调用完之后a变量在栈中的空间会被释放。
但是结果:
地址是一样的,这是系统优化分配的原因,因为这个地址被释放了,下次分配还可以找同样的地址。这是合法的,就像在磁盘删掉一个文件,然后再存储,还是原来的位置。
所以由于这种现象,上面这个代码并不能证明变量被重新分配内存空间。
我们要怎么做,在函数执行期间,调用其它函数干扰栈空间分配,就像磁盘删除文件,然后复制大量其它文件,这样再粘贴的文件位置就会不一样?
比如下面这个:
#include <iostream>using namespace std;
int i = 0;
void fun() {int a = 5;cout << "第" << i << "次地址:" << &a << endl;
}
void other()
{int b = 5;int c = 6;int bc[56] = { 0 };
}
int main() {for (; i < 4; i++) {fun(); other();//调用一下其它函数,里面申请栈空间,打乱分配。}return 0;
}
并没有用,原因是other里的也是局部变量,执行完后,同样变量占用的栈空间也被释放了。所以跟原来的还是一样。
那我们想个方法,不被释放,看下面的代码:
#include <iostream>using namespace std;
int i = 0;
void fun(int sum) {int a = 5;cout << "第" << sum << "次地址:" << &a << endl;
}int main() {for (; i < 4; i++) {fun(i);int b = 5; //定义 b变量,然后再调用fun(11) 此时b变量和fun(11)同时在for的作用域中fun(11);int c = 6;fun(22);}return 0;
}
很遗憾,还是没有效果,所有的a变量地址都是一样的。
我分析可能是进入for作用域一次性的分配好了(有待验证)。
从这个现象可以看出,虽然每次执行函数时其中的局部变量,都是重新分配,但系统遵循着某一种优化规则,使得每次分配的地址尽可能一样。
由于方向上的问题,这个规则就不深入研究了。
好了,通过直观的查看地址方法已失败,实验起来比较困难。
我们可以从侧面来验证,有两个方法,第一个通过值的变化来验证,如果内存空间被释放了,那么的它的值如果没有保留,那可以证明函数执行完,局部变量已经被释放。
取值验证:
#include <iostream>using namespace std;
int i = 0;
int* ptr;
void fun(int sum) {int a = 5;ptr = &a; //将a变量的地址存到全局指针变量ptr中 以便在函数外访问cout << "\n fun函数内a值:" << *ptr;
}
int main() {for (; i < 2; i++) {fun(i); cout << "\n函数外的a值:" << *ptr;}return 0;
}
结果:
可以看到,同样是取*ptr的值,函数外已经变了,说明系统没有为变量a留有内存空间来保存值了,函数执行完就被释放了。
第二个通过递归调用函数的方法强制验证,这样的地址绝对不能相同,比如说递归调用4次函数。
局部变量a肯定是不同的地址,如果每一次都重新分配空间的话。
为什么,因为在递归未完成时,所有的局部变量都不会被释放。因为所有的函数都没执行完。
它想复用上一次变量A的地址是不可能的。
这个方法是反向证明,证明每次是重新分配空间的。然后就可以佐证,即然每次执行重新分配空间,那么执行完了也应当是释放空间的。
递归调用验证:
#include <iostream>using namespace std;void fun(int sum) {int a = 5;if (sum == 0) return; //用sum来控制 递归调用fun函数,防止无限循环调用else{cout << "\nsum=" << sum << "时,a的地址:" << &a;sum = sum - 1;fun(sum);//递归调用fun}}
int main() {fun(4);return 0;
}
结果:
可以看到,每次a变量地址是不同的,四个不同的地址。
说了局部变量,这里有个有趣的点,有没有一种变量,我不想每次函数执行,重新分配和释放,是一直存在的,有,就是在函数内被static修饰的变量,这种变量跟全局变量一样,它的空间不是在堆栈中,而是静态内存空间中,从整个程序开始分配,运行期间一直存在,到程序结束才释放。
代码示例:
using namespace std;
int g = 5;
void fun() {static int s = 5; //静态变量int b = 10;int c = 10;s++;cout << "\n静态s变量值:" << s << "------地址:" << &s << "-----全局变量g地址:" << &g;cout << "\n局部变量b地址:" << &b << "---局部变量c地址:" << &c;}
int main() {for(int i=0;i<3;i++)fun();return 0;
}
运行结果:
1.从地址分配来看,可以证明,全局变量g和静态变量s 的地址相近,说明它们在同一块内存区域(静态存储区)。有着相同的特性。
2.而局部变量b,c又是另一块内存区域(动态存储区),即栈中。所以它们的地址很接近,只是后几位不同。
3.可以看到静态变量s的值在增长,说明并没有被释放,而开头一句static int s=5;静态变量在定义时赋值只会初始化一次。
4.另:还记得我们在文章开头取了一个函数地址吗,那么这个属于什么区域呢?这个是代码区,因为函数的执行代码是存储在程序代码区。
这就是一个函数的内存分布区域,不是所有的内容都是一起的。
相关文章:
C++ 语法之函数和函数指针
在上一章中 C 语法之 指针的一些应用说明-CSDN博客 我们了解了指针变量,int *p;取变量a的地址这些。 那么函数同样也有个地址,直接输出函数名就可以得到地址,如下: #include<iostream> using namespace std; void fun() …...
LabVIEW生成EXE文件错误提示
在LabVIEW生成EXE时弹出 “The build is missing one or more source files or items the source files reference on disk”,表明项目中引用的某些文件(如VI、子模块、依赖库或配置文件)未被正确包含或路径丢失。以下是具体原因及解决方案&a…...
HTML,CSS,JavaScript
HTML:负责网页的结构(页面元素和内容)。 CSS:负责网页的表现(页面元素的外观、位置等页面样式,如:颜色、大小等)。 Javascript:负责网页的行为(交互效果)。 MDN前端开发文档(MDN Web Docs) HTML HTML(HyperText Markup Language):超文本标记语言超文本:超越了文本的…...
SpringCloud 学习笔记2(Nacos)
Nacos Nacos 下载 Nacos Server 下载 | Nacos 官网 下载、解压、打开文件: 更改 Nacos 的启动方式 Nacos 的启动模式默认是集群模式。在学习时需要把他改为单机模式。 把 cluster 改为 standalone,记得保存! 启动startup.cmd Ubuntu 启动…...
Qt5.15.2实现Qt for WebAssembly与示例
目录 1.什么是Qt for WebAssembly? 1.1 什么是 WebAssembly? 1.2 WebAssembly 的优势 1.3 什么是 Qt for WebAssembly? 1.4 Qt for WebAssembly 的特点 1.5 编译过程 1.6 运行时环境 注意!!!注意&am…...
荣耀手机怎么录制屏幕?屏幕录制后为视频加水印更有“安全感”
在数字时代,屏幕录制已经成为记录和分享信息的重要方式之一。无论是记录游戏的高光时刻,还是制作教学视频,亦或是保存重要的线上会议内容,屏幕录制都能轻松搞定。 荣耀手机作为一款功能强大的设备,自然也提供了便捷的…...
3DXML 与 SOLIDWORKS 格式转换:技术协同及迪威模型方案
一、引言 在产品设计的前沿领域,3DXML 与 SOLIDWORKS 作为主流格式,虽各有所长,但因格式差异,常成为数据流通与协作的阻碍。对于技术人员和学生党而言,掌握二者间的转换技术,不仅能提升设计效率࿰…...
CH347使用笔记:CH347结合STM32CubeIDE实现单片机下载与调试
目录 基于 STM32CubeIDE的 CH347 JTAG/SWD调试器使用说明1. CH347驱动安装与配置2. STM32CubeIDE调试器配置2.1 打开相关工程后,进行以下操作2.2 openocd.exe替换2.3 脚本添加2.4 更改调试器选择 3. 下载程序4. 使用过程中可能遇到的问题4.1 CH347未插入4.2 Openocd…...
JS—基本数据类型和引用数据类型:1分钟掌握两者的区别
个人博客:haichenyi.com。感谢关注 一. 目录 一–目录二–分类三–核心区别四–实际场景中的问题五–总结对比 二. 分类 前面说过这么判断数据类型,今天来说说基本数据类型和引用数据类型的区别。 基本数据类型引用数据类型StringObjectNumberFunct…...
使用 CryptoJS 实现 AES 解密:动态数据解密示例
在现代加密应用中,AES(高级加密标准)是一种广泛使用的对称加密算法。它的安全性高、效率好,适合用于各种加密任务。今天,我们将通过一个实际的示例,展示如何使用 CryptoJS 实现 AES 解密,解密动态数据。CryptoJS 是一个基于 JavaScript 的加密库,它支持 AES、DES 等多种…...
[设计模式与源码]1_Spring三级缓存中的单例模式
欢迎来到啾啾的博客🐱,一个致力于构建完善的Java程序员知识体系的博客📚,记录学习的点滴,分享工作的思考、实用的技巧,偶尔分享一些杂谈💬。 欢迎评论交流,感谢您的阅读😄…...
使用React和google gemini api 打造一个google gemini应用
实现一个简单的聊天应用,用户可以通过输入问题或点击“Surprise me”按钮获取随机问题,并从后端API获取回答。 import { useState } from "react"; function App() {const [ value, setValue] useState(""); // 存储用户输入的问题…...
为什么Django能有效防御CSRF攻击?
在当今这个互联网高度发达的时代,Web安全问题层出不穷,其中跨站请求伪造(CSRF,Cross-Site Request Forgery)就是一个比较常见的威胁。攻击者利用用户的身份信息,发送恶意请求,改变用户的属性或执…...
Oracle常见系统函数
一、字符类函数 1,ASCII(c)和CHR(i)字符串和ascii码互转换 SQL> select ascii(Z) ,ascii(H),ascii( A) from dual;ASCII(Z) ASCII(H) ASCII(A) ---------- ---------- ----------90 72 32SQL> select chr(90),chr(72),chr(65) from dual;C…...
【Visio使用教程】
Visio使用教程 1. Visio 的基本介绍1.1 Visio 是什么?核心特点: 1.2 主要功能与应用场景典型用途:行业应用: 1.3 版本与兼容性1.4 Visio下载1.5 安装 2. Visio 的界面与基础操作2.1 界面布局详解2.2 创建新文档与模板选择2.3 形状…...
蓝桥杯 修剪灌木
问题描述 爱丽丝要完成一项修剪灌木的工作。 有 N 棵灌木整齐的从左到右排成一排。爱丽丝在每天傍晩会修剪一棵灌 木, 让灌木的高度变为 0 厘米。爱丽丝修剪灌木的顺序是从最左侧的灌木开始, 每天向右修剪一棵灌木。当修剪了最右侧的灌木后, 她会调转方向, 下一天开 始向左修…...
HTML中滚动加载的实现
设置div的overflow属性,可以使得该div具有滚动效果,下面以div中包含的是table来举例。 当table的元素较多,以至于超出div的显示范围的话,观察下该div元素的以下3个属性: clientHeight是div的显示高度,scrol…...
bbbbb
import java.util.ArrayList; import java.util.List; public class KthPermutation { public static String getPermutation(int n, int k) { // 计算阶乘 int[] factorial new int[n]; factorial[0] 1; for (int i 1; i < n; i) …...
Linux文件
1.Open函数 高频使用的Linux系统调用:open write read close Linux自带的工具:man手册: man 1是普通的shell命令,比如ls man 2是系统调用函数,比如open,write说明 在Linux系统库的定义: int o…...
kafka指北
为自己总结一下kafka指北,会持续更新。创作不易,转载请注明出处。 目录 集群controller选举过程broker启动流程 主题创建副本分布ISRleader副本选举机制LEO 生产数据流程同步发送和异步发送 分区策略ack应答生产者发送消息的幂等性跨分区幂等性问题&…...
Linux安装部署Elasticsearch8 全过程记录
一、安装 Elasticsearch8 1、下载 访问 Elasticsearch 官方网站(Download Elasticsearch | Elastic)。 在下载页面找到 Elasticsearch 8 的 Linux 版本(.tar.gz 格式)下载链接,点击下载。 下载Elasticsearch8&…...
ESP32(3)UDP通信
对于 lwIP 的 Socket 的使用方式,它与文件操作非常相似。在文件操作中,我们首先打开文件,然后进行读/写操作,最后关闭文件。在TCP/IP网络通信中,也存在着相同的操作流程,但所使用的接口不再是文件描述符或 …...
汽车机械钥匙升级一键启动的优点
汽车机械钥匙升级一键启动的优点主要包括: 便捷性:一键启动功能的引入极大地提升了用车便捷性。车主无需翻找钥匙,只需在车辆感应范围内轻触启动键,即可轻松发动汽车。 安全性:移动管家专车专用一键启动系统配备了防…...
【matlab例程】三维下的TDOA定位和EKF轨迹滤波例程,TDOA的锚点数量可自定义(订阅专栏后可获得完整代码)
本文所述的MATLAB例程实现了TDOA定位和扩展卡尔曼滤波(EKF)来提高位置估计的准确性,并通过可视化结果进行分析。 文章目录 运行结果MATLAB代码程序讲解关键步骤和功能步骤解释注意事项总结运行结果 三维轨迹: 三维误差曲线: RMSE曲线: 命令行输出内容:...
个人blog系统 前后端分离 前端js后端go
系统设计: 1.使用语言:前端使用vue,并使用axios向后端发送数据。后端使用的是go的gin框架,并使用grom连接数据库实现数据存储读取。 2.设计结构: 最终展示:仅展示添加模块,其他模块基本相似 前…...
OSG简介
OSG OpenSceneGraph (简称 OSG) 是一个开源的高性能3D图形库。 作用 它为开发者提供了一个强大的API,处理和渲染复杂的3D图形。 特点 OSG基于OpenGL构建,提供了对现代图形技术的支持,如着色器、纹理映射、光照模型等高级特性。 跨平台支…...
社区版Uos20.9从源码编译QT5.15.2
主要是在这个文章上学的究极保姆式教你如何在Ubuntu上源码安装Qt5.15.2_ubuntu安装qt5.15.2-CSDN博客 但原文上在环境变量的配置上真用在 uso上好像不行,要加一些引号和$号。原文的测试编译代码也有些问题,include上少了类。略作修改,在UOS社…...
AI学习第二天--大模型压缩(量化、剪枝、蒸馏、低秩分解)
目录 1. 量化:压缩大象的“脂肪” 比喻 技术逻辑 2. 剪枝:修剪大象的“无效毛发” 比喻 技术逻辑 3. 知识蒸馏:让大象“师从巨象” 比喻 技术逻辑 4. 低秩分解:把大象“折叠成纸偶” 比喻 技术逻辑 5. 推理优化&#…...
C++ —— 线程同步(互斥锁)
C —— 线程同步(互斥锁) 线程同步互斥锁(互斥量)测试代码mutex互斥锁 线程同步 线程同步:多线程协同工作,协商如何使用共享资源。 C11线程同步包含三部分内容: 互斥锁(互斥量&…...
相对路径跳转和绝对路径跳转有什么区别?
在 Vue 3 中使用路由跳转时,相对路径跳转和绝对路径跳转在使用方式、适用场景等方面存在明显区别,以下为你详细介绍: 定义 绝对路径跳转:指的是使用完整的路径来进行路由导航,路径以 / 开头,无论当前处于…...
Flume详解——介绍、部署与使用
1. Flume 简介 Apache Flume 是一个专门用于高效地 收集、聚合、传输 大量日志数据的 分布式、可靠 的系统。它特别擅长将数据从各种数据源(如日志文件、消息队列等)传输到 HDFS、HBase、Kafka 等大数据存储系统。 特点: 可扩展࿱…...
笔记类AI应用体验
笔记类AI应用体验 叮当好记视频一键转笔记, 祝你学习效率起飞 IMAGet笔记印象笔记(Evernote):Notion:Trilium Notes:二、开始搭建三、搭建步骤四、创建博客 Obsidian:案例让ai帮我执行大模型学习…...
Mysql篇——SQL优化
本篇将带领各位了解一些常见的sql优化方法,学到就是赚到,一起跟着练习吧~ SQL优化 准备工作 准备的话我们肯定是需要一张表的,什么表都可以,这里先给出我的表结构(表名:userinfo) 通过sql查看…...
【css酷炫效果】纯CSS实现故障文字特效
【css酷炫效果】纯CSS实现故障文字特效 缘创作背景html结构css样式完整代码基础版进阶版(3D效果) 效果图 想直接拿走的老板,链接放在这里:https://download.csdn.net/download/u011561335/90492053 缘 创作随缘,不定时更新。 创作背景 刚…...
【Java】链表(LinkedList)(图文版)
本博客总结了Java当中链表的实现,以及相关方法的使用,在最后附带了一些常见链表相关处理技巧,希望对你有帮助! ps:可拷贝到IDEA上自行测试,代码全部完成测试。 一.链表概述 1.什么是链表? 链…...
审批工作流系统xFlow
WorkFlow-审批流程系统 该项目为完全开源免费项目 可用于学习或搭建初始化审批流程系统 希望有用的小伙伴记得点个免费的star gitee仓库地址 仿钉钉飞书工作审批流系统 介绍 前端技术栈: vue3 ts vite arcodesign eslint 后端技术栈:springbootspring mvc mybatis mavenmysq…...
UNION,UNION ALL 的详细用法
目录 一、基本概念 二、核心区别 三、语法使用规则 四、代码实演示 4.1 两张表字段相同,字段顺序也相同 4.2 两张表字段相同。但字段顺序不同 4.3 两张表存在相同字段,但一张表字段多,一张表字段少 一、基本概念 操作符功能描述去重处…...
Java 集合遍历过程中修改数据触发 Fail-Fast 机制 ,导致报ConcurrentModificationException异常
Java Fail-Fast 机制 Fail-Fast 机制是 Java 集合框架中的一种错误检测机制,用于在遍历集合时检测结构修改。如果在迭代器创建之后,集合被修改(例如添加或删除元素),并且这种修改不是通过迭代器自身的 remove() 方法进…...
Javascript 日期相关计算
1、获取当前日期的前一天 // 获取当前日期let today new Date();today.setDate(today.getDate() - 1);// 转换为本地日期字符串格式let yesterdayStr today.toISOString().slice(0, 10);console.log(yesterdayStr); // 例如: "2023-04-03" (格式取决于地区设置) 2…...
自动驾驶背后的数学:特征提取中的线性变换与非线性激活
在上一篇博客「自动驾驶背后的数学:从传感器数据到控制指令的函数嵌套」—— 揭秘人工智能中的线性函数、ReLU 与复合函数中,我们初步探讨了自动驾驶技术中从传感器数据到控制指令的函数嵌套流程,其中提到了特征提取模块对传感器数据进行线性…...
DNS解析查询工具
dig命令 1 常用命令 命令:dig 您的域名(示例:dig www.baidu.com) 2、根据解析记录查询,比如MX,CNAME,NS,PTR等,只需将类型加在命令后面即可。 示例:dig bai…...
【eNSP实战】(续)一个AC多个VAP的实现—将隧道转发改成直接转发
在 一个AC多个VAP的实现—CAPWAP隧道转发 此篇文章配置的基础上,将隧道转发改成直接转发 一、改成直接转发需要改动的配置 (一)将连接AP的接口改成trunk口,并允许vlan100、101、102通过 [AC1]interface GigabitEthernet 0/0/8 …...
解决远程卡在下载vscode-server的问题,一键安装脚本
vscode-server 下载与安装脚本 vscode-server一键安装脚本 简介 此脚本用于下载并安装指定提交 ID 和架构的 VS Code Server。用户可以选择不同的架构,并输入对应的 VS Code 提交 ID 来下载和安装 vscode-server。VS Code提交ID可以在VS Code界面“帮助>关于…...
【unity实战】用unity封装一个复杂全面且带不同射击模式的飞机大战射击系统
考虑到每个人基础可能不一样,且并不是所有人都有同时做2D、3D开发的需求,所以我把 【零基础入门unity游戏开发】 分为成了C#篇、unity通用篇、unity3D篇、unity2D篇。 【C#篇】:主要讲解C#的基础语法,包括变量、数据类型、运算符、流程控制、面向对象等,适合没有编程基础的…...
LeetCode[42] 接雨水
动态规划 left_max[i] height[i]左侧的最高高度right_max[i] height[i]右侧的最高高度height[i]能接多少水?min(left_max[i], right_max[i])-height[i] class Solution { public:int trap(vector<int>& height) {int len height.size();vector<in…...
c++ 基础题目lambda
1. auto lambda = [](double x) { return static_cast<int>(x); }; 是 匿名函数对象 ,不可直接声明 a.可以赋值给一个与其类型兼容的 std::function 类型的对象 std::function<int(int, int)> lambda = [](int x, int y) { return x + y; }; b.使用具体的 lambda …...
RTSP/Onvif安防视频EasyNVR平台 vs.多协议接入视频汇聚EasyCVR平台:设备分组的区别
EasyNVR安防视频云平台是旭帆科技TSINGSEE青犀旗下支持RTSP/Onvif协议接入的安防监控流媒体视频云平台。平台具备视频实时监控直播、云端录像、云存储、录像检索与回看、告警等视频能力,能对接入的视频流进行处理与多端分发,包括RTSP、RTMP、HTTP-FLV、W…...
网络编程套接字【端口号/TCPUDP/网络字节序/socket编程接口/UDPTCP网络实验】
网络编程套接字 0. 前言1. 认识端口号2. 认识TCP和UDP协议3. 网络字节序4. socket编程接口5. 实现一个简单的UDP网络程序5.1 需求分析5.2 头文件准备5.3 服务器端设计5.4 客户端设计5.5 本地测试5.6 跨网络测试5.7 UDP小应用——客户端输入命令,服务器端执行 6. 地址…...
【C语言预编译处理精选题】
C语言预编译处理精选题 一、选择易错题1.1 纯文本替换,注意优先级!1.2 再来一道文本替换,别马虎1.3 宏定义的替换1.4带参数宏定义的空格问题1.5 " "的include1.6 条件编译1.7 预编译概念 二、填空易错题2.1 注意两个连续的 i2.2 异…...
云钥科技工业相机定制服务,助力企业实现智能智造
在工业自动化、智能制造和机器视觉快速发展的今天,工业相机作为核心感知设备,其性能直接决定了检测精度、生产效率和产品质量。然而,标准化工业相机往往难以满足复杂多样的应用场景需求,工业相机定制逐渐成为企业突破技术瓶颈…...