C Programming Language Hits a 15-Year Low On The TIOBE Index (businessinsider.com)
Gamoid writes: The venerable C programming language hit a 15-year low on the TIOBE Index, perhaps because more mobile- and web-friendly languages like Swift and Go are starting to eat its lunch. "The C programming language has a score of 11.303%, which is its lowest score ever since we started the TIOBE index back in 2001," writes Paul Jansen, manager of TIOBE Index. With that said, C is still the second most popular programming language in the world, behind only Java. Also worth noting as mentioned by Matt Weinberger via Business Insider, "C doesn't currently have a major corporate sponsor; Oracle makes a lot of money from Java; Apple pushes both Swift and Objective-C for building iPhone apps. But no big tech company is getting on stage and pushing C as the future of development. So C's problems could be marketing as much as anything."
Basically the calculation comes down to counting hits for the search query "language programming"
It doesn't matter if those results are positive or negative. All that matters is the number. If you make a language and get ten billion people to post on indexed sites about how badly your language sucks, your language would take the top spot on the TIOBE Index.
So... yeah. This is about as lame of an index as you could possibly come up with.
Java is the new COBOL. Given we need a language for that role, I actually think Java's pretty good.
As one of my professors once argued, its usually the accompanying libraries that make or break a language in more recent years. Java's advantage is simply a "more capable" "standard library".
Also we went recently through a phase in computer science education where people were really only taught java. Its the only tool in their toolbox.
New code is written in Fortran all the time. I guess you don't work in any place doing numerical analysis more complex than Excel. Talk to a Ford engineer some time. I am sure you'd find the same any large auto manufacturer.
Flawed logic. C++ doesn't have a corporate sponsor either, and yet it has a native compiler on Windows.
Multiple vendors pay good money to develop compliant C++ compilers for many of the platforms that we use.
The two main challenges I see for C are the competition with C++ and faster hardware.
Most "C compilers" are actually C++ compilers running in a "C mode". The trouble is that most of the corporate sponsors care more about being compliant with the latest C++ standard than being compliant with the latest C standard.
And because C++ is slightly more typesafe (strict aliasing), those optimizers can do more for C++ code than for C. So despite the more complex language, C++ can be marginally faster (~1%).
Probably not even that. In the tiny number of cases where strict aliasing buys you anything at all (which on modern out-of-order hardware it almost never does), it's around the same order of magnitude as the performance that you lose in C++ due to maintaining exception handling information. There's really nothing between C and C++ given the same code these days. In practice, most of the performance gains in C++ come from metaprogramming.
Which gets us to the faster hardware: how often is that performance even needed?
For the vast majority of "embedded" devices that I own, the main performance impact isn't CPU speed, it's battery life. A program which gets its job done as quickly as possible and then puts the CPU into an idle state is far more desirable than one which is perceptually just as responsive but wears down the battery faster.
sub f{($f)=@_;print"$f(q{$f});";}f(q{sub f{($f)=@_;print"$f(q{$f});";}f});
Probably not even that. In the tiny number of cases where strict aliasing buys you anything at all (which on modern out-of-order hardware it almost never does), it's around the same order of magnitude as the performance that you lose in C++ due to maintaining exception handling information. There's really nothing between C and C++ given the same code these days. In practice, most of the performance gains in C++ come from metaprogramming.
Actually...
Aliasing information does help. Modern compilers do auto parallelization at both the fine level (SIMD) and the coarser thread level in some cases. Take, for example the following code:
void foo(A* a, B* b)
{
for(i=0;i
A and B are the same types, there is nothing that stops the base where a=b+1. If A and B are different types, strict aliasing tells us those arrays never overlap, and so the compiler can use wide SIMD instructions in that loop.
If A and B are the same type, in C (and in practice C++ compilers with a nonstandard extension), you can specify "restrict", which informs the compilers that the arrays don't alias and so it can use the wide SIMD instructions. In that case C is a bit better than standard C++, since C++ has no restrict keyword.
Also, when it comes to exceptions, these days it's basically zero cost (I think it is actually zero cost), provided you don't throw. The compilers make throwing expensive to do that. I believe there's some sort of big look up table plus bisection somewhere so it can figure out the throw-position from the program counter, which is quite expensive. Then it jumps from there to the correct bit of unwind code. The practical effect is that there's nothing exception related in the main code except for a throw.
SJW n. One who posts facts.
_Generic - Useful for a few things (mostly tgmath.h, which I've rarely seen used in real code because C's type promotion rules make it very dangerous, but it was quite embarrassing that, for 12 years, the C standard mandated a header that could not be implemented in standard C). Existing compilers have all provided a mechanism for doing the same thing (they had to, or they couldn't implement tgmath.h), but it was rarely used in real code. Oh, and the lack of type promotion in _Generic makes it annoyingly verbose: int won't be silently cast to const int, for example, so if you want to handle both then you need to provide int and const int cases, even though it's always safe to use const int where an int is given as the argument.
_Static_assert - useful, but most people had already implemented a similar macro along the lines of:
This gives a 1 or -1 element array, depending on whether x is true. If x is true, the array is optimised away, if x is false then you get a compile-time failure. _Static_assert in the compiler gives better error diagnostic, but doesn't actually increase the power of the language.
And then we get on to the big contributions: threads and atomics. The threading APIs were bogged down in politics. Microsoft wanted a thin wrapper over what win32 provided, everyone else a thin wrapper over what pthreads provided. Instead, we got an API based on a small company that no one had ever heard of's library, which contains a clusterfuck of bad design. For example, the timeouts assume that the real-time clock is monotonic. Other threading libraries fixed this in the '90s and provide timeouts expressed relative to a monotonic clock.
The atomics were lifted from a draft version of the C++11 spec (and, amusingly, meant that C11 had to issue errata for things that were fixed in the final version of C++11). They were also not very well thought through. For example, it's completely permitted in C11 to write _Atomic(struct foo) x, for any size of struct foo, but the performance characteristics will be wildly different depending on that size. It's also possible to write _Atomic(double) x, and any operation on x must save and restore the floating point environment (something that no compiler actually does, because hardly anyone fully implements the Fortran-envy parts of even C99).
In contrast, let's look at what WG21 gave us in the same time:
Lambdas. C with the blocks extension (from Apple, supported by clang on all platforms that clang supports now) actually gives us more powerful closures, and even that part of blocks that doesn't require a runtime library (purely downward funargs) would have been a useful addition to C. Closures are really just a little bit of syntactic sugar on a struct with a function pointer as a field, if you ignore the memory management issues (which C++ did, requiring you to use smart pointers if you want them to persist longer than the function in which they're created). C++14 made them even nicer, by allowing auto as a parameter type, so you can use a generic lambda called from within the function to replace small copied and pasted fragments.
Atomics, which were provided by the library and not the language in C++11. Efficient implementations use compiler builtins, but it's entirely possible to implement them with inline assembly (or out-of-line assembly) and they can be implemented entirely in terms of a one-bit lock primitive if required for microcontroller applications, all within the library. They scale down to small targets a lot better than the C versions (which require invasive changes to the compiler if you want to do anything different to conventional implementations).
Threads: Unlike the C11 mess, C++11 threads provide useful high-level abstractions. Threads that can be started fro
I am TheRaven on Soylent News
Exceptions are possible in C. See the documentation for setjmp() and longjmp().
That said, exceptions are just "kicking the can down the road" for error handling. If a function call can fail, then you should check the return code. If you don't want to write with proper error reporting/recovery code immediately, there is always the assert() macro, e.g.:
if(func_which_might_fail() == ERROR_OCCURRED) assert(0);
If assert(0) gets called the program will stop immediately, and you can inspect the problem in detail with a debugger. Easy peasy.
Urgh, no. Never, ever do this. assert() usually becomes a no-op when compiling stuff without debug flags, so binaries shipped to the field will mysteriously ignore errors in ways that can't be reproduced in a debugger.
One thing that shouldn't change between debug and production code is error detection and handling. In this sense, exceptions are ideal, as handling only needs to happen as and when errors occur, and the common case of no errors incurs little (setjmp/longjmp) to no (exception tables) overhead.
Plus exceptions are less laborious to handle. You can have a single try/catch for a whole block of code, instead of testing and handling each and every function that could fail. If you write exception safe code, then that's all you need to do, and you can clean up failed operations in the exception handlers.