How to Get Fired Using Switch Statements and Statement Expressions
blog.robertelder.org
blog.robertelder.org
It's always a fun experiment in Dunning-Kruger to see someone ranks themselves with C/C++.
A decade and a half ago, one of my professor once told me: "You don't learn to master Linux, you learn to accept that you can't master it." Same point, different context.
That they knew far from the whole language taught me much about the world and my place in it.
I'd back off on "compiler developer" - that's severely different from "compiler exploiter."
The way to avid all the ugly in the language is to have good habits. You only use the parts that work well. When I see things like Heartbleed, I think "poor guy never debugged a comms protocol in the wild in his life." That's one of those thing that's like combat - you have to defend a real product in the wild for a span of time before you "get it."
Edit: oh wow, I feel like an atheist who previously thought the conversation about how horrible Hell is was mostly just overwrought fanatics but has now caught a glimpse of Cocytus.
It's still up, but it's super slow with > 20-60 second page load times. I just moved some images into s3 which made it a bit faster. There are currently 267 people on the site according to google analytics.
I suggest tossing the site logo in s3 too (which timed out when I was trying to mirror the page at archive.is), along possibly with the JS.
As for the article, my brain felt a bit like it was in freefall, and continuously played sounds of glass and crockery breaking as I read. Thanks for the intellectual stretching exercises :P
Edit; Anecdata, almost all my signups come from users who block google analytics.
00F0 Is your job secure?
00F1 ... Do you have code to prove it?
[1] http://www.mit.edu/people/mjbauer/Purity/hackpure.html----
Thompson, Ritchie and Kernighan admit that Unix was a prank
=====================================
(This piece was found on Usenet. This is fiction, not reality. Always remember that this is not true. It's really a joke, right? -- Editor)
In an announcement that has stunned the computer industry, Ken Thompson, Dennis Ritchie and Brian Kernighan admitted that the Unix operating system and C programming language created by them is an elaborate prank kept alive for over 20 years. Speaking at the recent UnixWorld Software Development Forum, Thompson revealed the following:
"In 1969, AT&T had just terminated their work with the GE/Honeywell/AT&T Multics project. Brian and I had started work with an early release of Pascal from Professor Niklaus Wirth's ETH Labs in Switzerland and we were impressed with its elegant simplicity and power. Dennis had just finished reading 'Bored of the Rings', a National Lampoon parody of the Tolkien's 'Lord of the Rings' trilogy. As a lark, we decided to do parodies of the Multics environment and Pascal. Dennis and I were responsible for the operating environment. We looked at Multics and designed the new OS to be as complex and cryptic as possible to maximize casual users' frustration levels, calling it Unix as a parody of Multics, as well as other more risque! allusions. We sold the terse command language to novitiates by telling them that it saved them typing.
Then Dennis and Brian worked on a warped version of Pascal, called 'A'. 'A' looked a lot like Pascal, but elevated the notion of the direct memory address (which Wirth had banished) to the central concept of the "pointer" as an innocuous sounding name for a truly malevolent construct. Brian must be credited with the idea of having absolutely no standard I/O specification: this ensured that at least 50% of the typical commercial program would have to be re-coded when changing hardware platforms.
Brian was also responsible for pitching this lack of I/O as a feature: it allowed us to describe the language as "truly portable". When we found others were actually creating real programs with A, we removed compulsory type-checking on function arguments. Later, we added a notion we called "casting": this allowed the programmer to treat an integer as though it were a 50kb user-defined structure. When we found that some programmers were simply not using pointers, we eliminated the ability to pass structures to functions, enforcing their use in even the simplest applications. We sold this, and many other features, as enhancements to the efficiency of the language. In this way, our prank evolved into B, BCPL, and finally C.
We stopped when we got a clean compile on the following syntax: for(;P("\n"),R-;P("|"))for(e=C;e-;P("_"+(u++/8)%2))P("| "+(u/4)%2);
At one time, we joked about selling this to the Soviets to set their computer science progress back 20 or more years.
Unfortunately, AT&T and other US corporations actually began using Unix and C. We decided we'd better keep mum, assuming it was just a passing phase. In fact, it's taken US companies over 20 years to develop enough expertise to generate useful applications using this 1960's technological parody. We are impressed with the tenacity of the general Unix and C programmer. In fact, Brian, Dennis and I have never ourselves attempted to write a commercial application in this environment.
We feel really guilty about the chaos, confusion and truly awesome programming projects that have resulted from our silly prank so long ago."
Dennis Ritchie said: "What really tore it (just when ADA was catching on), was that Bjarne Stroustrup caught onto our joke. He extended it to further parody Smalltalk. Like us, he was caught by surprise when nobody laughed. So he added multiple inheritance, virtual base classes, and later ...templates. All to no avail. So we now have compilers that can compile 100,000 lines per second, but need to process header files for 25 minutes before they get to the meat of "Hello, World".
Major Unix and C vendors and customers, including AT&T, Microsoft, Hewlett-Packard, GTE, NCR, and DEC have refused comment at this time.
Borland International, a leading vendor of object-oriented tools, including the popular Turbo Pascal and Borland C++, stated they had suspected for Windows was originally written in C++. Philippe Kahn said: "After two and a half years programming, and massive programmer burn-outs, we re-coded the whole thing in Turbo Pascal in three months. I think it's fair to say that Turbo Pascal saved our bacon". Another Borland spokesman said that they would continue to enhance their Pascal products and halt further efforts to develop C/C++.
Professor Wirth of the ETH Institute and father of the Pascal, Modula 2, and Oberon structured languages, cryptically said "P.T. Barnum was right." He had no further comments.
I'm confused. You can pass a structure to a function by value in C. What does he mean by this?
"In the years following the publication of K&R C, several features were added to the language [...] functions returning struct or union types (rather than pointers)"
The Asm fragment there seems like it doesn't even match the C at all; it begins with these 3 rather useless instructions which appear to be a very dumb translation of "if((char)1)", a statement that does not even exist in the C code:
movb $1, %al
testb %al, %al
jne .LBB0_2
In the first iteration the use of the jump from the switch statement jumps over these instructions, although they are executed on later loop iterations. It seems pretty reasonable that the compiler would do this, after all you are telling it to branch into the middle of a for loop comparison.IMHO if branching to that location is allowed, this would be a defect because there is a (slightly) saner interpretation: branch there to execute the case, then "return" 10 from the statement expression and compare that to i, continuing onward from there as usual. Whether the compiler chooses to use temporary variables shouldn't make any difference according to these semantics. If we "linearise" that example according to such a principle, it may become something like this:
int i = 0;
// switch(i)
if(i == 0)
goto case0;
else
goto switch_end;
// i = 0; // unreachable
// goto loop_check; // unreachable
loop_body:
i;
// loop increment
i++;
case0:
// if there was code for case 0 it would go here
loop_check:
int r = 10; // result of statement expression
if(i < r)
goto loop_body;
switch_end:
return 0;
...and that does not cause any uninitialised reads or writes. Also, the extremely contorted control flow in some of these examples reminds me of continuations.The jump into the function parameter is puzzling at first (and once again shows what I'd consider a defect since it crashes), but using the same semantics of "continue at the remainder of the statement", it can be transformed into this:
int i = 0;
if(i == 0)
goto case0;
else
goto switch_end;
case0:
// if there was code for case 0 it would go here
int r = 1;
// continue with evaluating the parameter
int p = i + r + i;
// and call the function
f(p);
switch_end:
return 0;The code you wrote did one additional thing: it moved the "switch" starting point from before the "i=0" initialization to after the "i=0" initialization, which is more than just a different interpretation, it's completely different semantics.
int i = 0;
switch(i){
for(i = 0; i < ({case 0:; 10;}); i++){
The compiler probably tried to be intelligent and optimized it away.EDIT: Okay I tried reading the assembly and I think I understand whats going on:
movl $0, -8(%rbp) # translation of i = 0
... # the following is jumped over
movl -8(%rbp), %eax
movl %eax, -16(%rbp) # 4-byte Spill # i is moved to somewhere else in memory
... # the switch jumps between these
movl -16(%rbp), %eax # 4-byte Reload # tries to reload i from its "new" position
Whoever wrote the code to place variables into memory obviously didn't consider this kind of insane control flow.to be fair MSVC doesn't support a lot of things that are in the standard either
As for whether they were well thought out, consider that you have the same initialization scoping problem with C99's compound literals, and more generally with any automatic-storage variables. You cannot jump over (using goto or switch) the declaration or definition statement that instantiates the variable. It's doubly stupid when you do it with statement expressions because the warning is right there in the very small, concise section of the GCC manual explaining them. And nobody should be using a construct without understanding the rules for using it, which typically means reading the rules.
You can't fix stupid.
I was actually bit by a scoping issue like this a year or two ago. I was using a compound literal in some code that a drive-by patch submitter made work with C++. Now, I'm not a C++ programmer, but I knew that C++ didn't support C99 compound literals. Yet I stupidly assumed that because GCC and clang accepted the construct syntactically (and without warning) they'd implemented the same semantics. Well, they didn't, at least not after somebody "fixed" those compilers to give that syntax the semantics of C++ temporaries. (Which made no sense to me; it's a parse error in standard C++, and subtly changing the semantics like that was bound to break code.) The difference was important, because compound literals have block-scoped lifetimes, whereas C++ temporary objects only have expression-scoped lifetimes. In short, this caused perfectly legal C code to scribble over stack memory when compiled as C++.
I must assume at least some of the blame because I should have known better than to continue using a compound literal in header code that purported to support C++. OTOH, I had zero intention of ever using that code as C++; I just thought I was doing somebody a favor by accepting their patch. No favor goes unpunished, I guess.
As for Duff's Device... I love using the Duff's Device to implement a poor man's coroutine in standard C. You can write very clean and concise code this way. For example, here's a re-entrant implementation of POSIX getopt I wrote. It's probably the shortest correct implementation of getopt in C or most any other language.
https://github.com/wahern/lunix/blob/rel-20161026/src/unix-g...
I try to come up with a real-world metaphor to describe this mixture of horror and delight to non-programmers, but I am not sure there is one.