是否有任何理由使用 C 而不是 C++ 进行嵌入式开发?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/812717/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Is there any reason to use C instead of C++ for embedded development?
提问by Piotr Czapla
Question
题
I have two compilers on my hardware C++ and C89
我的硬件 C++ 和 C89 上有两个编译器
I'm thinking about using C++ with classes but without polymorphism (to avoid vtables). The main reasons I'd like to use C++ are:
我正在考虑将 C++ 与类一起使用但没有多态性(以避免 vtables)。我想使用 C++ 的主要原因是:
- I prefer to use “inline” functions instead of macro definitions.
- I'd like to use namespaces as I prefixes clutter the code.
- I see C++ a bit type safer mainly because of templates, and verbose casting.
- I really like overloaded functions and constructors (used for automatic casting).
- 我更喜欢使用“内联”函数而不是宏定义。
- 我想使用命名空间,因为我给代码添加前缀。
- 我认为 C++ 的类型更安全,主要是因为模板和详细的转换。
- 我真的很喜欢重载的函数和构造函数(用于自动转换)。
Do you see any reason to stick with C89 when developing for very limited hardware (4kb of RAM)?
在为非常有限的硬件(4kb 的 RAM)开发时,您是否有理由坚持使用 C89?
Conclusion
结论
Thank you for your answers, they were really helpful!
谢谢你的回答,他们真的很有帮助!
I thought the subject through and I will stick with C mainly because:
我想通了这个主题,我会坚持使用 C,主要是因为:
- It is easier to predict actual code in C and this is really important if you have only 4kb of ram.
- My team consists mainly of C developers, so advanced C++ features won't be frequently used.
- I've found a way to inline functions in my C compiler (C89).
- 在 C 中预测实际代码更容易,如果您只有 4kb 的内存,这非常重要。
- 我的团队主要由 C 开发人员组成,因此不会经常使用高级 C++ 功能。
- 我找到了一种在 C 编译器 (C89) 中内联函数的方法。
It is hard to accept one answer as you provided so many good answers. Unfortunately I can't create a wiki and accept it, so I will choose one answer that made me think most.
很难接受一个答案,因为您提供了这么多好的答案。不幸的是,我无法创建一个 wiki 并接受它,所以我会选择一个让我思考最多的答案。
采纳答案by Steve Melnikoff
Two reasons for using C over C++:
使用 C 而不是 C++ 的两个原因:
- For a lot of embedded processors, either there is no C++ compiler, or you have to pay extra for it.
- My experience is that a signficant proportion of embedded software engineers have little or no experience of C++ -- either because of (1), or because it tends not to be taught on electronic engineeering degrees -- and so it would be better to stick with what they know.
- 对于很多嵌入式处理器,要么没有C++编译器,要么需要额外付费。
- 我的经验是,很大一部分嵌入式软件工程师很少或根本没有 C++ 经验——要么是因为 (1),要么是因为它往往不教授电子工程学位——所以最好坚持使用他们知道什么。
Also, the original question, and a number of comments, mention the 4 Kb of RAM. For a typical embedded processor, the amount of RAM is (mostly) unrelated to the code size, as the code is stored, and run from, flash.
此外,原始问题和一些评论提到了 4 Kb 的RAM。对于典型的嵌入式处理器,RAM 的数量(大部分)与代码大小无关,因为代码是在闪存中存储和运行的。
Certainly, the amount of code storage space is something to bear in mind, but as new, more capacious, processors appear on the market, it's less of an issue than it used to be for all but the most cost-sensitive projects.
当然,代码存储空间的数量是需要牢记的,但随着市场上出现新的、更大容量的处理器,除了对成本最敏感的项目之外,它不像过去那样成为问题。
On the use of a subset of C++ for use with embedded systems: there is now a MISRA C++standard, which may be worth a look.
关于将 C++ 的子集用于嵌入式系统:现在有一个MISRA C++标准,可能值得一看。
EDIT:See also this question, which led to a debate about C vs C++ for embedded systems.
编辑:另见这个问题,这导致了关于嵌入式系统的 C 与 C++ 的辩论。
回答by RBerteig
For a veryresource constrained target such as 4KB of RAM, I'd test the waters with some samples before committing a lot of effort that can't be easily ported back into a pure ANSI C implementation.
对于资源非常有限的目标,例如 4KB 的 RAM,我会先用一些样本进行测试,然后再进行大量无法轻松移植回纯 ANSI C 实现的工作。
The Embedded C++ working group did propose a standard subset of the language and a standard subset of the standard library to go with it. I lost track of that effort when the C User's Journal died, unfortunately. It looks like there is an article at Wikipedia, and that the committeestill exists.
嵌入式 C++ 工作组确实提出了该语言的标准子集以及与之配套的标准库的标准子集。不幸的是,当 C User's Journal 去世时,我忘记了这项工作。它看起来像有在文章维基百科,以及该委员会仍然存在。
In an embedded environment, you really have to be careful about memory allocation. To enforce that care, you may need to define the global operator new()
and its friends to something that can't be even linked so that you know it isn't used. Placement new
on the other hand is likely to be your friend, when used judiciously along with a stable, thread-safe, and latency guaranteed allocation scheme.
在嵌入式环境中,您确实必须小心内存分配。为了加强这种关心,您可能需要将全局operator new()
及其朋友定义为甚至无法链接的东西,以便您知道它没有被使用。new
另一方面,当与稳定、线程安全和延迟保证的分配方案一起明智地使用时,放置可能是您的朋友。
Inlined functions won't cause much problem, unless they are big enough that they should have been true functions in the first place. Of course the macros their replacing had that same issue.
内联函数不会造成太大问题,除非它们足够大以至于它们本来就应该是真正的函数。当然,他们替换的宏也有同样的问题。
Templates, too, may not cause a problem unless their instantiation runs amok. For any template you do use, audit your generated code (the link map may have sufficient clues) to make certain that only the instantiations you intended to use happened.
模板也可能不会导致问题,除非它们的实例化运行异常。对于您使用的任何模板,审核您生成的代码(链接图可能有足够的线索)以确保仅发生您打算使用的实例化。
One other issue that may arise is compatibility with your debugger. It isn't unusual for an otherwise usable hardware debugger to have very limited support for interaction with the original source code. If you effectively must debug in assembly, then the interesting name mangling of C++ can add extra confusion to the task.
可能出现的另一个问题是与调试器的兼容性。其他可用的硬件调试器对与原始源代码交互的支持非常有限,这并不罕见。如果您必须有效地在汇编中进行调试,那么 C++ 有趣的名称修改可能会给任务增加额外的混乱。
RTTI, dynamic casts, multiple inheritance, heavy polymorphism, and exceptions all come with some amount of runtime cost for their use. A few of those features level that cost over the whole program if they are used, others just increase the weight of classes that need them. Know the difference, and choose advanced features wisely with full knowledge of at least a cursory cost/benefit analysis.
RTTI、动态转换、多重继承、重多态性和异常都伴随着一定的运行时成本。如果使用这些功能,其中一些功能级别会超过整个程序的成本,其他功能只会增加需要它们的类的权重。了解差异并明智地选择高级功能,并至少了解粗略的成本/收益分析。
In an small embedded environment you will either be linking directly to a real time kernel or running directly on the hardware. Either way, you will need to make certain that your runtime startup code handles C++ specific startup chores correctly. This might be as simple as making sure to use the right linker options, but since it is common to have direct control over the source to the power on reset entry point, you might need to audit that to make certain that it does everything. For example, on a ColdFire platform I worked on, the dev tools shipped with a CRT0.S module that had the C++ initializers present but comment out. If I had used it straight from the box, I would have been mystified by global objects whose constructors had never run at all.
在小型嵌入式环境中,您将直接链接到实时内核或直接在硬件上运行。无论哪种方式,您都需要确保您的运行时启动代码正确处理 C++ 特定的启动杂务。这可能就像确保使用正确的链接器选项一样简单,但由于直接控制电源复位入口点的源是很常见的,因此您可能需要对其进行审核以确保它可以完成所有操作。例如,在我使用的 ColdFire 平台上,开发工具附带了一个 CRT0.S 模块,该模块具有 C++ 初始值设定项,但已注释掉。如果我直接从盒子里使用它,我会被那些构造函数从未运行过的全局对象所迷惑。
Also, in an embedded environment, it is often necessary to initialize hardware devices before they can be used, and if there is no OS and no boot loader, then it is your code that does that. You will need to remember that constructors for global objects are run beforemain()
is called so you will need to modify your local CRT0.S (or its equivalent) to get that hardware initialization done beforethe global constructors themselves are called. Obviously, the top of main()
is way too late.
此外,在嵌入式环境中,通常需要先初始化硬件设备,然后才能使用它们,如果没有操作系统和引导加载程序,那么执行此操作的是您的代码。您需要记住全局对象的构造函数在main()
调用之前运行,因此您需要修改本地 CRT0.S(或其等效项)以在调用全局构造函数本身之前完成硬件初始化。显然,顶部main()
为时已晚。
回答by Harper Shelby
No. Any of the C++ language features that could cause problems (runtime polymorphism, RTTI, etc.) can be avoided while doing embedded development. There is a community of embedded C++ developers (I remember reading columns by embedded developers using C++ in the old C/C++ Users' Journal), and I can't imagine they'd be very vocal if the choice was that bad.
不可以。在进行嵌入式开发时,可以避免任何可能导致问题的 C++ 语言功能(运行时多态性、RTTI 等)。有一个嵌入式 C++ 开发人员社区(我记得在旧的 C/C++ 用户杂志中阅读了使用 C++ 的嵌入式开发人员的专栏),我无法想象如果选择那么糟糕,他们会大声疾呼。
回答by leander
The Technical Report on C++ Performanceis a great guide for this sort of thing. Note that it has a section on embedded programming concerns!
关于 C++ 性能的技术报告是这类事情的一个很好的指南。请注意,它有一个关于嵌入式编程问题的部分!
Also, ++ on the mention of Embedded C++ in the answers. The standard is not 100% to my tastes, but it is a good bit of reference when deciding what parts of C++ you might drop.
此外,++ 在答案中提到了嵌入式 C++。该标准并非 100% 符合我的口味,但在决定可能放弃 C++ 的哪些部分时,它是一个很好的参考。
While programming for small platforms, we disable exceptions and RTTI, avoided virtual inheritance, and paid close attention to the number of virtual functions we have lying around.
在为小平台编程时,我们禁用异常和 RTTI,避免虚拟继承,并密切关注我们拥有的虚拟函数的数量。
Your friend is the linker map, though: check it frequently, and you'll spot sources of code and static memory bloat quickly.
不过,您的朋友是链接器映射:经常检查它,您将很快发现代码源和静态内存膨胀。
After that, the standard dynamic memory usage considerations apply: in an environment as restricted as the one you mention, you may want to not use dynamic allocations at all. Sometimes you can get away with memory pools for small dynamic allocs, or "frame-based" allocation where you preallocate a block and throw out the whole thing later.
之后,标准动态内存使用注意事项适用:在您提到的受限环境中,您可能根本不想使用动态分配。有时,您可以使用用于小型动态分配的内存池,或“基于帧”的分配,您可以预先分配一个块并在以后扔掉整个事情。
回答by arke
I recommend using the C++ compiler, but limiting your use of C++ specific features. You can program like C in C++ (the C runtime is included when doing C++, though in most embedded applications you don't make use of the standard library anyway).
我建议使用 C++ 编译器,但限制您使用 C++ 特定功能。您可以在 C++ 中像 C 一样编程(在执行 C++ 时包括 C 运行时,尽管在大多数嵌入式应用程序中您无论如何都不会使用标准库)。
You can go ahead and use C++ classes etc., just
您可以继续使用 C++ 类等,只需
- Limit your use of virtual functions (as you've said)
- Limit your use of templates
- For an embedded platform, you'll want to override the operator new and/or use placement new for memory allocation.
- 限制您对虚函数的使用(如您所说)
- 限制您使用模板
- 对于嵌入式平台,您将需要覆盖运算符 new 和/或使用放置 new 进行内存分配。
回答by Shing Wong
As a firmware/embedded system engineer, I can tell you guys some of the reason why C is still the #1 choice over C++ and yes, I'm fluent in both of them.
作为一名固件/嵌入式系统工程师,我可以告诉你们一些为什么 C 仍然是 C++ 的第一选择的原因,是的,我对它们都很流利。
1) Some targets we develop on has 64kB of RAM for both code and data, so you have to make sure every byte count, and yes, I've dealt with code optimization to save 4 bytes that cost me 2 hours, and that's in 2008.
1) 我们开发的一些目标有 64kB 的 RAM 用于代码和数据,所以你必须确保每个字节计数,是的,我已经处理了代码优化以节省 4 个字节,这花费了我 2 小时,这是在2008 年。
2) Every C library function is reviewed before we let them in the final code, because of size limitation, so we prefer people not to use divide (no hardware divider, so a big library is needed), malloc (because we have no heap, all memory is allocated from data buffer in 512 byte chunk and must be code reviewed), or other object oriented practice that carry large penalty. Remember, every library function that you use count.
2)每一个C库函数在我们把它们放进最终代码之前都要经过,因为大小限制,所以我们宁愿人们不要使用divide(没有硬件divider,所以需要一个大库),malloc(因为我们没有heap ,所有内存都是从 512 字节块中的数据缓冲区分配的,并且必须经过代码),或者其他面向对象的实践会带来很大的惩罚。请记住,您使用的每个库函数都会计数。
3) Ever heard of the term overlay? you have so little code space that sometimes you have to swap things out with another set of code. If you call a library function then the library function must be resident. If you only use it in an overlay function, you are wasting a lot of space relying on too many object oriented methods. So, don't assume any C library function, let alone C++ to be accepted.
3) 听说过叠加这个词吗?你的代码空间太少了,有时你不得不用另一组代码来交换。如果您调用库函数,则该库函数必须是常驻的。如果只在覆盖函数中使用它,那么依赖太多面向对象的方法会浪费大量空间。所以,不要假设任何 C 库函数,更不要说 C++ 会被接受。
4) Casting and even packing (where unaligned data structure crosses word boundary) are needed due to limited hardware design (i.e. an ECC engine that is wired a certain way) or to cope with a hardware bug. You cannot assume too much inplicitly, so why object orient it too much?
4) 由于有限的硬件设计(即以某种方式连接的 ECC 引擎)或处理硬件错误,需要强制转换甚至打包(其中未对齐的数据结构跨越字边界)。您不能隐含地假设太多,那么为什么要过多地面向对象呢?
5) Worst case scenario: eliminating some of the object oriented methods will force develop to think before they use resources that can explode (i.e. allocating 512bytes on a stack rather than from a data buffer), and prevent some of the potential worst case scenario that are not tested for or eliminate the whole code path all together.
5) 最坏情况:消除一些面向对象的方法将迫使开发者在使用可能爆炸的资源(即在堆栈上而不是从数据缓冲区中分配 512 字节)之前进行思考,并防止一些潜在的最坏情况没有测试或消除整个代码路径。
6) We do use a lot of abstraction to keep hardware from software and make code as portable as possible, and simulation friendly. Hardware access must be wrapped in a macro or inline function that are conditionally compiled between different platform, data type must be casted as byte size rather than target specific, direct pointer usage is not allowed (because some platform assume memory mapped I/O is the same as data memory), etc.
6)我们确实使用了很多抽象来使硬件与软件保持一致,并使代码尽可能可移植,并且模拟友好。硬件访问必须包含在不同平台之间有条件编译的宏或内联函数中,数据类型必须转换为字节大小而不是特定于目标,不允许直接使用指针(因为某些平台假设内存映射 I/O 是与数据存储器相同)等。
I can think of more, but you get the idea. Us firmware guys do have object oriented training, but the task of embedded system can be so hardware oriented and low level, that it is not high level or abstractable by nature.
我可以想到更多,但你明白了。我们固件人员确实接受过面向对象的培训,但是嵌入式系统的任务可以是面向硬件的低级任务,以至于它本质上不是高级或抽象的。
BTW, every firmware job I've been at uses source control, I don't know where you get that idea from.
顺便说一句,我从事的每项固件工作都使用源代码控制,我不知道你从哪里得到这个想法。
-some firmware guy from SanDisk.
- 一些来自 SanDisk 的固件专家。
回答by Rocketmagnet
My personal preference is C because :
- I know what every line of code is doing (and costs)
- I don't know C++ well enough to know what every line of code is doing (and costs)
我个人的偏好是 C,因为:
- 我知道每一行代码在做什么(和成本)
- 我不太了解 C++,无法知道每一行代码在做什么(和成本)
Why do people say this? You don'tknow what every line of C is doing unless you check the asm output. Same goes for C++.
为什么人们会这样说?你不知道什么C的每一行正在做,除非你检查ASM输出。C++ 也是如此。
For example, what asm does this innocent statement produce:
例如,这个无辜的语句产生了什么 asm:
a[i] = b[j] * c[k];
It looks fairly innocent, but a gcc based compiler produces this asm for an 8-bit micro
它看起来相当无辜,但是基于 gcc 的编译器为 8 位微机生成了这个 asm
CLRF 0x1f, ACCESS
RLCF 0xfdb, W, ACCESS
ANDLW 0xfe
RLCF 0x1f, F, ACCESS
MOVWF 0x1e, ACCESS
MOVLW 0xf9
MOVF 0xfdb, W, ACCESS
ADDWF 0x1e, W, ACCESS
MOVWF 0xfe9, ACCESS
MOVLW 0xfa
MOVF 0xfdb, W, ACCESS
ADDWFC 0x1f, W, ACCESS
MOVWF 0xfea, ACCESS
MOVFF 0xfee, 0x1c
NOP
MOVFF 0xfef, 0x1d
NOP
MOVLW 0x1
CLRF 0x1b, ACCESS
RLCF 0xfdb, W, ACCESS
ANDLW 0xfe
RLCF 0x1b, F, ACCESS
MOVWF 0x1a, ACCESS
MOVLW 0xfb
MOVF 0xfdb, W, ACCESS
ADDWF 0x1a, W, ACCESS
MOVWF 0xfe9, ACCESS
MOVLW 0xfc
MOVF 0xfdb, W, ACCESS
ADDWFC 0x1b, W, ACCESS
MOVWF 0xfea, ACCESS
MOVFF 0xfee, 0x18
NOP
MOVFF 0xfef, 0x19
NOP
MOVFF 0x18, 0x8
NOP
MOVFF 0x19, 0x9
NOP
MOVFF 0x1c, 0xd
NOP
MOVFF 0x1d, 0xe
NOP
CALL 0x2142, 0
NOP
MOVFF 0x6, 0x16
NOP
MOVFF 0x7, 0x17
NOP
CLRF 0x15, ACCESS
RLCF 0xfdf, W, ACCESS
ANDLW 0xfe
RLCF 0x15, F, ACCESS
MOVWF 0x14, ACCESS
MOVLW 0xfd
MOVF 0xfdb, W, ACCESS
ADDWF 0x14, W, ACCESS
MOVWF 0xfe9, ACCESS
MOVLW 0xfe
MOVF 0xfdb, W, ACCESS
ADDWFC 0x15, W, ACCESS
MOVWF 0xfea, ACCESS
MOVFF 0x16, 0xfee
NOP
MOVFF 0x17, 0xfed
NOP
The number of instructions produced depends massively on:
产生的指令数量很大程度上取决于:
- The sizes of a, b, and c.
- whether those pointers are stored on the stack or are global
- whether i, j and k are on the stack or are global
- a、b 和 c 的大小。
- 这些指针是存储在堆栈上还是全局的
- i、j 和 k 是否在堆栈上或全局
This is especially true in the tiny embedded world, where processors are just not set up to handle C. So my answer would be that C and C++ are just as bad as each other, unless you always examine the asm output, in which case they are just as good as each other.
在微小的嵌入式世界中尤其如此,在那里处理器没有设置为处理 C。 所以我的答案是 C 和 C++ 彼此一样糟糕,除非你总是检查 asm 输出,在这种情况下它们彼此一样好。
Hugo
雨果
回答by user21714
I have heard that some people prefer C for embedded work due to the fact that is simpler and therefore easier to predict the actual code that will be generated.
我听说有些人更喜欢 C 用于嵌入式工作,因为它更简单,因此更容易预测将生成的实际代码。
I personally would think writing C-style C++ (using templates for type-safety) would give you a lot of advantages though and I can't see any real reason not to.
我个人认为编写 C 风格的 C++(使用模板来保证类型安全)会给你带来很多好处,我看不出有什么真正的理由不这样做。
回答by C?t?lin Piti?
I see no reason to use C instead of C++. Whatever you can do in C, you can do it also in C++. If you want to avoid overheads of VMT, don't use virtual methods and polymorphism.
我认为没有理由使用 C 而不是 C++。无论您在 C 中可以做什么,您也可以在 C++ 中完成。如果你想避免 VMT 的开销,不要使用虚方法和多态。
However, C++ can provide some very useful idioms with no overhead. One of my favourites is RAII. Classes are not necessary expensive in terms of memory or performance...
然而,C++ 可以提供一些非常有用的习语而没有任何开销。我的最爱之一是 RAII。就内存或性能而言,类不一定很昂贵......
回答by GregC
I've written some code for ARM7 embedded paltform on IAR Workbench. I highly recommend relying on templates to do compile-time optimization and path prediction. Avoid dynamic casting like plague. Use traits/policies to your advantage, as prescribed in Andrei Alexandrescu's book, Modern C++ design.
我在 IAR Workbench 上为 ARM7 嵌入式平台编写了一些代码。我强烈建议依靠模板来进行编译时优化和路径预测。避免像瘟疫一样的动态投射。按照 Andrei Alexandrescu 的书《现代 C++ 设计》中的规定,使用特征/策略对您有利。
I know, it can be hard to learn, but I am also sure that your product will benefit from this approach.
我知道,这可能很难学习,但我也相信您的产品会从这种方法中受益。