Hacker News new | past | comments | ask | show | jobs | submit login
Fixing C (embedded.com)
43 points by rayascott on Apr 12, 2016 | hide | past | web | favorite | 117 comments

Of all the things in C that cause problems and that could be fixed by waving a magic wand, the author chose "curly braces"?! That betrays an immense lack of imagination! Worse, it shows that the author isn't even aware of the actual causes of lost productivity and bugs in C programs.

What about buffer overflows (it happens to the best of us)? Mounds of undefined behavior exploited by optimizing compilers (even in seemingly correct code)? A type system that is strict enough to get in one's way, but not strict/powerful enough for strong guarantees?

Some years ago I read a post on a forum for electronics/embedded programming, which basically said "The system of C header files is actually very good, because without it you would have to manually declare all functions you use." This article reminded me of that.

I've always wanted a heap inspection function. Give it a pointer and it returns the amount of data allocated to it. I mean free() can figure it out, why can't I see it?

I'm sure there are some good technical reasons that make this difficult/expensive, but there aren't even any half measures available. Not even something that requires the pointer be at the start of the originally malloc()ed memory and can return an error code if the pointer is somewhere in the middle of the heap or off in lala land.

A function like that would at least make defensive programming possible and not require people to manually pass in the size of their buffer all of the time.

There is such a function, it's just not been standardised (and I agree that it is a rather large oversight on the part of the standards committee to have not realised that this is useful information which all implementations of malloc() would already have in some form):


So write your own allocator...

EDIT: That might sound snarky, it's not meant to be. I just mean that you can do something like this:


people complain about C's memory manager, but really it's one of the most flexible out there because you can do stuff like this. Well written C libraries will provide hooks to use a custom allocator if needed.

You're missing the point though. Unless you can take over the world (and the C stdlib!) with this allocator it is of limited use. You have to rewrite everything to use it to get any value out of it.

So it's great for your own internal code, but of pretty limited value once you start pulling in libraries.

No, I've plugged in custom allocators into codebases that had millions of lines of C (and even C++). Very few functions in the C stdlib allocate memory. Most that do (like strdup) are convenience wrappers.

Most decent 3rd party libraries will allow you to plug in your own allocators, and if they don't, they should. Typically you do something like #define LIBFOO_ALLOC my_alloc before including things, or they have some kind of function like setAllocator().. For example, here's how to do it for Freetype (a pretty large 3rd party library). http://www.freetype.org/freetype2/docs/design/design-4.html

If a 3rd party library didn't let me do this, I would probably refuse to use it (or patch it). And you can always search/replace malloc and free if you're using some 3rd party code that doesn't do that. It really doesn't take that long.

Also, malloc and free are specified to be weakly linked, so you can implement your own and replace the cstdlib ones even if you don't have the source available. This isn't a great option, but it works, especially if you're just using it for a diagnostic build.

If you're talking about C++, that's yet another thing the STL screwed up. You can usually find ways to work around it but yeah, it's a bigger problem.

Yes - the smallest change to C that I'd make is to make it strongly typed. The implicit coercions seem so at odds with the rest of the design of C (where everything is explicit).

When I was reading I was thinking - if you like that kind of grammar just use Ada. And then later down he specifically mentions Ada. So - why not just use that?

With Ada, it is straightforward to create the kind of tight, fast code you'd write in C. You can control your own garbage collection and it has a fabulous type system. And - unless you need templates - it can do pretty much everything C++ can do as well.

I don't use Ada due to its verbosity. That's makes refactoring hard, and I refactor constantly. But only yesterday I picked up my copy of _Concurrency in Ada_ and flicked through to look at some ideas. There's lots of good stuff in there. I guess at the back story - a team who have spent years with difficult enterprise problems, who have thought really hard about them, and then rolled their conclusions back into the platform.

In the near future, a language called "flash" or "rock" or "bourne" will hit the hn frontpage. People will be amazed by its bootstrap website, its features, and will compare it well to Go. It'll get a whole lot of momentum. Weeks later a greybeard will publish an article, "Rock vs Brand X". The author of Rock will come clean in the comments saying that Rock is just a syntax transform that feeds to gnatmake, and that the whole thing was an April Fools joke.

I think the main two reasons are: - Ada is in practice managed by AdaCore; compiler features may or may not migrate back to the free GNAT compiler. I understand that they are kind of stuck: they have their customer base, and any kind of opening up would cause revenue losses that may or may not be recovered by more customers. I think they are making the right choice - superficial as the reason may be, the syntax turns many people off (it's kind of an acquired taste for me)

If you are in the Boston area tomorrow you can ask him in person, he will be speaking at the Embedded Systems Conference:


The full conference pass is $1300 but you can watch his session and many other sponsor sessions with the free pass.

So, could we have the semantics of ADA with a different syntax?

I for one would love the strength of SPARK with a more newfangled, perhaps rusty syntax. Call it Oxidise.

Removing curly braces? I have to paraphrase Torvalds on this:

  > The answer to that is that if you need
  > more than 3 levels of indentation, you're screwed anyway, 
  > and should fix your program.
Breaking the code into manageable chunks is a huge part in writing clear, maintainable software. A high amount of curly braces implies deep nested loops and/or branches, which also implies a high complexity (measured by cyclomatic complexity).

Also, IDEs can auto insert braces and indent the code. Unless you're using notepad, I don't see the problem.


  git clone --depth=1 git@github.com:git/git.git
  find git -name '*.c' -type f | xargs grep -P '^( {4}|\t){4}' | wc -l

  git clone --depth=1 git@github.com:torvalds/linux.git
  find linux -name '*.c' -type f | xargs grep -P '^( {4}|\t){4}' | wc -l
Stupid shitty broken software. </s>

Oh, stop :p

"$ find git -name .c -type f | xargs fgrep '{' | wc -l"


"$ find linux -name .c -type f | xargs fgrep '{' | wc -l"


I'm confused, this is just counting starting braces no?

I think the idea is that Linux has two orders of magnitude more loops, so one order of magnitude more 4-level-indentations is actually an improvement over Git's code, by ratio.

It's not as if Torvalds wrote the entire codebase by himself, anyway.


Git and linux were both written (originally) by Torvalds.

And both use a fair amount of deep indentation.

Right. I wasn't really commenting on whether git/linux were coded "correctly" in either case, just that it's not a very helpful metric for the larger conversation.

Fair enough. And my (flippant) comment was on two very large, very successful projects written predominantly in C with thousands of contributors.

Whether >3 level of indentation is "good", IDK. But it happens a lot. And I'm a practical man.

If it's embedded code, it's quite possible you can't just decompose code into little bitty chunks to avoid nesting, because your maximum stack depth is fixed and very small.

Inline functions.


It is also definitely possible to go overboard with chopping up your code. I'm sure everyone has read that file where damn near every single line ended up in its own little function (in its own file with some big framework around it) and you're jumping all over the place trying to figure out what its actually doing.

Everything should be made as simple as possible, but not simpler.

>I'm sure everyone has read that file where damn near every single line ended up in its own little function (in its own file with some big framework around it) and you're jumping all over the place trying to figure out what its actually doing.

Sounds like node and its moronic packaging ecosystem.

I don't really see the problem either. A lot of this stuff I see seems to be centered around stuff that 'trips up beginners'

What I would like would be a way to portably create a stack frame and pass it around as an object and then call a function with it.

Just having a standardized way to do tail calls would be a big step in the right direction. The likes of Java could do with that too.

Doesn't this equally imply the opposite argument? You could get away with enforced indenting, remove the curly braces, and there should still be no problem.

Problem is you end up like python where writing tooling is hard/impossible because the syntax doesn't have enough redundancy. I look at python and get the feeling the guy tried to make it work with just indenting, and then had to punt and add colons.

There are a million problem with C that come before curly braces. So many of the people that venerate C either live in a reality distortion bubble or don't actually writing C. I've always found C a disaster for generating good assembly.

to start:


we see that bare C has:

- no vectorization/SIMD support

- no hinting at likely branches

- no way to prefetch memory

- no way to block inlining

- no actual inlining!

whoever decided that the compiler should be allowed to ignore the 'inline' keyword ....

additional issues off the top of my head:

- const != immutable so 'const' is relegated to being a keyword for generating compiler warning

- RVO is implicit.. so just pray it happens!

The language is frankly just too old. Half of those features probably just simply didn't exist in hardware when the language was designed

I'm desperate for a better language

> const != immutable so 'const' is relegated to being a keyword for generating compiler warning

I'm not sure what you mean here. On embedded targets, static data declared 'const' will be put in with the program memory, and so will be definitely read-only. Casting the pointer and writing to it will cause a hard fault (or segfault, or whatever the equivalent is on your platform).

#include <stdio.h>

int main(int argc, char argv) {

const int *b = 9;

b = 10;



what does (should) this print? (It compiles with warnings on llvm 7.3 / clang703 OSX)

It will print 10. There's absolutely no issue there at all. You've changed the value of the pointer which is NOT const.

    const int \*b
Means a pointer to a thing that is const. The pointer itself (which is on the stack in this case) is NOT const.

    const int *b = 9;
    *b = 10;
^^^ This will NOT compile.

    const int b = 9;
    int main() {
       int *a = (int *)b;
       *a = 10;
^^^ This will compile (possibly with warnings). If you run it on an embedded target, this will crash: b will have been put in flash, so trying to write to it is a hard-fault.

Yes, C allows you to do 'unsafe' things with pointers. You aren't going to fix that without throwing away C and starting again.

No I don't want to throw it out, I kinda like C. And you can also do seemingly weird things with it. I was trying to bring some light to how const and immutable could be conflated. A lot of people see const and think, no aspect of this is changeable.

His point was that you aren't interpreting the `const' the same way the compiler does.

To the compiler, this:

    const int *a;
    a = NULL; // perfectly OK
    *a = 0;   // does not compile
Is a pointer to a constant int, not a constant pointer to an int.

You're probably confusing it with this:

    int *const a;
    a = NULL; // does not compile
    *a = 0;   // perfectly OK
Which is a constant pointer to a regular int.

Of course you can combine both as follows:

    const int *const a;
    a = NULL; // does not compile
    *a = 0;   // does not compile
Which is a constant pointer to a constant int (which, of course, makes no sense at all in this case since 'a' is uninitialized and cannot be initialized without an unsafe cast, but it's a perfectly valid statement in C).

I think we're in violent disagreement. I fully understand what my code did, but people I've worked with and gone to school have been tripped up by statements like these. Stuff like this is all over exams.

I agree that we disagree. Your comment elsewhere:

> It's all about the underhanded trick(s) with the pointers, yours will never compile. I'm forcing clang to do horrible things.

Indicates, to me, that you see this as a strange behaviour that you're forcing the compiler into when in fact this is exactly the intended (and expected) semantics of const.

If you think that though, you haven't got a clear picture of what pointers are and how they work. The pointer itself and thing it is pointing to are two completely different variables in memory. Being unclear about the difference is likely to cause lots of problems when coding C.

Using a pointer to iterate through a string/array/other data structure, that is stored in const is a completely standard thing to do.

I do get what's going on here, maybe poor example. I was trying to show off some confused ways I've seen pointers and const used together, in attempt to confuse people. Some corollary of Poe's law maybe at play.

I like that you have an example, but I'm not getting the results you are. Perhaps there is some oddity with your odd use of pointers?

I switched the code to this:

  #include <stdio.h>
  int main(int argc, char **argv) {
    const int b = 9;
    b = 10;
And I get a very clear "error" and nothing compiled with gcc, icc, and clang. Did you maybe have a left over binary from a previous compilation? I didn't try OSX, but it's possible the real moral would be to always pay attention to warnings.

It's all about the underhanded trick(s) with the pointers, yours will never compile. I'm forcing clang to do horrible things.

There's no veneration here. It's just eminently possible to use 'C' to get work done.

Oh god! This brings back memories of the nightmarish legacy code that I had to work on, a long time ago. The previous developer apparently liked Pascal and hated C. His principal header file had something like

    #define begin {
    #define end   }
and many other Pascalisms. As a result, his code did not look like C at all. IIRC, the code had gone unmaintained since the late 80's, which is not surprising in embedded systems. The code I had to fix was truly cringe-worthy.

I vehemently disagree with Ganssle's article. Curly braces are the way to go. I am quite comfortable with pythonic indentation now, but remove one 'if' in complex code, and we have to change all the code below it manually. Cut-paste some code, and we have to manually take care of indentations. It's a pain.

Instead of his proposal, I'd want cleaner syntax for bit-wise addressing, which is currently handled via cumbersome unions or mask macros.

Isn't there the anecdote that the Bourne shell is written in this style?

I remember some famous name being very fond of ALGOL and so abused the preprocessor so his code would look like ALGOL. Makes you understand how so often you're one pretty printer away from another language.

I despise BEGIN/END blocks, and here's why:

One of the most beautiful and elegant ideas in syntax is that of pattern matching. You want your syntax to match your semantics. That means, for instance, that your arrays (being a fixed, ordered sequence of items) should be presented syntactically as a fixed, ordered sequence of symbols. Your functions (being means of converting inputs to outputs) should syntactically separate inputs from outputs. (This is why I also despise INOUT and it's brethren.) And the most elegant languages will even use pattern matching for control flow, by syntactically differentiating different branches in parallel.

Part of C's brilliance was using pattern matching to declare variable types. So when you write this:

    int *a;
If you solve for `a`, you get an `int * `, and if you solve for `* a`, you get `int`. That kind of general purpose symmetry is the goal.

However, BEGIN/END as delimiters of blocks of code are far to imperative. The words refer to positions in the code, not to the structure of the code itself. This allows it to create noisy ambiguities:

    FOR i=0; i < 10; i++
        WHILE j > 0
        END FOR
This is nonsense, because the terms are too granular and don't work in relative position. It may be more explicit, but explicit is only good when you're actually making decisions. Nobody should be deciding to overlap loops or blocks of code. Explicit is not good when there's one best way to do something, and `{}` always implicitly denotes a block. Nesting is easy, and doesn't need to be explicit. It just needs to be readable.

C, to me, requires a lot of discipline about good coding style and conventions. I personally like that a lot, even though, of course, it sometimes causes one to shoot oneself in the foot. But, and I can't point to exactly why, I don't think I want to change anything.

Maybe Rust or something new will come along sometime soon to fix all the issues that exist with C.

Oh, and for those who haven't seen it, there was a cool guide to writing neat, modern C on here a few months ago. https://matt.sh/howto-c

Worth a read in my opinion.

I personally like that a lot, even though, of course, it sometimes causes one to shoot oneself in the foot. But, and I can't point to exactly why, I don't think I want to change anything.

I feel much the same way, and I think it's the general principle of freedom over security that makes it fun. You can do lots of things that other languages wouldn't allow, and despite not needing to most of the time, the fact that such power is there if you want to use it is what I like. It's a bit of a refreshing environment compared to all the other "safe" languages.

> [...] I think it's the general principle of freedom over security that makes it fun.

That's okay, just please don't have this attitude while writing production code. If my system is compromised because of yet another buffer overflow exploit in some C library, I don't care if you felt the wind in your hair while programming it.


I think Keith Thompson's (not related to Ken Thompson) critique of the link you posted is an even better read: https://github.com/Keith-S-Thompson/how-to-c-response

howto-c has a bunch of problematic opinions which aren't liked by the community. The crap about never using ints, longs, etc has been debunked over and over.

This syntax comes close to Nim.

I am using Nim for years now, and it is much more convenient than C while retaining the same performance. C functions can be imported and used seemlessly. Nim offers several optional garbage collectors which can be turned off for embedded systems. Nim has a package manager (Babel), and Nim's macros which are powerful like Lisp macros (way ahead of C++ macros) make it possible to create special DSLs for test cases and webservers, or to use Perl's awesome regular expressions with native Perl syntax.

Homepage: http://nim-lang.org

Nim on Arduino: http://disconnected.systems/nim-on-arduino/

Embedded Stack Trace Profiler: http://nim-lang.org/docs/estp.html

Nim for scientific computing: http://rnduja.github.io/2015/10/21/scientific-nim/

A sinatra-like web framework for Nim: https://github.com/dom96/jester

"We just switched from Rust to Nim for a very large proprietary project": https://news.ycombinator.com/item?id=9050114

I know many programming languages and styles. Nim is one of the most amazing, powerful, practical and effective programming languages ever - a real gem. I am still wondering why so few people know about it. Probably just because of lack of promotion.

> Nim has a package manager (Babel)

It's called Nimble now: https://github.com/nim-lang/nimble

C has many, many problems. Curly braces are really not one of them. If I could wave a magic wand and fix C I'd make arrays and strings know their size.

Edit for the nitpickers: Yes, arrays decay to pointers and that's the real problem. Sorry.

I would make it impossible for a simple addition of two integers to result in undefined behaviour

Good news. Arrays have sizeof(arr)/sizeof(arr[0]) elements and strings have strlen(str) letters.

.. until you pass them as a function argument. And strlen() only works if your null terminator is valid, and doesn't tell you anything about available space in the string buffer for adding to the string.

This kind of thing keeps coming up in vulnerability reports.

This happens because people don't use the tools properly.

If different people keep chopping into themselves with a power tool it's probably wise to consider a safety guard.

Wrong is still wrong, and if someone did it, they did it wrong.

This really isn't complicated. It's hardly obscure. There's no excuse.

They should be using Ada instead of C, amiright /s

I have no way of knowing what they should use.

C arrays “know” their size. (String literals do too.)

You can sizeof them if the declaration is in scope. If you pass them as function argument, this information is not obtainable inside function. You have to pass length as well. This is not very elegant and sometimes you elect to process them without using functions just because you can iterate them easier.

So roll your own struct based solution to where the size is now state.

If you're talking about the old stdlib.h calls, don't use them with blind pointers where the size isn't known. I'd say use the "n" versions but you don't even have to do that.

'C' gets easier when you do faux RAII and make "objects" with internal state, at least think about not using dynamic allocation for everything and think a little bit about what might make the API usable by the extremely lazy.

Now, at one point, you stopped talking about arrays and started talking about pointers.

That's because C tosses out all of that array state information at the drop of a hat once you start passing them around to functions.

Worse, it's an easy way to get burned on the sizeof() function, especially if you at some point refactor the code and put that chunk in a function separate from the original declaration.

This is why C programmers get gunshy about relying on that information and instead just treat strings like pointers all of the time.

> This is why C programmers get gunshy about relying on that information and instead just treat strings like pointers all of the time.

Good programmers do nothing of the sort.

Exactly. When array is passed into function, you lose length information and array construct is reduced to pointer. Array syntax is just synthetic sugar for dereferencing pointer with offset.

This discussion won't get very far if all you think arrays are is syntactic sugar. The array type is useful - think in terms of a static analyser if it helps - in different ways than pointers.

"array syntax" != "arrays". He means things like a[i]. Arrays are only really arrays in their local scope, if you pass them to functions they suddenly are pointers (but for some insane reason, C compilers let you specify "the size of the array in a function argument" which doesn't actually compile to any code).

I generally don't use downvote button on social sites, but this time I really miss it.

This article is contentless... I mean there's absolutely nothing informative in it. And anyone who things that curly brackets is the thing to fix in C must have been never using C in the first place.

There's no way to make C modern language without breaking all the legacy code. But if possible, I would remove the preprocessor and cover its usage in core language.

For projects where you are free to choose your language, Nim is very modern and powerful. It compiles to C so you can use it as easy replacement.

That would fix so many errors and the notation is extremely simple.

That's definitely the best idea I've seen.

I'd make const the default, requiring a var keyword for variables. This makes it easier to get const correctness right because a missing var would break compilation while currently, a missing const doesn't. Next on my list would be := for assignment which is less easy to confuse with equality followed by variable declarations being name: type which is better once type names get long - admittedly they never are in plain C.

It takes a lot more than that to get const correctness right. Consider the strstr function. The constness of the result really ought to match the constness of the first argument.

It can get much worse. Consider a function that takes two strings as arguments, then returns the longer one. Constness of the result ideally depends on the input. At the very least, when both inputs have the same constness you'd expect the result to also have that constness.

> My vote is to get rid of the curly braces. All of us have suffered from bugs and compiler complaints from deeply-nested blocks of code where we get mixed up about how many closing braces are needed, and where they should be placed.

I cannot remember the last time this happened to me.

> I’d prefer requiring matching begin and end blocks, with the end statement indicating which block is being closed.

So you can close an outer block before closing inner blocks? Or you can omit closing of a block altogether? Why - doesn't this create loads of nasty ambiguity?

> Sure, careful indentation tells us the same thing, but so much code today has been modified by so many people that the original engineer’s careful indenting often becomes hopelessly mangled.

"Often"? Just fix that problem if, indeed, it IS a problem.

> I often put an indication of which brace is closing which block in the comments.

As others have pointed out, if you need to do this, you have bigger problems.

I do actually like Python's approach, although I don't have enough experience with it to judge if it's a big improvement.

I wonder how this article has been able to make HN's home page... The main thing the author thinks about improving C is... replacing braces with keywords?

Perhaps because some many users upvote articles with a clear and concise thesis even if they disagree with it? An article can spur productive discussion even if the premise of the article is wrong. I frequently post articles that I disagree with, hoping to see whether someone can show me why I'm wrong. That said, in this case do I agree with the consensus: of all the things, why is he complaining about braces?

I would add multiple return so api's don't suck.

There you go:

  struct much_improved_c { ... };

  struct much_improved_c an_api_that_doesnt_suck() { ... };

I always found that unnatural in C and preferred the other way around: to pass the struct to populate as a pointer and keep the return code for tracking operation status/handlers

This is the only way it can be done in statically typed language. You cannot have multiple returns without defining how many there are what are their types.

The definition of what they are and how many could just as easily be done at the function declaration. There is no requirement that multiple return types in a static language have to be declared separately and verbosely. That is just how C does it.

I mostly miss Python-like modules and namespaces in C.

Of all the things you could have chosen, you picked the most trivial? I hate loops and logic in bash because you need to always do things like this:

  for i in {1..20}
    echo $i
What is the purpose of having the `do` and `done`? And with if statements, what's the purpose of having `then` and `fi`? Curly braces fix that issue and are much easier to read. Not to mention that literally every single real editor has a plugin for making sure your braces always match up.

I think `else if` to `elif` would be a much better change to fix people who write `else <newline> if` and other things that make it hard to read code. However, it should be noted that you couldn't do some pretty horrible macro hacks that way (but maybe if you made the no-brackets format of blocks no longer valid, then that problem would be fixed).

Not to mention that `end <block>` is syntactic clutter. We all know what block it must terminate -- that's why brackets work.

When I read this, I wasn't sure if it was a joke article or not. I have read Jack's article for many years and this has to be one of his weakest yet. Complaining about curly braces...

Why don't we get rid of the semi-colon and delimit the line of code via the symbol EOL, or maybe use the carriage return.

There are many things that could be fixed in C, but adding "begin" and "end" everywhere? No, thank you.

Here is a much better start: http://blog.regehr.org/archives/1180

"Proposal for a Friendly Dialect of C"

Of all the things you could fix in C, curly braces? I would have thought that the massive security issues, weird standard library design, non-existent module system, copious undefined behaviour, weak type system or risible package management might have merited some attention first.


This is the only thing that I miss in C. I always want to declare local functions, and return pointers to them.

GNU C supports nested functions. Does that solve it (if you accept GCC lock-in)?


From GCC documentation: "If you try to call the nested function through its address after the containing function exits, all hell breaks loose."

I would definitely accept GCC lock-in, but so far GCC does not have this feature. Clang does, hoever, via _Blocks, but the syntax is not very C-like.

That would require implicit heap allocation, which is a lot to ask for in a language where malloc is a library function.

Not all closures require heap allocation.

Enriquto asked for closures which remain valid after the enclosing function has returned — upward funargs. If the closed-over variables need to survive the parent’s stack frame, where else would you put them?

The variables could be static, or optimized away, or...

I'm not saying every closure can be done without allocation. I'm saying many useful ones can be though.

You are not alone in that. A lot of the annoying stuff in C could be fixed going down that path.

I dealt with this by adding a comment to the closing curly like so: } // end of functionName

I do the same thing, but I always a bad feeling that if you have to do this your function is too big.

> Suppose you could wave a magic wand and change just one aspect of C. What would that be and why?

> I’d prefer requiring matching begin and end blocks, with the end statement indicating which block is being closed.

The author's magic wand sounds like C macro to me.

I would want it to be based on references and slices, both nullable and non-nullable. References don't allow pointer magic, therefore are always in bounds. Slices only allow sub slicing, therefore they're also always within bounds (dereferencing them would need bounds checks to be safe though).

The great problem of C is that almost everything is a pointer and they potentially can all be invalid. On top of this there's no easy way to check wether they are valid. This is the reason, why memory safety is so hard to get right with C.

Wouldn't a linter for C and a half-decent text editor address the author's issues about block formatting way less intrusively than trying to change the language syntax?

A less drastic change that would achieve the same purpose would be to require braces in selection and iteration statements, and to add an "elseif" or "elsif" keyword to replace "else if" chains. That's what Perl does, and it works pretty well.

Of course it would break a lot of existing C code, which is why it will never happen in any language named "C".

I'm gonna go with Tony Hoare here and say if I could pick just one it'd be NULL pointers.

(Braces though? Really?!?)

That's why it's a good practice to comment closing curly braces for the long blocks:

    while (1)
        < many lines of nested code >
    } // end of while

But before you do that, try pulling out functions.

Of course, but in embedded applications fairly large loops are common. It's not uncommon to see all code embedded inside a single while (1).

I might be missing something, but what is that 'begin' for? That looks useless to me

The top change I would love to see in C is to have a way to have conditional macros, that could produce efficient statically compiled chunk of texts of defines. I think you can get some of that in C++ with templates, but without the whole C++.

Regarding the curly brackets thing vs. end statements and indentation:

How is this a problem with editors/IDEs doing it all for you? If your editor/IDE does not do this, then.. why don't you have this in your setup?

You can do auto-indent in Java, PHP, Perl... but not in C/C++, where you're free to embed curly braces etc. in macros, and it's really common to do so.

To make matters worse, you actually need to compile the program in order to determine which macro gets expanded into what amount of braces...

Vim auto-indents macro blocks just fine, and preprocessing isn't compilation. For your edification, clang-format is amazing: https://youtu.be/JSjoCisIHcM?t=1764

Ah now I see. Although I understand that there are some tools that do this, it is not a trivial process like in many other languages. Thanks for enlightening me.

If curly braces is his biggest issue, it might be better to take a hint from Go and enforce them to be required in anything other than third-party headers, and enforce a single style on the codebase.

I'm surprised not to see lua on the chart, it seems to be fairly popular from what I've seen in limited resources environments.

Guidelines | FAQ | Support | API | Security | Lists | Bookmarklet | Legal | Apply to YC | Contact