(For reference, a project I have at hand is compiled with -std=c99 -pedantic -W -Wall -Wno-sign-compare -Wno-unused-parameter -Wbad-function-cast -Wcast-align -Wchar-subscripts -Wfloat-equal -Wmissing-declarations -Wmissing-format-attribute -Wmissing-noreturn -Wmissing-prototypes -Wnested-externs -Wpointer-arith -Wshadow -Wstrict-prototypes -Wwrite-strings -Wundef -Werror and uses lint to check for improper conversion between types.)
If you told C "make me a sandwich", it'd give a warning about not having enough mustard to cover your entire body and an error about not have a large enough bun type to fit you in.
Compiler writers can do whatever the heck they want, and if dumping ALL THE WARNINGS was the priority, you'd need three lines of command line switches to turn off the "noise" warnings. Clearly, the a bunch of people see it as "noise" or don't care about writing clean code. That's more systemic of programmers.
To make it even more fun, there are many use cases for fall through switch statements. My personal favorite is duff's device, which is an unwrapped version of a serial copy that will run faster on certain architectures than a more typical serial copy will run: send(to, from, count) register short to, from; register count; { register n = (count + 7) / 8; switch(count % 8) { case 0: do { to = from++; //this is for a memory mapped register. case 7: to = from++; // use to++ for non memory mapped registers case 6: to = from++; case 5: to = from++; case 4: to = from++; case 3: to = from++; case 2: to = from++; case 1: to = *from++; } while(--n > 0); } }
But, most of the bugs I notice in the static analysis output are not possible in the model of other languages. That's... kind of depressing.
C isn't broken. Gah. Why does everyone have such a hard time understanding that? The language does //exactly what you tell it to do//. If that's overrun a buffer, then that's what you get. If it is fallthrough a switch statement, that's what you get. If it is typecast a block of memory that is too small to hold the structure, then that's what you get. In short, for many programs, that means doing stupid things at some time. For many programs, edge cases aren't considered at all. Blame C? It let me?
For every single one of the "warts" of C, there is also a usecase. This is why it will not be "fixed" and why it isn't "flawed". To remove them is to remove a useful function that could otherwise cause more machine code to be needed. THIS IS THE KEY. Minimizing machine code (overhead).
If you aren't good at managing these edge cases, don't need native code, don't need interop with other code, can take a performance hit, or don't want to use automated tools to help you, then don't use C for large programs.
Take it for what it is: a low level, mostly portable, mask over assembly. Stop complaining about C, start complaining about programmers.
But this switch/case/break statement behavior exists in lots of languages. I almost expect it. (It doesn't in Ruby and I usually have to stop and think about it when geeking out in that language.)
Most C code isn't that critical. Say, about 97% of the time.
Plan 9 doesn't have the concept of superuser. "sudo" is moot. Plan 9 is written in C.
Just two examples that come to mind.
Maybe it's not the language. Maybe it's the person using it.
There is never going to be a perfectly "idiot-proof" language that shares the power of a language like C. But many still seem to believe this is possible.
As someone who supports ordinary programmers, expecting smarts is not the right track. expecting average and mistakes is the right track. If your foundation is not robust by default, your systems will not be robust by default either.
To mix metaphors: Better to design foundations that keep the gloves on by default, then when you need to, take them off. You can see that design philosophy in Mercurial.
Suppose - for argument's sake - that C had two different pointer types: checked pointers for arrays and unchecked pointers for arbitrary memory and that conversions between the two were relatively seamless. What difference would that make, 97% of the time?
Plan 9 settled the issue.
But sometimes I think some people have come to love the mental challenge of UNIX permissions. It is like a game to them. I made a function that produces all possible permissions for a file, including the nonsensical ones, so I could display permissions as octal instead of the usual ls format. Have you ever thought about how many possibilities there are? Too many.
The setuid concept is interesting and must have seemed quite innovative at the time. Doesn't surprise me they filed a patent for it. But today, I could just as well do without the complexity.
Over time, I've come to believe C is deadly flawed, and greenfield projects should seriously investigate languages that are better designed.
Trivial errors in C like dangling elses, fallthroughs in switches, and easy buffer-overruns by default say to me that C's broken, has been broken, and will continue being broken.
For non-realtime apps, I would recommend pretty much anything but C; for realtime apps, I would be looking into things like SystemC or D and choose appropriately.
This class of avoidable bug should not be happening in 2012.