-
如果想要更详细的了解指针原理、用途,请自行阅读《The C Programming Language》第 5 章。指针是什么指针其实就是一个包含一个变量的地址的变量。如何定义指针定义指针的方式很简单:类型 *指针名称;即可。比如来声明一个变量名为a,类型为int的指针:int *a;1也就是比平时声明变量多了一个型号*。但是这里需要注意的是,声明时候的*a表示的是指针a包含的内存地址指向的内容,而a包含的则是地址。 这个地址开始的一部分连续的空间将会划分给指针,以供存储地址,划出的大小由类型int决定,一般int为 2 个字节(这里的一个字节在有些英文文档中被称为“cell”)。如何获取一个变量的地址刚才说到指针其实就是一个包含一个变量的地址的变量。那么如何获取一个变量的地址呢?这样才可以给指针赋值。方法很简单,使用&符号,如下(a就是上文声明的指针):a=&b;1通过以上表达式,就将变量b的地址赋值给指针a,如果这时候使用*a输出,就会发现输出的是b的值。而且修改b,输出*a的值也会发生改变。如果a并不是指针,那么变量a将会以十六进制存储变量b的地址(一般是十六进制,有些环境、设备可能会有不同)。下面展示输出一个变量地址的方法:#include <stdio.h>int main(){ int a=10; printf("%x\n",&a); return 0;}12345678910输出结果如下:bfeff2c8Program ended with exit code: 012可以看到,第一行的bfeff2c8就是变量a的地址。赋值给一个指针指针只负责存储地址,具体的值还是由变量存储。所以赋值给指针,就是将一个变量的地址赋值给它。如下:#include <stdio.h>int main(){ int a=10; int *b; b=&a; printf("%d\n",*b); return 0;}12345678910111213输出:10Program ended with exit code: 012指针的用途听了上述描述,可能你会觉得指针的作用好像就是新建了一个软连接(symbolic link)或者创建了一个别名(alias)。但是指针最大的作用其实是搭配数组使用。通过指针来使用数组因为一个数组的地址其实是数组第一个元素的地址,而数组在地址上是连续存放的,所以可以对地址加 1 来实现控制数组。如下(实际运行的时候可能会提醒,但是这里出于研究目的忽略提醒):#include <stdio.h>int main(){ int a[5]={1,2,3,4,5}; int *b; b=&a; printf("数组地址:%x\n",&a); printf("数组第一个元素:%d\n",*b); b=b+1; printf("是不是数组第二个元素:%d\n",*b); return 0;}12345678910111213141516输出:数组地址:bfeff2b0数组第一个元素:1是不是数组第二个元素:2Program ended with exit code: 01234这里说明一下,如果在开发过程中,指针指向数组的话,请使用b=&a[0];,这样不会出现提醒。字符串和指针字符串也是数组的一部分,所以也可以使用指针来实现一些功能,例如对比字符串(这个虽然不用指针也能写,但是使用指针的话,代码会变得更简洁)。下面来演示一下指针访问字符串的例子(记得把指针类型换一下,换成char,不然int存放的会是 ASCII编码,而不是字符串的内存地址):#include <stdio.h>int main(){ char a[20]="Hello! World!"; char *b; b=&a[0]; printf("字符串的地址:%x\n",&a); printf("字符串的第一个字符:%c\n",*b); b=b+5; printf("字符串的第六个字符:%c\n",*b); return 0;}12345678910111213141516输出:字符串的地址:bfeff2b0字符串的第一个字符:H字符串的第二个字符:!Program ended with exit code: 01234其他用途还有一些其他的用途,但是有上述的知识也不用过多解释了,讲一下思路即可。更多维度的数组C 语言支持二维数组,但是有时候需要更多维度,那么就需要使用指针来嵌套数组,这样可以实现更多维度的数组。一般就是将指向多个数组的多个指针存放在一个数组中,然后通过上述方法来使用。处理命令选项这个一般使用不多,因为现在也没多少人使用命令行,都用图形化了,但是我还是要说一下。先看一下我的另一篇博客《C语言中函数main的参数argc和argv是什么》。argv是一个指向一个字符串数组的指针,数组包含了参数,每个字符串就是一个参数,最后一个元素为0。不过一般习惯使用多级指针来操作字符串。这里介绍一下,一般的命令行程序,使用起来的命令如下:程序名 -短选项 --长选项 内容1短选项为-加一个字母,长选项一般为--加一个单词。现在有些程序的短选项会省略-。短选项一般可以写到一起,举个例子:ls -a -f也可以写成ls -af。而这一功能的实现就需要使用指针(首先这是个存放了指向字符串指针的数组,其次指针真的更方便)。————————————————版权声明:本文为CSDN博主「zhonguncle」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。原文链接:https://blog.csdn.net/qq_33919450/article/details/126129081
-
整理 | 苏宓出品 | CSDN(ID:CSDNnews)近日,来自多伦多大学和 YScope 公司(为软件系统提供创新的日志管理和故障排除工具。由一群计算机工程教授和博士创立)的 David Lion、多伦多大学 Adrian Chiu 和 Michael Stumm、多伦多大学和 YScope 公司 Ding Yuan 共同发布了一份《调查托管语言的运行时性能:为什么 JavaScript 和 Python 比 C++ 慢了 8 倍和 29 倍,而 Java 和 Go 却能更快》(https://www.usenix.org/system/files/atc22-lion.pdf)的论文分析报告,深度剖析了不同编程语言运行时在代码开发中真实的性能情况,由此方便开发者可以精确地测量执行任何字节码指令所花费的时间等。性能是系统软件不得不面对的挑战在报告中,研究人员指出,自 2015 年以来,具有集成运行时环境的编程语言越来越受欢迎,其中,全球知名的代码托管平台 GitHub 上最受欢迎的三种语言分别是 JavaScript、Java 和 Python。作为开发利器,编程语言帮助开发者快速构建各种应用程序和服务,也极大地提高了生产力。同时,这些语言自身也提供了各种功能,如动态类型检查、带有垃圾收集的内存管理,以及动态内存安全检查等等。为此,研究人员用「托管语言」(managed languages)专业术语来指代这些类型的编程语言。现实来看,托管语言越来越多地被用于实现性能至关重要的系统软件上,如Hadoop 和 Spark 都在 Java 虚拟机(JVM)上运行,因为它们分别用 Java 和 Scala 实现;Kubernetes、etcd(分布式键值存储)和 M3(由 Uber 建立的分布式时间序列数据库和查询引擎)都是用 Go 实现的。当前,甚至连操作系统(OS)的内核 Biscuit 也是用 Go 实现的 。Openstack、Paypal、Instagram 和 Dropbox 都大量使用 Python,其中,Python 是 Dropbox "在后台服务和桌面客户端应用中使用最广泛的语言",在一个存储库中就有近 400 万行 Python 代码;JavaScript 也被用于 Facebook 的 Bladerunner pub/sub 系统的性能关键路径中。在开发过程中,编程语言的性能在一开始很少会被考虑到项目中,部分原因是不少开发者认为性能问题可以在以后慢慢去解决,也许可以通过简单地增加硬件来进行横向扩展。不过,随着代码产品或服务使用规模的扩大,服务变得越来越慢或者硬件成本变高,性能成为一个不容忽视的问题。这也是为什么 Stream 要放弃了 Python 而改用 Go、 Discord 从 Go 切换到 Rust、Twitter 从 Ruby on Rails 切换到 Scala 和 Java 的主要原因。不少开发者往往为了提升性能,想破脑袋,但现实只有两条路,一条是从现有的代码中想尽办法尽可能地做优化,另一条是思考使用的编程语言是否已经达到了性能极限,看看有没有必要将旧的代码移植到一个新的性能更高的语言上。为了彻底解开系统软件中不同编程语言导致的性能问题,研究人员决定以 C++ 为极限,对 Java、Go、JavaScript 和 Python 四种编程,还有应用最广泛的运行时系统 CPython、OpenJDK。Node.js 与 JavaScript 的 V8 引擎进行深入的定量性能分析。同时,研究人员还从头开始建立了 6 个应用程序,并创建了一个名为 LangBench 基准(https://github.com/topics/langbench)。这些应用程序涵盖了各种不同的计算强度、内存使用、网络和磁盘 I/O 强度以及可用的并发性的应用场景等复杂性。对此,研究人员全面分析了它们的完成时间、资源使用和可扩展性。测试方法值得一提的是,研究人员指出,这份论文没有也不可能全面地回答与语言运行时的性能有关的每一个问题。本文只是评估了四种语言的运行时,而且对于每种语言,只评估了最广泛使用的实现。此外,研究人员只在一个单一的操作系统/硬件堆栈上运行了工作负载。其研究结果与使用的基准有关,这些基准模拟了现实生活中的应用,但可能不代表广泛的应用。在测试方法上,研究人员在两台内部服务器上进行了实验,每台服务器有 2 个Xeon E5-2630V3、16 个虚拟核心、2.4GHz CPU、256GB DDR4 内存和两个 7200 RPM 硬盘。它们运行的系统是 Linux 4.15.0,并通过 10Gbps 的互联网络连接。对于 C++ 程序,研究人员使用的是 GCC 9.3.0 根据 C++17 标准用 -O3 进行编译。对于 OpenJDK 13、CPython 3.8.1 和Go 1.14.1 ,其使用了各自语言的参考实现。同时,使用 Node.js 13.12.0 和 V8 7.9.317.25 版本。研究人员对每个基准进行了 5 次测试,取平均值。其中,在运行键-值存储、日志分析器和文件服务器的基准时,client 和 worker 线程的数量从 1 到 1024 不等。对于 OpenJDK 和 V8 来说,最小的内存量是通过确定不会导致崩溃的第一个堆配置来设置的;对于 Go 来说,GOGC 被设置为5%。然后研究人员不断增加堆的设置,直到性能不再提高。其使用第一个设置的结果(即最小的堆大小)得出最佳性能。对于日志解析器和文件服务器基准,所用的日志文件被存储在一个复制系数为 2 的分布式文件系统上。 在运行每个基准之前,研究人员都清除了 Linux 的页面缓存,以保证测试准确性。其中,优化的 GCC 平均速度最快,Go 和 OpenJDK 紧随其后,比 GCC 慢了 1.30 倍和 1.43 倍。令人印象深刻的是,在 12 项基准测试中,Go 和 OpenJDK 有 3 项超过了优化的 GCC。总体而言,研究人员发现 V8 / Node.js 和 CPython 表现最差,执行应用程序的平均速度分别比 C++ 应用程序慢 8.01 倍和 29.50 倍,这意味着运行时下,JavaScript、Python 要比 C++ 慢这么多。更糟糕的是,这两个运行时上的应用程序扩展性很差,因为它们无法有效地利用多个内核。在极端情况下,CPython 比 GCC 慢了 129.66 倍(在排序基准中)。V8/Node.js 和 CPython 只有在工作负载受到磁盘 I/O 的瓶颈时,即在文件服务器基准中,才与 GCC 有竞争力。相比之下,OpenJDK 和 Go 应用程序即 Java 和 Go 语言比 C++ 更具有性能竞争力,运行速度仅慢了 1.43 倍和 1.30 倍,并且可以轻松扩展到多个内核。在一些应用中,OpenJDK 和 Go 的性能超过了 C++ 的同类产品。更多完整报告内容详见:https://www.usenix.org/system/files/atc22-lion.pdf
-
如果说C++在内存安全上做出了自己的努力,那么在线程并发安全上则努力程度还不够;而Rust则是从一开始就在内存安全和线程安全上下足了功夫,同时没有抛弃性能。在一些基本的语言表达方式上,Rust和C/C++存在一些不同,体现在:(1)变量默认是不可变绑定(let),需要修改变量,则需明确使用可变绑定(let mut);(2)没有实现Copy trait的对象,绑定、赋值、非引用传参时默认是移动语义;(3)支持函数内嵌定义;(4)支持函数表达式返回(最后不加分号);(5)在同一个作用域内,变量可以重新绑定(let),在Rust中叫做遮蔽机制;(6)支持零尺寸的结构体、空枚举、空数组([T, 0]);(7)两种字符串类型变量:&str相当于C++中的const char*,用于指向字符串字面常量;而String相对于C++中的std::string,支持可变引用&Mut String和不可变引用&String;(8)基本的数据类型都实现了Copy trait,默认在栈上分配,支持复制语义;而String、Vec等默认只支持移动语义,要进行深拷贝,需要显式调用clone函数;(9)不支持switch & case,使用match模式匹配代替;(10)不支持三目运算符;(11)支持?运算符,用于调用的函数返回异常时,直接退出当前函数并返回对应的错误Err<T>;……
-
1、C和C++的区别1)C是面向过程的语言,是一个结构化的语言,考虑如何通过一个过程对输入进行处理得到输出;C++是面向对象的语言,主要特征是“封装、继承和多态”。封装隐藏了实现细节,使得代码模块化;派生类可以继承父类的数据和方法,扩展了已经存在的模块,实现了代码重用;多态则是“一个接口,多种实现”,通过派生类重写父类的虚函数,实现了接口的重用。2)C和C++动态管理内存的方法不一样,C是使用malloc/free,而C++除此之外还有new/delete关键字。3)C++中有引用,C中不存在引用的概念2、C++中指针和引用的区别1)指针是一个新的变量,存储了另一个变量的地址,我们可以通过访问这个地址来修改另一个变量;引用只是一个别名,还是变量本身,对引用的任何操作就是对变量本身进行操作,以达到修改变量的目的2)引用只有一级,而指针可以有多级3)指针传参的时候,还是值传递,指针本身的值不可以修改,需要通过解引用才能对指向的对象进行操作引用传参的时候,传进来的就是变量本身,因此变量可以被修改3、结构体struct和共同体union(联合)的区别结构体:将不同类型的数据组合成一个整体,是自定义类型共同体:不同类型的几个变量共同占用一段内存1)结构体中的每个成员都有自己独立的地址,它们是同时存在的;共同体中的所有成员占用同一段内存,它们不能同时存在;2)sizeof(struct)是内存对齐后所有成员长度的总和,sizeof(union)是内存对齐后最长数据成员的长度、结构体为什么要内存对齐呢?1.平台原因(移植原因):不是所有的硬件平台都能访问任意地址上的任意数据,某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常2.硬件原因:经过内存对齐之后,CPU的内存访问速度大大提升。4、#define和const的区别1)#define定义的常量没有类型,所给出的是一个立即数;const定义的常量有类型名字,存放在静态区域2)处理阶段不同,#define定义的宏变量在预处理时进行替换,可能有多个拷贝,const所定义的变量在编译时确定其值,只有一个拷贝。3)#define定义的常量是不可以用指针去指向,const定义的常量可以用指针去指向该常量的地址4)#define可以定义简单的函数,const不可以定义函数5、重载overload,覆盖(重写)override,隐藏(重定义)overwrite,这三者之间的区别1)overload,将语义相近的几个函数用同一个名字表示,但是参数列表(参数的类型,个数,顺序不同)不同,这就是函数重载,返回值类型可以不同特征:相同范围(同一个类中)、函数名字相同、参数不同、virtual关键字可有可无2)override,派生类覆盖基类的虚函数,实现接口的重用,返回值类型必须相同特征:不同范围(基类和派生类)、函数名字相同、参数相同、基类中必须有virtual关键字(必须是虚函数)3)overwrite,派生类屏蔽了其同名的基类函数,返回值类型可以不同特征:不同范围(基类和派生类)、函数名字相同、参数不同或者参数相同且无virtual关键字6、new、delete、malloc、free之间的关系new/delete,malloc/free都是动态分配内存的方式1)malloc对开辟的空间大小严格指定,而new只需要对象名2)new为对象分配空间时,调用对象的构造函数,delete调用对象的析构函数既然有了malloc/free,C++中为什么还需要new/delete呢?运算符是语言自身的特性,有固定的语义,编译器知道意味着什么,由编译器解释语义,生成相应的代码。库函数是依赖于库的,一定程度上独立于语言的。编译器不关心库函数的作用,只保证编译,调用函数参数和返回值符合语法,生成call函数的代码。malloc/free是库函数,new/delete是C++运算符。对于非内部数据类型而言,光用malloc/free无法满足动态对象都要求。new/delete是运算符,编译器保证调用构造和析构函数对对象进行初始化/析构。但是库函数malloc/free是库函数,不会执行构造/析构。7、delete和delete[]的区别delete只会调用一次析构函数,而delete[]会调用每个成员的析构函数用new分配的内存用delete释放,用new[]分配的内存用delete[]释放一.构造函数构造函数是和类名相同的一个函数,它的作用是实现对象的初始化。当对象被创建时,构造函数自动被调用。特点:没有类型没有返回值(也不用写void)名字与类名相同可重载!作用:完成类的对象的初始化Cdate d; //定义对象d注意:当对象d被创建时,会自动调用构造函数 d.Cdate()。当类中未定义构造函数时,编译器会自动假设存在以下两个默认构造函数:(此构造函数什么都不做,就是个形式)。如果作者自己定义了构造函数,则默认的构造函数不会存在。//默认构造函数一 Cdate::Cdate() { } //默认构造函数二 Cdate::Cdate(const Cdate& a) { }三.析构函数我们已经知道构造函数是在创建对象时,对其进行初始化。而析构函数与其相反,是在对象被删除前象由系统自动执行它做清理工作。作为一个类,可能有多个对象,每个对象生命结束时都要调用析构函数,且每个对象调用一次。特点:无类型无返回值名字与类名相同不带参数,不可重载,析构函数只有一个!析构函数前“~” (取反符,表示逆构造函数)作用:在对象被删除前做清理工作。注意:对象的析构函数在对象被销毁前被调用,对象何时销毁也与其作用域相关。例如,全局对象是在程序运行结束时销毁;自动对象是在离开其作用域时销毁;而动态对象是在使用delete运算符时销毁。析构函数特别适用于当一个对象被动态分配内存空间,而在对象被销毁前希望释放它所占用的内存空间的时候。我们不会忽略初始化的重要性,却常常忽略清除的重要性,然而对销毁变量的内存清理是非常重要的。例如,我们在堆中申请了一些内存,如果没有用完就释放,会造成内存泄露,会导致应用程序运行效率降低,甚至崩溃,不可掉以轻心。而在c++中提供有析构函数,可以保证对象清除工作自动执行。析构与构造的调用次序相反,即最先构造的最后被析构,最后构造的最先被析构。7.1、虚函数、纯虚函数虚函数:虚函数是C++中用于实现多态(polymorphism)的机制。核心理念就是通过基类访问派生类定义的函数,是C++中多态性的一个重要体现。利用基类指针访问派生类中的虚函数,这种情况下采用的是动态绑定技术。纯虚函数:纯虚函数是在基类中声明的虚函数,它在基类中没有定义,但要求任何派生类都要定义自己的实现方法。在基类中实现纯虚函数的方法是在函数原型后加“=0”.纯虚函数不能实例化对象。抽象类的介绍抽象类是一种特殊的类,它是为了抽象和设计的目的为建立的,它处于继承层次结构的较上层。(1)抽象类的定义: 称带有纯虚函数的类为抽象类。(2)抽象类的作用: 抽象类的主要作用是将有关的操作作为结果接口组织在一个继承层次结构中,由它来为派生类提供一个公共的根,派生类将具体实现在其基类中作为接口的操作。所以派生类实际上刻画了一组子类的操作接口的通用语义,这些语义也传给子类,子类可以具体实现这些语义,也可以再将这些语义传给自己的子类。(3)使用抽象类时注意:抽象类只能作为基类来使用,其纯虚函数的实现由派生类给出。如果派生类中没有重新定义纯虚函数,而只是继承基类的纯虚函数,则这个派生类仍然还是一个抽象类。如果派生类中给出了基类纯虚函数的实现,则该派生类就不再是抽象类了,它是一个可以建立对象的具体的类。抽象类是不能定义对象的。总结:1、纯虚函数声明如下: virtual void funtion1()=0; 纯虚函数一定没有定义,纯虚函数用来规范派生类的行为,即接口。包含纯虚函数的类是抽象类,抽象类不能定义实例,但可以声明指向实现该抽象类的具体类的指针或引用。2、虚函数声明如下:virtual ReturnType FunctionName(Parameter) 虚函数必须实现,如果不实现,编译器将报错,错误提示为:3、对于虚函数来说,父类和子类都有各自的版本。由多态方式调用的时候动态绑定。4、实现了纯虚函数的子类,该纯虚函数在子类中就编程了虚函数,子类的子类即孙子类可以覆盖该虚函数,由多态方式调用的时候动态绑定。5、虚函数是C++中用于实现多态(polymorphism)的机制。核心理念就是通过基类访问派生类定义的函数。6、在有动态分配堆上内存的时候,析构函数必须是虚函数,但没有必要是纯虚的。纯虚函数的引入,是出于两个目的:1、为了安全,因为避免任何需要明确但是因为不小心而导致的未知的结果,提醒子类去做应做的实现。2、为了效率,不是程序执行的效率,而是为了编码的效率。动态绑定:基类指针是调用派生类的中的成员函数还是调用基类中的成员函数要到程序运行时确定。主要看此时基类指针所指向的对象。 这里要涉及一些很重要的概念,也是我最近看完Effective C++才明白的东西,记录下来。这些概念就是静态类型和动态类型,静态绑定和动态绑定。静态绑定和动态绑定。静态绑定是说前期绑定。 所谓对象的静态类型,就是它在程序中被声明的时候采用的类型。 考虑下面的class继承体系:class Shape{ virtual void draw(color = Red) const=0; ... ... }; class Rectangle:public Shape{ virtual void draw(color = Red) const; ... ... }; class Circle:public Shape { virtual void draw(color = Red) const; ... ... }; 现在考虑以下这些指针: Shape* ps;//静态类型为Shape* Shape*pc =new Circle;//静态类型Shape* Shape*pr = new Rectangle;//静态类型Shape*在本例中,ps,pc,pr都被声明为Shape*类型的,所以它们的静态类型都是Shape*。注意:无论它们真正指向什么,它们的静态类型都是Shape*. 所谓的对象的动态类型是指“当前所指对象的类型”。也就是说,动态类型可以表现出一个对象将会有什么行为。根据上面的例子,pc的动态类型是Circle*,pr的动态类型是Rectangle*。ps没有动态类型,因为它没有指向任何对象。 动态类型一如其名所示,可以在执行过程中改变(通常是经过赋值运算):ps=pc; \\ps的动态类型如今是Circle* ps=pr; \\ps的动态类型如今是Rectangle*Virtual函数系动态绑定而来,意思是调用一个virtual函数的时候,究竟调用的是哪一个函数代码,取决于发出调用的那个对象的动态类型。ps->draw(); \\调用的是Rectangle::draw(Red)8、STL库用过吗?常见的STL容器有哪些?算法用过几个?STL包括两部分内容:容器和算法容器即存放数据的地方,比如array, vector,分为两类,序列式容器和关联式容器序列式容器,其中的元素不一定有序,但是都可以被排序,比如vector,list,queue,stack,heap, priority-queue, slist关联式容器,内部结构是一个平衡二叉树,每个元素都有一个键值和一个实值,比如map, set, hashtable, hash_set算法有排序,复制等,以及各个容器特定的算法迭代器是STL的精髓,迭代器提供了一种方法,使得它能够按照顺序访问某个容器所含的各个元素,但无需暴露该容器的内部结构,它将容器和算法分开,让二者独立设计。Vector是顺序容器,是一个动态数组,支持随机存取、插入、删除、查找等操作,在内存中是一块连续的空间。在原有空间不够情况下自动分配空间,增加为原来的两倍。vector随机存取效率高,但是在vector插入元素,需要移动的数目多,效率低下。注意:vector动态增加大小时,并不是在原空间之后持续新空间(因为无法保证原空间之后尚有可供配置的空间),而是以原大小的两倍另外配置一块较大的空间,然后将原内容拷贝过来,然后才开始在原内容之后构造新元素,并释放原空间。因此,对vector的任何操作,一旦引起空间重新配置,指向原vector的所有迭代器就都失效了。————————————————版权声明:本文为CSDN博主「Cpp编程小茶馆」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。原文链接:https://blog.csdn.net/xu_fu_yong/article/details/122948379
-
【功能模块】深度学习模型部署【操作步骤&问题现象】1、深度学习模型(如检测、跟踪)部署时,是选用c++还是培养python?mdc300 mini上能否pip安装?python是否支持和c++程序之间的通信?2、modelzoo给的模型大部分是基于python的,是否能支持转换为om模型?算子是否支持?
-
【CSDN 编者按】在嵌入式环境中,C、C++作为最常见的编程语言,早已被广泛应用在底层工具链、库中。不过,近日嵌入式工程师Omar Hiari提出一种完全不同的看法,他认为一开始就考虑安全问题的编程语言Rust才是嵌入式领域的未来。虽然要将嵌入式应用程序代码迁移到一种新的编程语言上非常麻烦,但他认为这是可行的,只是需要一些方法和实践。软件故障在嵌入式领域时常发生,随着物联网(IoT)和网络物理系统(CPS)等技术的普及,这些故障也没有得到应有的重视。当人们谈论自动驾驶汽车、机器人、无人机时,他们总认为这些出错的机率很小。但在了解一些历史上著名的软件事故、Bug案例研究后,你就不会这么想了。重现问题简直是一场噩梦首先,需要声明的是,我主要从事汽车嵌入式领域。此外,在职业生涯中,我还接触到了功能安全领域。功能安全,简单来说,是指一个系统或是设备整体安全的组成部分。其达成安全性的方式是靠系统或组成零件在接受输入讯号后,可以正常的动作,来减少导致人类受伤的事故风险。例如一个马达中加装温度感测器,若温度超过一定值,即停止马达运转,此机能就属于功能安全。在进入到汽车行业这些年,我担任过几个项目的技术负责人,其中部分职责会涉及到处理客户退货的问题。之所以这些汽车会被退回,大概率是由于电子单元或模块出现了问题,而我们团队需要确定导致该问题的根本原因。如果问题的根源确认是模块中出现了一个错误,我们会针对这个错误提出一个修复方案,并且将它整合到未来的更新中。有时我们会花费几天,甚至是几周来找出Bug的原因。我现在还记得我第一次处理这些问题的的经历,那时我还是一个做模块测试的实习生。我们发现,有几个被退回的模块,在现场测试时出现了不一样的结果。有些能够正常工作,有些不能。在和软件团队调试了很长时间后,我们终于找出了问题所在,原来是因为一个未初始化的变量!我惊讶于这样的错误竟然没有被检测出来。随着在这个行业不断地成长,我所遇到的问题越来越复杂。有时,我们会在成千上万的模块中找一个有问题的模块。通常情况下,第一步是尝试复现这种问题,以便用调试工具追踪问题的来源。这简直是一场噩梦!复现某些行为需要几天或者几周的时间,有时甚至需要更长的时间,因为要复现这个问题,需要非常具体的实现过程相组合。在大多数情况下,这些实现过程会存在一些未经测试的配置,最终导致触发Bug。正常导致汽车故障的大多数问题本质上出在软件上。有趣的是,即使是在一些质量至上,而且拥有大量经验丰富软件工程师的公司也会出现这种情况。基本上,这些遇到现场问题的模块,已经进行了彻底的测试、代码审查、代码标准的实施(例如MISRA-C)。一开始就考虑安全问题?后来,在从汽车嵌入式领域进入功能安全领域时,我发现我的经历更加有趣。在功能安全方面,像工业IEC-61508和汽车ISO-26262这样的标准在汽车行业开始流行起来。遵循这些标准的目的是为了减少产品故障的风险,风险的大小取决于产品的使用方式或者地点。通过在开发过程中加入更多的测试和检查,可以让产品达到一定的安全水平。以ISO-26262为例,该标准对软件和硬件中可能发生的故障类型进行了分类。硬件的故障被分为两种类型:随机硬件故障和系统故障。在软件方面,只有一种类型,即系统性故障。根据ISO-26262,随机硬件故障被定义为:在硬件元素的生命周期中,可能会发生不可预测的故障,并且遵循概率分布。该标准还补充说:随机硬件故障率可以以合理的精度进行预测。而系统性故障被定义为:故障以确定的方式与某种原因相关,只能通过改变设计或制造过程、操作程序、文件或其他相关因素来消除。这意味着,系统故障或多或少源于人为错误,并不是真正可以预测的。因此,如果我们考虑修复软件问题,就意味着需要更多额外的检查。当然,在应用程序中也可以添加一些方法让其自己检查(例如N-版本编程),尽管如果把这些方法添加到动态的应用软件代码中,消耗的内存空间会增多。鉴于我前面提到的,人们可能会认为这种变化是喜闻乐见的。但情况恰恰相反,大多数工程师都会反对整合功能安全流程。事实上,如何让工程师和团队接受这一点,让许多管理安全开发者感到很头疼。这种推动力通常来自于产业链的顶端,即建立一种 “安全文化”。我认为,部分原因是由于流程和额外的文件或者可交付成果方面的重大变化,而不一定是因为个人反对安全的想法本身。(如果有什么是是工程师不喜欢做的,我想写文件一定排在首位。)还有一点也让管理安全开发者感到头疼,即说服团队,让他们知道产品需要从一开始就以安全前提来设计。这意味着,工程师不应该只致力于让现有产品开始应用其功能来满足安全要求。换句话说,就是对现有产品进行修补以使其满足安全要求。这主要是针对硬件和应用软件来说。让我感到不解的是,为什么这一项不能应用在编译语言或者编译器上呢?与编译器一起使用的语言并没有从一开始就考虑到安全问题。相反,它们是根据功能安全应用中的标准准则进行修改的。比如创建语言的”子集“,删除其中被认为不安全的部分。这意味着排除了编程语言结构中存在的不安全因素。迫在眉睫的问题对于一些了解甚少的人来说,在汽车领域最普遍的编程语言是C。但从我的亲身经历来看,我认为C、C++并不是能够引领未来应用趋势的语言。事实上,从长远来看,坚持使用C、C++,不管有多少标准引入都令我感到担忧。毫无疑问,C、C++是强大的语言,但与即将到来的应用程序相比,目前的应用程序还是太简单了。此外,随着行业的发展,工程师的水平会参差不齐。所以无论流程多么严格,出现系统性错误的可能性都会越来越大。随着经验的不断积累,实践不断增加,以及在C、C++这样的语言中不断增加补丁,但是类似的问题仍然是由于系统性错误而产生,是不是至少应该考虑切换到另一种思路来设计语言,即一开始就把安全性作为前提来设计?又或者只是创造出一种更加现代的语言,正如系统性故障所定义的一样:只能通过改变设计来消除问题。一条可能的前进道路我在Rust中找到了一条可能通往嵌入式的道路,它似乎就是我在这个行业多年来所要找的。Rust的设计初衷是为了成为一种安全的语言。当然,要把嵌入式程序代码迁移到一种新的编程语言上是非常麻烦的。显然,阻碍因素之一是在汽车等嵌入式环境中,C、C++的工具链和库已经根深蒂固了,早已成为了生态系统中的一部分。然而,虽然代码迁移有困难,但也不是不可能,我们可以制定计划,循环渐进地进行切换。在非嵌入式环境中,Rust已经获得了相当大的知名度,并且得到了亚马逊、Discord、Dropbox、Facebook、谷歌和微软等公司的投资。事实上,微软和谷歌已经为Rust在某些领域能够消除70%的安全问题做了担保。到目前为止,关于嵌入式,某些团体已经有了一些有趣的动向。有一个Rust嵌入式工作组正在社区内工作,以弥合与Rust团队的差距,同时发展嵌入式生态系统。该小组在发展生态系统方面的速度令人印象深刻。(如果对这个工作小组的成就感兴趣可以访问这个网站:https://www.autosar.org/news-events/details/autosar-announces-new-working-group-for-programming-language-rust-in-automotive-software-context-202/)另一个是Ferrous Systems,它在支持Rust生态系统方面也做了大量工作。Ferrous在为Rust创建不同的工具和扩展方面做出了重大努力,并且正在ferrocene项目下为ISO26262认证Rust编译器工具链。有趣的是,在我写这篇文章的时候,AUTOSAR(AUTomotive Open System ARchitecture汽车开放系统架构)也宣布了一个在新的汽车背景下的Rust工作小组。(感兴趣可以访问这个链接:https://www.autosar.org/news-events/details/autosar-announces-new-working-group-for-programming-language-rust-in-automotive-software-context-202/)(补充:我不属于也没有参与过上述任何实体)Rust可能是嵌入式未来市场方向表明,我们开始从C、C++向更安全、更现代的编译型编程语言转变的时机到了。(并不是对于所有的应用,只是对于那些C或者C++可能出现问题的应用)Rust编程语言虽然相当年轻,但似乎是最适合这种情况的。对于公司和个人来说,使用Rust编程可能是一个很好的战略决定,能够在未来获得优势。对于个人来说,即使像Rust这样的语言永远不会被采用,但它至少会给个人一个全新的视角,让他知道如何成为一个更好的C/C++开发者。原文链接:https://apollolabsblog.hashnode.dev/why-you-should-be-worried-about-the-future-of-cc-in-embedded-a-case-for-rust
-
众所周知,C 是一种被广泛使用的语言,从操作系统内核到加密算法的编写,到处都在使用 C,它在多个领域发挥着重要作用。不久前,国外一位 Swift 和 Rust 专家 Aria Beingessner 在其文章《 C 不再是一种编程语言》中说,如今 C 不再只是一种编程语言,而成了每一种通用编程语言都需要遵守的协议,它具有绝对的统治地位。不过近日 The Register 网站的作者 Liam Proven 却在其发表的关于 C 的文章中表示,古往今来有很多不涉及 C 的编程语言和操作系统都发展得不错:“并不是所有东西都基于 C。在 C 出现之前,许多当前流行的商业操作系统就已经出现了,还有一些用新旧语言编写的操作系统,也基本不涉及 C 语言。”ALGOL 语言ALGOL(ALGOrithmic Language),计算机发展史上首批清晰定义的高级语言,在20世纪60年代,ALGOL 影响力巨大,是大多数现代指令性语言的鼻祖。美国 Burroughs 公司曾围绕 ALGOL 高级语言编写操作系统以及应用程序,开创性地设计了一系列大型机,即 Burroughs 大型系统。其中第一台大型机 B5000 于 1961 年推出,所使用的操作系统为 Burroughs 公司在 1916 年引入的 MCP(Master Control Program)大型机操作系统。(MCP 最初是于 1961 年用 ESPOL 语言编写的,而 ESPOL 为 ALGOL 60 的超集。 )在众多商用操作系统中,MCP 是一款为数不多仍然沿用至今的“古老”操作系统,目前主要应用在 Unisys ClearPath/MCP。ClearPath MCP 当前的版本是 20.0,于2021年5月发布。PASCAL 语言及其“后裔”Pascal 语言是由瑞士计算机科学家 Niklaus Wirth 在上世纪六十年代末所设计的。Pascal 语法严谨,一出世就受到广泛欢迎,迅速地从欧洲传到美国。Pascal 语言是 Apple Lisa(苹果公司发布的世界首台图形界面计算机)和早期 Mac 开发使用的高级语言,此外最初 Macintosh 操作系统的部分也是从 Pascal 源代码手工翻译成 Motorola 68000 汇编语言的。不少人眼中的 Pascal 语言是一种并不流行的语言,但实际上 NOI(全国奥林匹克信息学竞赛)将 Pascal 、C 与 C++ 作为竞赛使用的程序设计语言, 同时 IOI(国际信息学奥林匹克竞赛)也将 Pascal 作为三种程序设计语言之一——事实上 Pascal 并没有大家想的那么不受欢迎。20 世纪 80 年代,Wirth 为操作系统和应用程序专门设计了 Modula-2 语言。目前,世界上已经开发了近百个 Modula-2 编译系统。欧洲、加拿大、澳大利亚等不少大学已经用 Modula-2 代替 Pascal 语言作为计算机科学系本科生的第一门程序设计课。此外,Wirth 设计的 Oberon 操作系统也具有重要的价值。如今,Oberon 仍有多个项目正在运行中,如 Project Oberon 项目,该项目在基于 FPGA(现场可编程逻辑门阵列)的现代硬件上运行一个现代化的操作系统版本;还有 Native Oberon 项目,在 x86-32 PC 和 QEMU(一款开源的模拟器及虚拟机监管器)下运行。C 的“后裔”以上所讲的与 C 并无关联的编程语言还是有些晦涩难懂,下面可以看一看 C 的“后裔”如今发发展如何。C++首先是 C++。虽然 Linux 之父曾多次表明自己不喜欢 C++ ,还将其批得一无是处:“C++ 是一门很糟糕的语言”、“很多不合格的程序员都在使用它” 、”C++ 最后做出来的就是一堆可怕且难以维护的垃圾”…但 C++ 依旧是目前非常受欢迎的一种语言。由 C 扩展升级而产生的 C++,拥有计算机运行的实用性特征,同时还致力于提高大规模程序的编程质量与程序设计语言的问题描述能力。像是曾经辉煌的 Symbian 系统(塞班公司为手机而设计的操作系统)和目前流行的开源数据库 MySQL 等等,都基于 C++ 语言设计。C#C# 则是由 C 和 C++ 衍生出来的一种安全稳定、简单优雅的面向对象编程语言,在继承 C 和 C++ 强大功能的同时去掉了一些它们的复杂特性。它凭借其强大的操作能力以及优雅的语法风格等条件成为 .NET(用于构建多种应用的免费开源开发平台)开发的首选语言。C# 是兼顾系统开发和应用开发的实用语言,因此被很多人认为其很有可能成为编程语言历史上的第一个“全能”型语言。Rust事实上,近几年来有关 C 是否会被 Rust 语言取代的话题一直层出不穷,其中被讨论最多的应该就是 Rust 能否成为继 C 语言之后 Linux 内核的第二官方语言。作为上线时间并不是很久的 Rust 语言目前已经受到了足够的重视,从微软探索将 Rust 作为 C 和 C++ 的安全替代方案开始,Rust 吸引了越来越多的目光:内核维护者表示愿意接受用 Rust 开发 Linux 驱动;AWS(亚马逊 Web 服务)也伸出橄榄枝,宣布赞助 Rust……“C 不等于整个世界”虽然 C 的世界是吸引人的,但同时 C 以外的世界也很精彩,作者 Liam Proven 在其文章的最后表示:“我希望这些例子可以说明一点:操作系统不以 C 开头和结尾,C 也不等于整个世界。”不少阅读完 Liam Proven 文章的网友都表达了自己的一些看法,一部分网友对 Liam Proven 的观点表示赞同:网友@Jonathan Knight:“我认为,以 C 语言为中心的操作系统观点真的只对年轻人有效。”网友@bazza:“上世纪60、70年代,在众多操作系统/语言的竞争中必须要有一种获得胜利,只是碰巧它是 C 和 UNIX 。”也有一部分网友对 Liam Proven 的观点表示怀疑,认为如今再提 ALGOL 和 Pascal 已没有意义:网友@R Soul:“这篇文章就相当于在鼠疫时代,一个推着手推车的人到处说‘把你的死人带出来,把你的死人带出来’。”那么,你对于非 C 操作系统之外的世界有什么看法呢?原文链接:https://blog.csdn.net/csdnnews/article/details/123988894
-
需要使用C++的thread库,请问可以在CMakeLists中加-pthread吗
-
按照文档说明排查了:一、echo $? 本地结果为0二、内存 占用14M三、打包 使用的是CodeCraft_zip.sh,压缩,确认压缩的是./CodeCroft-2022/,内容为: ./CodeCroft-2022/src/CodeCraft-2022.cpp ./CodeCraft-2022/src/CMakeLists.txt四、可执行文件 名字为CodeCraft-2022五、输出文件 为根目录/output/solution.txt
-
如图,我在c++源码里面和终端都测试了echo $?,结果都是0,但上传就显示程序运行错误检查了我怀疑可能是内存超标,但我查看内存只占用了14M还有哪些可以排查的?
-
【功能模块】【操作步骤&问题现象】1、2、【截图信息】【日志信息】(可选,上传日志内容或者附件)
-
【功能模块】队伍名:我们是第一名对吗? 提交时间:2022-03-23 16:28:16【操作步骤&问题现象】1、2、【截图信息】【日志信息】(可选,上传日志内容或者附件)
-
这个是linux运行./build_and_run.sh的截图这个是运行失败的截图求求各位大佬了。。。。
-
【功能模块】团队名称:708-03【操作步骤&问题现象】1、2、【截图信息】【日志信息】(可选,上传日志内容或者附件)
-
【功能模块】【操作步骤&问题现象】1、2、【截图信息】【日志信息】(可选,上传日志内容或者附件)
推荐直播
-
HDC深度解读系列 - Serverless与MCP融合创新,构建AI应用全新智能中枢2025/08/20 周三 16:30-18:00
张昆鹏 HCDG北京核心组代表
HDC2025期间,华为云展示了Serverless与MCP融合创新的解决方案,本期访谈直播,由华为云开发者专家(HCDE)兼华为云开发者社区组织HCDG北京核心组代表张鹏先生主持,华为云PaaS服务产品部 Serverless总监Ewen为大家深度解读华为云Serverless与MCP如何融合构建AI应用全新智能中枢
回顾中 -
关于RISC-V生态发展的思考2025/09/02 周二 17:00-18:00
中国科学院计算技术研究所副所长包云岗教授
中科院包云岗老师将在本次直播中,探讨处理器生态的关键要素及其联系,分享过去几年推动RISC-V生态建设实践过程中的经验与教训。
回顾中 -
一键搞定华为云万级资源,3步轻松管理企业成本2025/09/09 周二 15:00-16:00
阿言 华为云交易产品经理
本直播重点介绍如何一键续费万级资源,3步轻松管理成本,帮助提升日常管理效率!
回顾中
热门标签