一、keil5单片机编译器在哪里?
keil5编译按钮在:Debug--找到图中所示按钮即为单步运行。
分别由C51及C51编译器编译生成目标文件(.obj)。目标文件可由LIB51创建生成库文件,也可以与库文件一起经L51连接定位生成绝对目标文件(.abs)。abs文件由OH51转换成标准的hex文件,以供调试器dScope。
KeilC51单片机软件开发系统的整体结构:
C51工具包的整体结构,μVision与Ishell分别是C51forWindows和forDos的集成开发环境(IDE),可以完成编辑、编译、连接、调试、仿真等整个开发流程。开发人员可用IDE本身或其它编辑器编辑C或汇编源文件。
然后分别由C51及C51编译器编译生成目标文件(.obj)。目标文件可由LIB51创建生成库文件,也可以与库文件一起经L51连接定位生成绝对目标文件(.abs)。
abs文件由OH51转换成标准的hex文件,以供调试器dScope51或tScope51使用进行源代码级调试,也可由仿真器使用直接对目标板进行调试,也可以直接写入程序存贮器如EPROM中。
二、keiluvision4怎样添加c51
重新安装C51版本的就行了。去我的共享空间下载安装包,就是C51版本的,里面有注册机,安装完后,计算出注册码就可以注册了。
编译器开发语言选择:Rust还是OCaml
作者|Hirrolot责编|明明如月
责编|夏萌
出品|CSDN(ID:CSDNnews)
关于如何选择最合适的编程语言来开发编译器,这个话题在编程语言爱好者中经常引起热议。具体可参考以下讨论:链接1、链接2和链接3。遗憾的是,许多人的回答要么局限于自己偏爱的语言,没有提供合理解释,要么给出模糊的解释却缺乏具体的例证。这两种回答对提问者来说几乎没有任何帮助。在本文中,我将尝试通过比较两种语言——Rust和OCaml,来对这个话题提供更详细的阐述。
CPS转换
在阐述我的具体观点之前,我会展示一个非常简单的语言的CPS(延续传递风格)转换的两个相似实现,并不做任何结论性陈述。这一通用方法源于AndrewW.Appel的“CompilingwithContinuations”。即使你对这个概念不太了解,也不必担心;你只需要关注Rust和OCaml中是如何具体实现这个理念的。
以下是用Rust编写的CPS转换:
代码包括注释和空行共145行。
同样的算法用地道的OCaml编写:
代码包括注释和空行总共有74行。这比Rust版本短了约2.0倍。
比较两种实现
开发的核心特点涵盖了:
大量使用递归定义的数据结构。
复杂的数据转换流程。
下面简要概述Rust和OCaml在这两方面的处理差异:
递归数据结构:
OCaml:直接支持递归数据结构。
Rust:递归数据结构的实现需要借助Rc和Box将TermTree和CpsTerm的递归结构进行封装。
复杂数据转换:
OCaml:
a、递归广泛应用,拥有尾调用和“TailModuloConstructor(TMC)”优化。
b、模式匹配的实现便捷高效,无需额外缩进和复杂的参数描述。
c、标准数据结构主要为不可变类型,有助于代码理解。
Rust:
a、递归使用较少,且尾递归并不保证。
b、模式匹配的实现相对复杂,涉及额外的缩进和参数详述。
c、标准数据结构大多为可变类型,倾向于使用命令式风格,需要进行迭代操作才能实现流水线编程。
与OCaml相比,Rust的语法更冗长。作为一门无垃圾回收的语言,它要求开发者必须精确处理内存管理。这确实增加了对执行过程的透明度,但对理解算法本身的帮助却不明显。
在Rust中,修改变量或数据结构的值也相对复杂:
与之相比,在OCaml中比较简单:
为何选择RefCell而非mutu32?因为Rust强制在任何时刻只允许一个可变引用指向特定值。尽管在多线程代码中这是合理的,但在单线程的算法中,我们需用RefCell来绕过这个限制。
另外,Rust中convert_list的实现也值得注意。由于fn本质上只是代码指针,所以我们每次调用go都必须明确传递gen和finish,导致了变量类型的重复声明。与之相对,OCaml则会自动捕获gen和finish。
虽然上述算法不算特别复杂,但已经足以体现ML家族语言在编程方面的便利性。下面,我们将深入探讨两种语言类型系统的更多细节。
类型安全性:GADTs
与Rust相比,OCaml的类型系统通常更具表现力,并且在资源管理之外具有更多优势。例如,OCaml支持广义代数数据类型(GADTs),以强化数据结构的某些不变性。考虑一种包含布尔值、整数及其操作的对象语言,其描述如下:
那么,我们该如何编写该语言的求值器呢?以下是一个可能的解决方案:
虽然该解决方案相对简单,但并不够稳健。如果对eval的输入类型不合适会怎样呢?以下示例说明了问题:
程序会因为“And运算符的第二操作数必须为布尔值”而出现问题。
为了避免此类错误,我们可以在OCaml中采用GADTs:
现在,尝试构造一个不合适的类型会是什么情况呢?
类型检查不会通过!
之所以会这样,是因为我们在term的定义中实质上编码了对象语言的类型系统。OCaml知道And只接受布尔类型的项,而不是整数类型。在实际应用场景中,我们可以有一个无限制的、类似Rust的Term项,该项是解析生成的,并可进一步详细阐述为合适的GADT风格的term。无论采用eval还是compile,后者均可被处理。
类型灵活性:First-ClassModules
OCaml中具备一项Rust所不具备的独特功能:First-ClassModules。First-ClassModules允许模块存储于变量、作为参数传递,甚至可以从常规函数返回。假设你的目标语言涵盖了各种固定大小的整数,如i8/u8、i16/u16等,那么你可以通过OCaml中的(常规)模块来表示这些类型:
fixed_ints.mli
在抽象语法树(AST)中,整数值可以按照以下方式表示:
那么,在存在诸多算术运算符add/sub/mul/div/rem和多种类型的操作数时,该如何高效地实现评估呢?
解决方案如下:
定义函数pair_exn,将两个generic映射为一个一等模块Pair。
为特定的整数对定义模块Pair,并实现S。
定义函数do_int_bin_op,接收Pair作为参数,并执行整数对上的操作op。
从eval中调用do_int_bin_op。
在OCaml中:
fixed_ints.mli
pair的实现如下:
fixed_ints.ml
现在,我们可以按如下方式编写eval:
extract_int_exn取出一个值,并提取一个整型generic,如果该值不是整数则抛出异常。
最后,do_int_bin_op定义如下:
S.to_value将类型化的整数转换回持有generic的值。
通过借助First-ClassModules,我们能够在无需过多样板代码的前提下实现固定大小整数的评估。而在Rust中的最佳实践则是使用macro_rules!。然而,该方法因其难以理解的语法、与编程语言其他部分集成不深入,以及较差的IDE支持而备受诟病。
结束语
虽然Rust在资源管理方面表现优秀,但OCaml更适合于编译器的开发。这其中涉及许多引人注目的特性,如多态变体、自定义绑定操作符和EffectHandlers。得益于完全静态且灵活的类型系统,OCaml在历史上已广泛应用于众多项目,包括Frama-C工具链、Coq定理证明器,以及Rust编译器的早期版本。
然而,OCaml也不是完美的。OCaml的标准库和整体生态系统与Rust相比显得略逊一筹。在OCaml中直接使用Rust的完整固定大小整数集合并不可行。不过,通过整合原生OCaml整数、Int32、Int64模块和CFFI,可以达到同样的效果。(专业提示:避免使用[ocaml-stdint](https://github.com/andrenth/ocaml-stdint),截至2023年8月6日,该库未维护且存在多个错误。[ocaml-integers](https://github.com/yallop/ocaml-integers)是更稳定的选择,但缺乏Int8、Int16和128位整数的支持,并在文档方面有所不足。)
随着Rust的日益普及,更多的开发者开始在GitHub上使用它来启动编译器项目。我认为,如果你试图借助Rust编写大量编译器来深入学习Rust,或者确切了解自己的目标,这可能是明智之举。如果你主要关注的是编译器开发,那么OCaml将能够为你节省大量时间和精力。
其他值得考虑的选项包括Haskell和各类Lisp方言。如果你已经熟练掌握了Haskell(对此我同时表示祝贺和哀悼),那么仅为了编写编译器而学习OCaml可能是不必要的。如果你尚未掌握Haskell,OCaml可能是更容易上手的选择。尽管Lisps极具灵活性,但由于它们通常缺少静态类型安全性,运行时错误可能成为一个棘手问题。
参考文献
CPS是编译器StandardMLofNewJersey的核心表示形式。
代码和附带的测试可在此处访问。
选择Rc是为了减轻在某些地方昂贵的克隆TermTree的负担。
从严格意义上讲,OCaml中的所有函数都是柯里化(Currying)的,因此function只是定义了一个单一参数的函数,并在其上进行模式匹配。
在此处闭包未能提供解决方案,因为它们不能递归调用,至少在未进行一些复杂操作之前不能调用。
广大网友激烈讨论
网友们围绕Rust和OCaml的优劣以及如何选择展开了激烈讨论。
关于Rust和OCaml优劣对比。有些网友认为Rust的优点在于其内存安全性和性能,这使得它在系统编程和高性能计算中非常有用。然而,Rust的学习曲线相对较陡,可能需要更多的时间和精力去掌握。有网友表示,OCaml在函数式编程和类型推断方面非常强大,它的语法简洁,易于学习。然而,OCaml的生态系统相对较小,可能没有Rust那么多的库和工具可供选择。也有网友认为,Rust和OCaml都有一些独特的特性,如Rust的所有权系统和OCaml的模式匹配,这些特性在编译器开发中可能非常有用。
关于如何选择。有网友认为,如果项目的主要目标是快速开发和原型设计,那么OCaml可能是更好的选择。如果项目需要处理复杂的并发和内存管理问题,那么Rust可能更适合。也有网友认为,Rust和OCaml都是优秀的编程语言,但在编译器开发中,它们各有优势和劣势,选择编程语言不仅仅是技术问题,还涉及到团队动力、项目管理和人力资源等多个方面。因此,选择哪种语言需要综合考虑多个因素。
参考链接
链接1:https://www.reddit.com/r/ProgrammingLanguages/comments/k3zgjy/which_language_to_write_a_compiler_in/
链接2:https://www.reddit.com/r/ProgrammingLanguages/comments/13eztdp/good_languages_for_writing_compilers_in/
链接3:https://www.reddit.com/r/ProgrammingLanguages/comments/15gz8rb/how_good_is_go_for_writing_a_compiler/
CPS(延续传递风格:https://en.wikipedia.org/wiki/Continuation-passing_style
“CompilingwithContinuations”:https://www.amazon.com/Compiling-Continuations-Andrew-W-Appel/dp/052103311X
“TailModuloConstructor(TMC)”:https://v2.ocaml.org/manual/tail_mod_cons.html
并不保证:https://stackoverflow.com/questions/59257543/when-is-tail-recursion-guaranteed-in-rust
广义代数数据类型(GADTs):https://v2.ocaml.org/manual/gadts-tutorial.html
First-ClassModules:https://dev.realworldocaml.org/first-class-modules.html
多态变体:https://v2.ocaml.org/releases/4.14/htmlman/polyvariant.html
自定义绑定操作符:https://v2.ocaml.org/manual/bindingops.html
EffectHandlers:https://v2.ocaml.org/manual/effects.html
Frama-C工具链:https://frama-c.com/
Coq定理证明器:https://coq.inria.fr/
此处:https://gist.github.com/Hirrolot/d16dc5e78639db6e546b5054afefd142