My colleague, Michael Barr, wrote an interesting piece earlier this month, entitled “Real men program in C.”1 I won't try to summarize it–you can read it yourself. The article provoked numerous comments from readers citing reasons to prefer C over C++. I disagree with several of those comments, and I'd like to say a little in reply to each.
One reader wrote, “Just for the fun of it you might as well want to have a look at what Linus Torvalds thinks about C and C++,” followed by the URL to the remark (which I've omitted intentionally).
I suppose reading what Torvalds wrote would be fun if you like reading rants. It wasn't fun for me. It's provocative but not well grounded, as explained by my friend and colleague Steve Dewhurst.2 Torvalds has made positive contributions to the computing industry, but this wasn't one of them.
Another reader wrote, “I've studied C++ but never used it professionally and, the more I study it, the less I think it appropriate for embedded use. It's not just that it is expensive in terms of resources; it encourages you to do too many risky things. It's bad enough that you can do things like dynamic allocation and recursion in C but, in C++, the language will do it without your ever realizing it, unless you know what it's doing behind the scenes…”
I have used C++ for prototyping embedded systems and consulted for others who have developed production systems in C++. The more I use it and see it used, the more convinced I am that it's preferable to C, when available.
Rather than encourage risky behavior, many of C++'s core features–such as classes, access control, constructors, destructors, references and overloading–work hand-in-hand with stricter type checking to help rein in the riskiest parts of C. While I agree that typical C++ code tends to be a bit larger and slower than comparable C, the added expense is rarely prohibitive (on the order of 10-15%). I've had little problem tuning time-critical code, such as interrupt handlers, to be as or more efficient than it would be in C.
I know of no place where the C++ language performs dynamic allocation or recursion behind the scenes. 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. In fact, C++ supports simple compile- and link-time techniques you can use to explicitly prevent using dynamic allocation, which I'll cover in an upcoming column.
Yet another reader wrote, “The only part of object orientation that is a must-have in embedded systems is private encapsulation. C supports this nicely enough with static variables declared at file scope.”
While I agree with the basic sentiment of the first sentence, I wouldn't word it so strongly. I would say that the single most useful feature of C++ that's absent from C is classes with private access control. I would not call it a “must-have” feature simply because programmers do write viable embedded systems in C–without classes. However, you can write better embedded systems with classes. I wouldn't want to code without them.
On the other hand, I disagree that C supports access control “nicely enough with static variables declared at file scope”. I believe the reader is referring to the common technique whereby you:
• place the data declarations for the encapsulated data structure in a separate source file,
• add the keyword static to each data declaration so that the data has internal linkage,3 and
• define non-inline functions with external linkage in that same source file to provide the publicly accessible operations on the encapsulated data.
I refer to this technique as encapsulation by separate compilation . It works “nicely enough” only if you're willing to accept the following:
• Your code will probably be both bigger and slower than it would be if the data were not encapsulated, because code outside the encapsulation unit can access the data only through non-inline function calls.
• Each program can have at most one instance of the encapsulated data structure, unless you're willing to accept even bigger and slower code that comes with using an ad hoc memory manager.
C++ classes don't have these limitations.
The same reader added that “Initialization through constructors is not something you desire; in fact you will want to avoid this for security reasons and for reduced bootup time.”
I strongly disagree with this statement. Initialization by constructors is highly desirable. I'd rank constructors and destructors as the second most useful C++ feature that's absent from C (after classes with access control). Failure to properly initialize objects can lead to security problems, and constructors go a long way toward ensuring proper initialization. (Order-dependencies in member initializers can be hazardous, but you can use static analysis tools that will alert you to such hazards.) I would be interested to see concrete examples of how using constructors can cause security problems. So would Robert Secord, author of Secure Coding in C and C++ (2005, Addison-Wesley) and The CERT C Secure Coding Standard (2008, Addison-Wesley).
If you're concerned about startup time, C++ offers features such as “placement new” to give you greater control over when constructors execute. Using placement new, you can defer initialization for selected objects until after system startup. I expect to cover this in the not-too-distant future.
Despite my avowed preference for C++ over C, I still recognize that many of my readers have legitimate reasons to use C, such as the lack of an adequate C++ compiler for the intended target platform.4 I will continue to write about topics of interest to both C and C++ programmers, in part because I think your programming language choice should be well-informed.
Between the time I wrote this column and the time it was posted, further comments on Michael Barr's column pursued the question of whether constructors can lead to security problems. The reader who originally raised this issue explained that the problem “isn't because of the language, but because of the unreliable nature of RAM. there could be days, weeks or years from the point of initialization to the point where the variable is used. If you rely on the initialization values, you leave the safety in the hands of the RAM manufacturer.” It sounds to me like the problem he's describing is, by his own admission, independent of the use of constructors. I still see no basis for the claim that one should avoid using constructors for security reasons.
Dan Saks is president of Saks & Associates, a C/C++ training and consulting company. For more information about Dan Saks, visit his website at www.dansaks.com. Dan also welcomes your feedback: e-mail him at . For more information about Dan .
1. Barr, Michael, “Real men program in C”, Embedded.com, August 1, 2009. Available online at www.embedded.com/columns/barrcode/218600142.
2. Kalev, Danny, C++ Reference Guide: A Response to Linus Torvalds' C++ Diatrib e. Posted online November 14, 2008 at www.informit.com/guides/content.aspx?g=cplusplus&seqNum=411.
3. Saks, Dan, “Linkage in C and C++”, Embedded Systems Design , March, 2008, page 9. Available online at www.embedded.com/columns/technicalinsights/206901036.
4. Saks, Dan, “Moving to Higher Ground”, Embedded Systems Programming , October, 2003, page 45. Available online at www.embedded.com/columns/programmingpointers/15200968