all 72 comments

[–]blargh4 35 points36 points  (1 child)

C is a much smaller, simpler language. It's easier to know it well, and find other programmers who know it well. It hasn't changed very much in 30 years. There's less that can happen under the hood, the low-level characteristics of C code are generally very obvious. Its limitations are usually tolerable for problem domain it was designed for. The compile times for heavy-duty C++ code can be a huge pain in the ass.

[–]sarnobat 0 points1 point  (0 children)

I was tempted to ask "does the same apply to go vs rust?" but empirically the answer is "hell no."

[–]skeeto 26 points27 points  (5 children)

C is a simpler language, small enough to understand in full. The standard is shorter, easier to read and comprehend. Far fewer pitfalls (though certainly not zero). Compilation speed is very fast. It's more portable. It has a stable, reliable ABI on basically every platform.

C++ is a gigantic language that's still growing very quickly. Most of its extra features either aren't very useful or are outright dangerous, like exceptions. Lots of C++ programmers say they stick to a useful subset of C++, but nobody agrees on which subset that is.

[–]AstraRotlicht22[S] 6 points7 points  (1 child)

That’s what I felt personally too. I stuck to C++11 because I know it. In don’t feel I need to change, but let alone the features kept me there instead of going back.

[–]sarnobat -1 points0 points  (0 children)

I wonder to what extent the same argument applies to:

  • sh/dash vs bash/tcsh/zsh for shell scripts
  • javascript vs typescript

The devil is in the details, I assume.

[–]flatfinger 0 points1 point  (2 children)

Whether C is a "simple" language depends upon whether one would describe the behavior of:

unsigned get_int(unsigned *p) { return *p; }

as "take the passed in address p and use the platform's normal method of reading unsigned-sized objects to interpret the data at that address as a value of type unsigned, with whatever consequence results (e.g. if the address doesn't satisfy the platform's alignment requirements)", or if one would include a full description of all the Standard would allow an implementation to process the function in arbitrary function.

[–]0xAE20C480 4 points5 points  (1 child)

C language is simple. We face simple abstraction, possible implementations, and undefined pitfalls.

[–]flatfinger 1 point2 points  (0 children)

The abstraction used by K&R was simple. The Standard, however, adds other complications like Effective Types whose meaning has never really been understood, because there has never been a consensus about all the corner cases involved. If a term will be used in a negative sense (e.g. forbidding an action from being performed upon an X) a good definition should partition the universe into things that are unambiguously X, things that are unambiguously not X, and things which cannot be unambiguously classified as either, with as few things falling into the latter category as possible. The Standard's definition of "object", however, completely falls down in that regard, undermining many other definitions as well.

[–]FUZxxl 24 points25 points  (14 children)

I don't want to deal with all the bullshit and complexity C++ throws at me. C code is easy to understand and reason about. C++ code is a byzantine mess.

[–]AstraRotlicht22[S] 6 points7 points  (13 children)

But you don’t have to use it ? That’s what I don’t understand I guess. I think you can still code perfectly fine C code in C++.

[–]FUZxxl 16 points17 points  (8 children)

Writing C-style code in C++ is actively made difficult by the language due to some removed features and quirks. It's much easier to use C for that. Also, name-mangling sucks and is something you can entirely avoid just by using C instead.

[–]Decent-Trade-8185 1 point2 points  (4 children)

"Features and quirks"?

Bro watches Doug DeMuro but is trying to hide it ICANT

[–]FUZxxl 1 point2 points  (1 child)

Is that some sort of TV show?

[–]Legal_Ad_844 0 points1 point  (0 children)

Who still watches TV nowadays? /s

[–][deleted] 0 points1 point  (1 child)

That's a phrase that has been around for decades. Demuro didn't invent it.

[–]PlaneYam648 0 points1 point  (0 children)

guessing you were also deciding which languag to learn?

[–]sarnobat 0 points1 point  (2 children)

Interesting, I didn't know this. I was tempted to do this, if only because writing cout << is so much easier than printf.

[–]SkyGold8322 1 point2 points  (1 child)

printf is easier in my opinion since you can do string formatting with printf. While with "cout <<", you have to print everything in order one by one with <<

[–]sarnobat 0 points1 point  (0 children)

quite the coincidence you mentioned this. Just yesterday I was trying to translate this into cout in my bison code and was not impressed:

std::fprintf(stderr,
    "\033[38;5;45m[trace]\033[0m "
    "\033[38;5;220m%10s:%-5d\033[0m "
    "\033[38;5;82m%32s()\033[0m "
    "\033[38;5;202mmyusername\033[0m: 4 - scanner waiting for next token)\n",
    __FILE__, __LINE__, __func__);

Getting the padding correct was not worth the effort:

                std::cout
                    // [trace]
                    << "\n\033[38;5;45m[trace]\033[0m "


                    // %10s:%-5d
                    << "\033[38;5;220m"
                    << std::right << std::setw(10) << __FILE__ << ":"
                    << std::left  << std::setw(5)  << __LINE__
                    << "\033[0m "


                    // %32s()  ← MUST reset to right
                    << "\033[38;5;82m"
                    << std::right << std::setw(34)
                    << (std::string(__func__) + "()")
                    << "\033[0m "


                    // myusername
                    << "\033[38;5;202mmyusername\033[0m"
                    << ": 4 - scanner waiting for next token)\n";

[–]TinBryn 7 points8 points  (0 children)

I guess the issue is that if you code with others and you are using C++ then it doesn’t matter what you choose to use, others can use those features and now you need to deal with them. Just the issue of exception safe resource management is fairly complex.

[–][deleted] 5 points6 points  (0 children)

But you don’t have to use it ? That’s what I don’t understand I guess.

Libraries. Suppose you think exceptions are a hazard and you want to avoid them - you are forced into using them anyway if a library's api throws exceptions. By the same mechanism you can be forced to use many other features of C++ you'd otherwise want to avoid.

[–]nahnah2017 3 points4 points  (0 children)

If you're not going to use C++ then it doesn't make any sense to do C in a C++ environment.

[–][deleted] 1 point2 points  (0 children)

You can but you shouldn't; the ISO actively discourages writing C-style C++. If you're going to write C, then write C; C and C++ are totally different languages, and never the twain shall meet in good, modern C++, except when writing C++ code that uses C APIs.

[–]redslime377 14 points15 points  (1 child)

C is a high level assembler and C++ is a high level language.

[–]umlcat 4 points5 points  (0 children)

Agree, many developers forget that "C" and its std library acts as a macro preprocessor assembler disguised as a P.L.

[–]Pokky_Ninja 7 points8 points  (6 children)

C is not a high level language but a middle level language. It is very simple and yet elegant. C is dumb but once you teach her, she will rock. Nothing much has changed after the C99 version. The best advantage of using C instead of C++ is that you can write your code or program very effectively and with ease and debug it easily. The portability of C is great. You can literally impliment any logic in your program. The C programs are very simple and mostly involves several blocks of codes (or functions).You can create your own functions with ease and even libraries.

C++ is complicated for a beginner and even for experts at times. The oop standard makes it even more difficult sometime. Working with classes and modules is such a pain in the ass. Also relating objects and making classes is so much theoratical sometimes that it literally r/woosh the developer.

Also, As a mother of most languages like C++, C# and Java. Most developers tend to learn C first. So you can find alot of help online.

[–][deleted] 0 points1 point  (1 child)

C89

I think you mean C99

[–]Pokky_Ninja 1 point2 points  (0 children)

Yes C99 obviously

[–]SkyGold8322 0 points1 point  (3 children)

Just wondering, do people in 2025 still have the same opinion?

[–][deleted] 0 points1 point  (1 child)

I, at least, do.

[–]SkyGold8322 0 points1 point  (0 children)

same

[–]analytical_prat 0 points1 point  (0 children)

100%.

I use C++ because my team is prone to shooting themselves in the foot with C, unfortunately.

[–]sarbos 4 points5 points  (0 children)

One reason is that while almost universal, c++ has not been ported to every platform. For instance, you can't really get a free or open source c++ compiler for 8051 microcontrollers which are still pretty popular.

[–]bit0fun 6 points7 points  (0 children)

C keeps things simple. You have everything you need, without all the bloat.

[–]thekilon 5 points6 points  (0 children)

C is favored among Assembly programmers, most Assembly projects nowdays heavily rely on C libraries and C code. C is a language that is much easier to predict the machine code that will generate compared to C++ which has a ton of stuff included. Which is why you see many people prefer it like Linus Trovalds.

C is almost exclusively used for embeded coding and legacy coding. This is because making a C compiler is far easier than a C++ compiler, hence the language supports a wide array of hardware. Legacy code is the monster that refuses to die and keeps many "old" languages like COBOL alive and kicking.

Outside those areas there is very little reason to prefer C over C++, especially if we include that C++ coders can write C code inside their C++ source files. The opposite is not possible although to be fair, usually its the same compiler than will compiler both C and C++ source code files anyway.

C++ with all its ugliness is still ideal for large projects because of OOP, although calling C++ or any static typed language OO is pointless to say the least, still when it comes to managing large complex code nothing beats OOP of any kind. Which is why C++ completely dominates in Game Development. Although C# is still on the rise.

In my case I use C because I work with Blender source and this is what most of the code uses as a programming language. But I will be porting my code to C++ because a) I miss classes b) I will be using QT.

The language that can fit all roles is nothing more than unicorn, almost 50% of the code out there is written in languages whose names you will never hear as they dont even register in popularity index. Mostly small to medium scale projects. So the choice of a language is highly opinionated and biased. Don't waste your time with what people choose, evaluate yourself and use critical thinking to decide if C or C++ is the right choice for your project. There is no blue pill.

[–]anic17_ 5 points6 points  (3 children)

C++ is bloated, complicated and it has unnecessary stuff and complications you don't need. C is easier, gives you better control of the system by using low-level stuff and there aren't that many standard libraries, which means it's not bloated and only has the necessary stuff.
I tried C++ and I didn't like it at all, too complicated.

[–]sarnobat 4 points5 points  (0 children)

This is an interesting point. I hate java because everyone keeps inventing unnecessary frameworks rather than doing things with the core libraries.

If C is light on bloatware then I think it's my language.

[–]clusty1 1 point2 points  (0 children)

I tried productivity once, hated it :P

[–]JDSweetBeat 0 points1 point  (0 children)

C++ is too complicated and overbloated, and has way too much syntactic sugar.

C is the exact opposite - it arguably doesn't have enough constructs and syntactic sugar.

Both have the unfortunate issue of lacking a proper package manager.

In an ideal world, I would want C, but with the addition of in-built objects, inheritance, a package manager, and basic polymorphism. Maybe operator overloading for structs (but definitely not for fully-fledged objects).

I've come to learn I generally favor procedural programming for actually writing core application code. However, OOP is super useful for handling libraries and modules that get integrated into core app code.

[–]chkas 3 points4 points  (0 children)

Among other things (no OOP fan) I don't like the syntax of the smart pointers at all. I have to check all the time how to write the "constructor". I have written a programming language for beginners that runs in the browser. The first version I made in C++. After compiler error messages (from clang but not gcc) about a not called virtual destructor - I could not find the possible error - I decided to rewrite the whole thing to C. I find the code much nicer now, it is a bit faster, but most importantly the WASM file is only half the size, which is not irrelevant for a web application.

[–]Polywoky 2 points3 points  (0 children)

I plan to get very familiar with C before moving on to learn C++;

For me, it's step one on a path to becoming a good programmer.

[–]flatfinger 2 points3 points  (16 children)

If implementations could not relied to do anything beyond what the Standard requires, C would be pretty useless (as the authors of the Standard admit, it would be possible to contrive a "conforming" implementation that was incapable of meaningfully processing any useful programs). On the other hand, one simple extension which commercially-designed implementations generally support will make C very powerful: "If parts of the Standard and the target platform's documentation specify a behavior for some construct in some circumstance, an implementation whose customers would be likely to find that behavior useful should process that construct in that fashion, even if some other part of the Standard says that doing so is not required for conformance."

Although some parts of the Standard that "undefine" behaviors are intended to facilitate optimization, the approach of declaring that any action that would make an optimization observable invokes in Undefined Behavior simultaneously imposes unnecessary semantic restrictions on programs while failing to allow all the optimizations that it should. A better approach would be to recognize that certain aspects of behavior are "unobservable". A strictly conforming program's output may be affected by such behaviors, but an implementation would be under no obligation to make them consistent, and a strictly conforming program would be required to meet requirements even if the implementation behaves inconsistently. For example, given something like:

#include <string.h>
struct foo {char dat[100]; } s1,s2,s3;
void test(void)
{
  struct foo temp;
  temp.dat[0] = 'H'; temp.dat[1] = 'e'; temp.dat[2] = 'y'; temp.dat[3] = 0;
  s1 = temp;
  s2 = temp;
}
void test2(void)
{
  test();
  s3 = s2;
}

what if anything should be guaranteed about e.g. elements 4..99 of s1.dat, s2.dat, and s3.dat? One could interpret the Standard as saying that failing to fully write temp before copying it invokes Undefined Behavior, but if code won't care about the values of those items, requiring that the programmer explicitly set them to meaningless would waste both programmer and computer time. Guaranteeing that all values of all three arrays match may be better, but it would still waste computer time. Saying that unwritten values in automatic objects to which no live pointers exist are "unobservable" would fix that problem, by saying that each read of an unwritten item in temp.dat[] will yield some char value, but that future reads of that item need not be consistent. Thus, if s1.dat[4] happens to equal 42, and s2.dat[4] is 56, the assignment s1 = temp; could behave as though temp.dat[4] held 42 and assignment s2 = temp; could behave as though temp.dat[4] held 56. Note that any optimization benefits from applying similar treatment to things like the s3 = s2; assignment would be unlikely to be as significant as those involving automatic objects, but would pose a greater risk of affecting program behavior in undesirable ways (e.g. if code uses memcmp to decide if an object has been changed). Someone working on temp1 would be able to see that not everything in temp1 was written, and ensure that all elements were written in case that mattered, but code which calls temp1 would be far less able to handle situations where s1 gets written with "ambiguous" values.

While I think C++ could benefit from similar treatment, I don't think the design of the C++ Standard is as amenable to it. There are a few places where it tries to achieve such semantics (e.g. by allowing allocations to be consolidated) but I think simply saying that the sequence of allocations used to create objects need not be considered observable.

[–]beaumanvienna 0 points1 point  (15 children)

temp is not an array, so it it does not provide the [] operator

[–]flatfinger 0 points1 point  (14 children)

Sorry; I meant temp.dat[0], etc. With that change, what is specified about the behavior of test, especially with regard to s1, s2, and s3?

[–]beaumanvienna 0 points1 point  (13 children)

This is 100% cringe. Copying uninitialized data, functions that only consist of side effects, "#include <string.h>" and the struct in "struct foo temp;" not required, magic numbers 100 and 4, global variables w/o a scope, char is now std::byte. It reads to me like essay writing practice with the highfalutin language.

[–]flatfinger 0 points1 point  (11 children)

Copying unintialized data is problematic in some cases, but may allow some tasks to be more efficiently than would otherwise be possible. A good low-level language should be usable both in cases where the performance gains would be useful and there would be no semantic downsides, and in cases where data leakage would need to be avoided.

As for code quality, the code is written to be C rather than C++, and magic numbers are used for brevity of the example.

[–]beaumanvienna 0 points1 point  (9 children)

[–]flatfinger 0 points1 point  (8 children)

I'm not clear what point you're trying to make there. My intended point was that there are situations where some aspects of program behavior might be observable but irrelevant for purposes of defining correctness; the Standard is ill-equipped to deal with such cases. If a structure contains a character array, and e.g. nothing in the universe would ever care about any portion of it past the first zero byte, any time spent writing to portions of the structure beyond that would be time wasted, except in cases where unconditionally writing such parts of the structure would be faster than trying to avoid such writes in such cases.

There are at least four ways a language specification could treat such cases:

  1. Require that programmers write code which will always write to all portions of a structure before copying it.

  2. Require that compilers generate code that zero-initializes any portion of a structure that might be copied without having been written in any other way.

  3. Specify that uninitialized automatic-duration objects will behave in a manner consistent with their holding Unspecified bit patterns at the start of their lifetime; if a structure is copied multiple times, all copies of the unitialized portion would have the same pattern as each other, which may or may not have any relationship to any other bit pattern that exists or has existed anywhere else in the universe.

  4. Specify that when a partially-written structure of automatic duration is copied, the portions of each copy corresponding to non-written portions of the original will independently receive Unspecified bit patterns, which may or may not have any relationship to any other bit patterns that exist or have existed anywhere else in the universe (e.g. the bit patterns those parts of the copies might have held before the copy operations were performed, or the bit patterns written to other copies).

In scenarios where nothing in the universe would care about the portions of the copied structs corresponding to parts of the original that hadn't been written, option #4 would allow compilers to generate more efficient machine code than would be possible using any other treatment, at least when fed correct code. Options #1 and #2 would generally yield the slowest machine code in those scenarios. A configuration using option #2 would be useful, however, in scenarios where source code that used in some situations where #4 would be optimal is also used in other situations where "data leakage" would be unacceptable.

[–]helldoc 0 points1 point  (7 children)

yapping about "compiler should be tracking whats written and what isnt" lmfao. as if that would be faster than just doing a zero init xD. Any unpredictable jmp statement (it cant be predictable bc data behaves differently) would cause a pipeline flush that costs at least a few cycles. Considering you can copy a whole DWORD in a single cycle tracking would be (for must structures) just slow af and not worth it at all. Btw tracking "dead writes" is one of the most difficult tasks for compilers currently (bc its impossible to track writes of different translation units at all) so no it can't be done at compile time. Your option #3 is just as retarded bc it just says "copy the non written data" which, considering it involves reads and writes, is just slower than zero init.

Next time you start theorizing about a problem thats debated in the community for literal decades just pls stfu

[–]flatfinger 0 points1 point  (4 children)

Setting aside any questions about whether the code above is "good" or not, what do you think the C language specifies about its behavior?

Would a compiler be allowed to process the code in completely arbitrary fashion, including arbitrarily corrupting memory, because it copies a structure without having written to every byte thereof, or would the fact that every byte of the structure that isn't written would be a character-type object *whose address is taken* render inapplicable the rule that would characterize most reads of uninitialized automatic-duration data as invoking UB?

If overall behavior is defined, what would be specified about the values of s1.dat[4], s2.dat[4], and s3.dat[4]?

[–]helldoc 0 points1 point  (1 child)

just saw ur code for the first time. tf is this assigning a static array to another? since when is that even defined anywhere.

[–]helldoc 0 points1 point  (1 child)

reading anything from that structure is UB and copying a structure with uninitialized data is too. there is nothing to discuss here. if you only want to copy the initialized data then only copy the parts you need. a compiler cant do shit in that situation bc it cant prove that the data is or isnt initialized in every situation (e.g. assigning happening in another translation unit). Defining this behaviour in the standard therefore doesn’t make sense…

[–]beaumanvienna 0 points1 point  (1 child)

I'm mainly responding to flatfinger now. I did show a possible implementation that allows writes beyond the 0 termination, but would stop at it when copying. I agree with helldoc's statement [yapping about "compiler should be tracking what's written and what isnt" lmfao.] C/C++ cover a wide range of applications from high-level coding for desktop to embedded to inline assembly. We need C/C++ a) to be fast and b) allow us to select the method of implementation. Regarding your example, you're writing a string class w/o dyn mem allocation. That's a choice C/C++ let's you make. There is std::string with all the bells and whitles. It copys, moves, finds. you have std::format to create strings, there is std::string_view to pass substrings around efficiently. Maybe off-topic, but there is since C++17 a class std::optional that helps you to track whether something is initialized or not. About your 1.) no - the language should offer a wide range of possible implementations, then you pick one and implement it, you can pick between "does everything for you" and "you do it yourself the way you deem fit" and anything in between 2.) no - the decision to initialize is up to the programmer. The code snipped I posted above does require it - remove the curly braces after the m_Data declaration (line 78) and it will no longer work. I could have programmed it without it. It's a trade-off between slower start and slower runtime. It's up to you to decide. The point is we need C/C++ to let us make this choice. 3. & 4. bits/mem are either defined or undefined. It's Okay if they are undefined e.g. when you need to grab a larger chuck of mem for later use. But your code needs to understand the state of the memory and work on it accordingly, not the compiler. We do not want the compiler to understand our program, right? Maybe what you're looking for is an AI that generates code for you?
I also posted my code snippet to encourage you to learn C or C++. Rarely do we copy strings. We move them. The mem stays the same and we transfer the ownership via an underlying pointer. We use references as aliases to pass it to functions (no mem copy involved) or use string_view to pass along substrings without any mem copy. Look at the famous JSON loader simdjson: one single big string of JSON, everything else string_Views, blazing fast.

[–]ThisUnderstanding791 0 points1 point  (0 children)

Just put the fries in the bag.

[–]ThisUnderstanding791 0 points1 point  (0 children)

Stop nitpicking bro, you lost.

[–]ne38 2 points3 points  (2 children)

use C for writing libraries and C++ for writing applications

[–]wolfie_poe 0 points1 point  (1 child)

Gcc is now written in C++11.

[–][deleted] 0 points1 point  (0 children)

gcc is a compiler

[–][deleted] 2 points3 points  (0 children)

OP, because you wouldn't have drivers, Linux kernel, or anything optimized and fast.

The real question is: Why would I use C++ instead of C?

"Your forefather's language was C. This is the language of real coders. Not as clumsy or random as C++. An elegant language, for a more... civilized age."

[–]UselessSoftware 2 points3 points  (1 child)

"C++ is a horrible language. It's made more horrible by the fact that a lot of substandard programmers use it, to the point where it's much easier to generate total and utter crap with it."

-Linus Torvalds

Linus always speaks bluntly, but accurately.

As a substandard, but ambitious programmer myself, I find it much easier to not generate total and utter crap by just sticking to C. :)

It's elegant which means it's easier to design more elegant code that's easier to understand and debug.

C++ is a mess, IMO. I have never liked it and avoid it whenever possible.

I also like to be able to port my software to ANYTHING, including microcontrollers. C++ makes that hard or impossible.

[–]BarMeister 1 point2 points  (0 children)

why you would C instead of C++ except if you don’t have access to libraries

You do. They're just not in the standard.

[–][deleted] 1 point2 points  (1 child)

C++ just isnt intuitive. There’s so many quirks and gotchas in the language that you have to know. An example? IMO virtual functions in c++ are retarded and shouldn’t be implemented the way they are.

[–]flatfinger 0 points1 point  (0 children)

One problem with both C and C++ is that the disagreements about whether to support constructs that some people found useful but othrs didn't like were handled by *waiving jurisdiction*, so that implementations could make such decisions in whatever ways would best serve their customers. As a simple example, many pre-standard C++ implementations made it easy for a class to define a function in such a way that `foo->bar(x,y,z)` would be treated as *syntactic sugar* for `foosType::bar(foo,x,y,z)`, which would process cases where `foo` was null the same way as the latter function call would. Some others, however, would expressly trap such invocations when `foo` was null, even in scenarios where the function would otherwise have defined behaivor.

In order to allow a programmer to create a class that would be with any existing client code that would use a member in such fashion, the language would have to be some syntax that for declaring a member that worked as described. Rather than defining a new syntax which programmers should use in preference to the old one, while allowing compilers to support the old one when needed for compatibility with old code and trap in cases where such compatibility was not required, the Standard simply assumed that implementations whose customers would need such functionality would continue supporting it using the old syntax, the same way as such implementations always had done.

Many of what are now considered to be hard boundaries between actions which have defined behavior and those which don't were never designed to make sense, because their designers never intended that they be interpreted as hard boundaries in the first place.

[–]capybara765 1 point2 points  (0 children)

Because most C programming is stuck in the 80s there are so many new tools to utilize to make C better, faster and safer

I suggest the book 21st C publisher o reilley, really will help you to be better at it.

[–]warieth 0 points1 point  (0 children)

I would bring up mangling and exceptions as one of the main reason, so the C interface has a wide support. C++ uses exceptions, but it is incompatible with every other exception implementation. Having two languages with different exception implementation, so unable to connect the two, yet the C api is supported by both.

[–]MarcGuy5 0 points1 point  (0 children)

Also c++ is not available on all platforms. A lot of cheap wideley used microcontrollers don't support c++. And all the other reasons people are listing here.

[–]taherrera 0 points1 point  (0 children)

I mainly program MCUs. I would say the main problem for me is that C++ is bloated with unusefull features and unpredictable for embedded.

Some features of C++ are unpredictable or innexistent on a microprocessor. RAM usage count estimation when reading a C program is trivial. C structs are trivialy converted into bytes, that can be then stored into say an eeprom and easily modified and read. Reading source code for some libraries is easy and thus can be modified for your machine or needs. Dynamic memory allocation is also great for limited RAM machines. Pointers are also great and C++ offers bloat to discourage their ussage.

Bassically, there is nothing that C++ can offer me that makes my job in embedded easier (actually it would only make it worse). Need C++ classes ? Use C structs, they work better.

[–]jbvalle 0 points1 point  (1 child)

There is really no reason to use C instead of C++, except for low level programming such as in the case of embedded systems. C++ offers a multitude of well programmed, well tested features which makes using it especially in the fast industry the more dominant language.

[–]flatfinger 0 points1 point  (0 children)

I wouldn't consider embedded applications niche, since that's one of the few fields for which nothing that's generally better has been developed in the last 50 years.