In 2009, Michael Barr wrote an article entitled "Real men program in C." 1 The article prompted numerous comments from readers, including a number who cited reasons to prefer C over C++. I chimed in by writing a column entitled "Poor reasons for rejecting C++" in which I explained why I disagreed with some of those comments.2 That, in turn, provoked another flurry of comments. Many of those comments affirmed the wisdom of my analysis, thank you. Others raised interesting issues that I will try to address in this and future columns.
Some readers took exception to my statement that, "I know of no place where the C++ language performs dynamic allocation or recursion behind the scenes." One reader wrote that, "The flexible nature of common data types such as the C++ vector and string classes would seem to completely rely on dynamic allocation, just as the whole STL library. I can't quite see how you could implement things like the string + operator, vector push_back member function, etc. ... without a heap ..."
Arguably, I could have been more explicit that I was talking only about the C++ language itself and not its accompanying library. I did elaborate that, "Indeed, your code might call a function that uses dynamic allocation or recursion, but this is no more a problem in C++ than in C," but I guess I wasn't explicit enough.
I think embedded programmers should learn to think of the C and C++ programming languages as distinct from their accompanying standard libraries. Although I'm not prepared to say embedded programmers should view all languages this way, it's probably an appropriate way to view statically-typed, compiled languages, which include C and C++, as well as others such as Ada. Most compilers come with a small collection of mysteriously-named functions that support operations such as program startup, program shutdown, and floating point arithmetic, but such functions are not really part of the library as defined by the language standards. Rather, such functions are just artifacts of the compiler's code generation strategy.
The C Standard formalizes a separation between the language and the library by distinguishing between hosted and freestanding implementations. Informally, a hosted implementation is a C translation and execution environment running under an operating system with full support for the language and library. A freestanding implementation is a C translation and execution environment with nearly full language support but essentially no support for the standard library's runtime components—an environment not uncommon among low-end embedded systems.
Here's what the C 99 Standard actually says:3
"The two forms of conforming implementation are hosted and freestanding. A conforming hosted implementation shall accept any strictly conforming program. A conforming freestanding implementation shall accept any strictly conforming program that does not use complex types and in which the use of the features specified in the library clause (clause 7) is confined to the contents of the standard headers , , , , , , and ."
Those seven named headers define only constants, types, and a few function-like macros. They don't declare any functions. A freestanding implementation need not provide headers such as or . Thus, it need not support input and output functions such as fgetc and printf or memory management functions such as malloc and free. A freestanding implementation doesn't even need to support the string handling functions in , such as memcpy and strlen.
Although many embedded systems do without i/o and memory management, I suspect very few get by without memcpy. Thus, the C Standard's notion of a freestanding implementation defines a narrower subset than what most embedded tool chains offer and what most embedded programmers actually use. Part of learning to be a good embedded developer is learning to be selective about which parts of the library are safe and appropriate to use. Documents such as the MISRA-C guidelines can provide assistance in this regard.4
The C++ Standard distinguishes hosted and freestanding implementations much as the C Standard does, but a freestanding environment in C++ demands more runtime support than it does in C. In particular, the 2003 C++ Standard says that a freestanding implementation must provide at least the headers , , , , , , and , and that "the supplied version of the header shall declare at least the functions abort(), atexit(), and exit()."5
Whereas a freestanding C implementation need not provide , and therefore need not support malloc and free, a freestanding C++ implementation must provide and therefore must support operators new and delete. For programmers concerned that they might inadvertently use dynamic allocation, C++ offers compile- and link-time techniques you can use to prevent using these operators. I'll discuss those techniques in a future blog.
End notes:
1. Barr, Michael, "Real men program in C", Embedded.com, August 1, 2009.
2. Saks, Dan. "Poor reasons for rejecting C++", Eetasia.com, March 2011. http://forum.eetasia.com/BLOG_ARTICLE_6861.HTM.
3. ISO/IEC Standard 9899:1999, Programming languages—C.
4. MISRA-C 2004: Guidelines for the use of the C language in critical systems. Motor Industry Research Association, October 2004.
5. ISO/IEC Standard 14882:2003(E), Programming languages—C.
文章评论(0条评论)
登录后参与讨论