Important reminder just in the Preface :-)
Takeaway #1: "C and C++ are different: don’t mix them, and don’t mix them up"
>Takeaway #1: "C and C++ are different: don’t mix them, and don’t mix them up"
Where "mixing C/C++" is helpful:
- I "mix C in with my C++" projects because "sqlite3.c" and ffmpeg source code is written C. C++ was designed to interoperate with C code. C++ code can seamlessly add #include "sqlite3.h" unchanged.
- For my own code, I take advantage of "C++ being _mostly_ a superset of C" such as using old-style C printf in C++ instead of newer C++ cout.
Where the "C is a totally different language from C++" perspective is helpful:
- knowing that compilers can compile code in "C" or "C++" mode which has ramifications for name mangling which leads to "LINK unresolved symbol" errors.
- knowing that C99 C23 has many exceptions to "C++ is a superset of C" : https://en.wikipedia.org/wiki/Compatibility_of_C_and_C%2B%2B...
C++ can seamlessly include C89 headers.
The C library headers for libraries I write often include C11/C99 stuff that is invalid in C++.
Even when they are in C89, they are often incorrect to include without the include being in an `extern "C"`.
Clang supports C11 - 23 in C++, as well as some future C features like fixed-point integers. The main pain points with Clang are just the fundamental differences like void* and char, which don't typically matter much at an interoperability layer.
Extern "C" around the prototypes is mandatory, otherwise your linker will search for C++ symbols, which cannot be found in the C libraries you pass it.
Specially relevant to all those folks that insist on "Coding C with a C++ compiler", instead of safer language constructs, and standard library alternatives provided by C++ during the last decades.
Funny because for a long time the Microsoft MSVC team explicitly recommended compiling C code with a C++ compiler because they couldn't be arsed to update their C frontend for over two decades (which thankfully has changed now) ;)
https://herbsutter.com/2012/05/03/reader-qa-what-about-vc-an...
That thing always baffled me, this huge company building a professional IDE couldn't figure out how to ship updates to the C compiler.
> it is hard to say no to you, and I’m sorry to say it. But we have to choose a focus, and our focus is to implement (the standard) and innovate (with extensions like everyone but which we also contribute for potential standardization) in C++.
I mean, yeah if it came from a two member team at a startup, sure focus on C++, understandably. But Microsoft, what happened to "Developers! Developers! Developers!"?
It's not baffling, it's remarkably consistent. They implemented Java as J++ and made their version incompatible in various ways with the standard so it was harder to port your code away from J++ (and later J#). They implemented things in the CSS spec almost exactly opposite the specification to lock people into IE (the dominant browser, if you have to make your site work with 2+ incompatible systems which will you focus on?). Not supporting C effectively with their tools pushed developers towards their C++ implementation, creating more lock-in opportunities.
Funnily enough, the intellisense parser does support C syntax because it's using a commercial frontend by edison under the hood. MSVC's frontend doesn't.
Perfectly valid to do if you need to interface with a large C code base and you just want to do some simple OO here and there. Especially if you cannot have runtime exceptions and the like.
This is how I managed to sneak C++ into an embedded C codebase. We even created some templates for data structures that supported static allocation at compile time.
What would be an example of "simple OO here and there" that cannot be done cleanly in plain C?
Templating on pixel classes so that a blitter builds all supported pixel paths separately and inlines them.
Yes you can do it less cleanly with macros or inline functions. But you can't do it performantly with struct and function pointers.
RAII
The killer feature of RAII is when combined with exceptions. But sneaking in exceptions in an embedded C project isn't something I'd encourage or recommend.
C++ imo doesn't offer anything compelling for the embedded usecase. Especially not considering all the footguns and politics it brings.
You can of course be strict and diligent about it but if you are you are pretty much just writing C anyway. Better to do it explicitly.
Allowing the use of the C++ standard library has been one of my biggest regrets (not that it was my decision to make, I fought it).
Is RAII Object orientation? I thought it was an idiom of C++ by Stroustrup.
It doesn't necessarily have to be OO no. Rust uses RAII and it uses traits instead of traditional OO style inheritance etc. You do need something like destructors/drop trait for it to work as far as I know though.
CRTP?
Namespaces, methods.
Namespaces is not object orientation, is it? Am I missing something? You can place functions (methods) inside of structs in C23, can't you?
You can handcode vtables in C, just as you can handcode loops in assembly (i.e. it works but it's verbose, not particularly readable, and brings more footguns).
But why would you do that if you have an instrument that lets you work at the same level as C, but with methods provided as a proper abstraction that maps exactly to what you'd have written yourself anyway?
On a high level, "object orientation" means you think of your code as representing the state and interactions of objects. You can equally well do this in assembly. If you think of some namespace as a "singleton object" then that's what it is.
I guess what you're really asking is what are the best or most common ways to do OO in C?
Oh. I learned that object orientation is primarily a way to structure data and code, such that the data is encapsulated with the code that works on it, in so called objects. So an Object is the Data, plus the functions that work on the data, an ensure that some invariants are kept. In OO parlance, that code gets executed by sending messages (calling methods).
Where can I find something about objects being "think of your code as representing the state and interactions of objects" honesty totally new to me.
So no, certainly I'm not asking ways to do OO in C. But it seems to be more definitions of object orientation as I thought...
> Where can I find something about objects being "think of your code as representing the state and interactions of objects" honesty totally new to me.
I’m scratching my head how you think this is materially different than what you described in your first para. s/state/data and s/interactions/methods.
If anything though I would say the GP is more aligned with the classic definition as it highlights the focus is more on the messages (interactions) themselves rather than the implementation.
There's no clear definition of what OO is, so the best you can do pragmatically is look at mainstream languages that are broadly recognized as OO and try to deduce the commonalities.
If you do that, you'll notice that, for example, encapsulation is not a part of that de facto definition, because languages like Python and (until recently) JavaScript lack it, despite being considered OO.
Indeed, the only two things that appear to be consistently present in all OO languages are: 1) some notion of object identity as distinct from object state, and 2) runtime polymorphic dispatch.
Correct, and you did ask specifically for OO things, but I thought I'd list namespaces too as far as “C++ things you might use when writing C-like C++ code”.
Another big one that I always forget C still doesn't support is function overloading.
I mean as long as your goal is specifically to do that I think it's fine. Using a C++ compiler to compile a C program isn't that rare.
A couple of months ago, in the company I work, there was a talk from HR, where they explained how to make a good CV (the company is firing lots of people). She say: "if you have experience in programming C, you can writing just that, or, if you have lots of experience in C, is customary to write ``C++ Experience'' "
Sooo... yeah... I should definitely change company!
That literally made me do a spit take, and it was fizzy water and it burned.
My god. That's amazing.
Bjarne should have called it ++C.
Because people choose to use pre-increment by default instead of post-increment?
Why is that?
It should be ++C because with C++ the value you get from the expression is the old one.
If you're asking why people use pre-increment by default instead of post-increment, it's mostly historical. The early C compilers on resource-constrained platforms such as early DOS were not good at optimization; on those, pre-increment would be reliably translated to a simple ADD or INC, whereas code for post-increment might generate an extra copy even if it wasn't actually used.
For C++ this was even worse with iterators, because now it depended on the compiler's ability to inline its implementation of postfix ++, and then prove that all the copies produced by that implementation have no side effects to optimize it to the same degree as prefix ++ could. Depending on the type of the underlying value, this may not even be possible in general.
The other reason is that all other unary operators in C are prefix rather than postfix, and mixing unary prefix with unary postfix in a single expression produces code that is easy to misunderstand. E.g. *p++ is *(p++), not (*p)++, even though the latter feels more natural, reading it left-to-right as usual. OTOH *++p vs ++*p is unambiguous.
K&R seems to use pre-increment early on, then post-increment consistently (or a lot, anyway, I haven't done a thorough check) after chapter 3, in situations where either would do. In fact, after introducing post-increment at 2.8.
Why use this operator? Like most C and C++ features the main reason tends to be showing off, you learned a thing (in this case that there are four extra operators here) and so you show off by using it even if it doesn't make the software easier to understand.
This is not one of those beginner -> journeyman -> expert cycles where coincidentally the way you wrote it as a beginner is identical to how an expert writes it but for a very different reason. I'd expect experts are very comfortable writing either { x = k; k += 1; } or { k += 1; x = k; } depending on which they meant and don't feel an itch to re-write these as { x = k++; } and { x = ++k; } respectively.
I'm slightly surprised none of the joke languages add equally frivolous operators. a%% to set a to the remainder after dividing a by 10, or b** to set b as two to the power b or some other silliness.
It's more useful for pointers than for values, IMO
Why would you use post increment by default? The semantics are very particular.
Only on very rare occasions I need post increment semantics.
And in those cases I prefer to use a temporary to make the intent more clear
People seem to mostly write a typical for loop ending with ; ++i){
But I write ; i++){ and seeing it the other way round throws me off for a minute, because I think, as you put it, why would you use those very particular semantics?
But I guess this is only a semantic argument.
> why would you use those very particular semantics?
The difference is that i++ has to keep a copy to the original around as the return value is the pre-increment value, while with ++i that isn't needed as the resulting value is being returned.
In the for loop that shouldn't matter as a) for an integer it is essentially for free (it is just reordering when the relevant register is set) and b) that value is hopefully optimized out anyways by the compiler, however as there are cases where it matters some people prefer the ++i style, some just think it looks better.
I was going to ask if there is a good list of C books and then answered my own question. It categorizes _Modern C_ as Intermediate level.
https://stackoverflow.com/questions/562303/the-definitive-c-...
Table of contents in the sidebar doesn't work properly for me when I click on an entry (in macOS Preview).
Doesn't work for me either... but I will not dismiss the book because of that.
I just test some links in the table of content, works fine for me. Using zathura pdf reader.
Also works in Adobe and Firefox, but doesn't work in Chrome and Edge.
Table of contents is definitely broken right now.
Same here.
So happy that we still get the dinosaur mascots! This is a good book.
Really looking forward to #embed, once the compilers catch up. Until then, Golang.
I do that with ld and objcopy:
https://stackoverflow.com/questions/58815959/include-binary-...
This is not how C standards work. If it appears in the standard, it means that it is already implemented in some compilers (in that case, at least in gcc and clang).
I end up using a .S asm file with .incbin directives to embed files.
#embed would be much nicer
Or
xxd --include <file>
:)Clang 19 has it.
in example 1.1 i read that as 'tits_square' until i saw the output
that's by design
How does "Modern" C compare safety-wise to Rust or Zig?
Modern C still promptly decays an array to a pointer, so no array bounds checking is possible.
D does not decay arrays, so D has array bounds checking.
Note that array overflow bugs are consistently the #1 problem with shipped C code, by a wide margin.
You'd be surprised: Zig has one UB (Undefined Behaviour) that C doesn't have!
In release fast mode, unsigned overflow/underflow is undefined in Zig whereas in C it wraps.
:-)
Of course C has many UBs that Zig doesn't have, so C is far less safe than Zig, especially since you can use ReleaseSafe in Zig..
Do they still use 0-terminated strings/char* as the main string type?
Is the usage of single linked lists still prevalent as the main container type?
> Do they still use 0-terminated strings/char* as the main string type?
Of course, it's still C.
> Is the usage of single linked lists still prevalent as the main container type?
As far as I can remember, the C standard library has never had any functions that used linked lists. Nor are there any container types, linked lists or otherwise, provided by C. So I'd say this is a question about how people teach and use C, not related to the language -- or language spec version -- itself.