
Deep C (2011) - Tomte
http://www.pvv.org/~oma/DeepC_slides_oct2011.pdf
======
GlitchMr
All the talk about `printf("%d\n", a);` and it's invalid due to a certain part
of a language. From list of undefined behaviours (J.2).

> In a context requiring two function types to be compatible, they do not have
> compatible return types, or their parameters disagree in use of the ellipsis
> terminator or the number and type of parameters (after default argument
> promotion, when there is no parameter type list or when one type is speciﬁed
> by a function deﬁnition with an identiﬁer list) (6.7.5.3).

> disagree in use of the ellipsis terminator

printf is called with the following implicit type signature (due to not
including stdio.h)

    
    
        int (char *, int);
    

However, the actual printf signature is as follows.

    
    
        int (const char *, ...);
    

While char * can be used where const char * is being used, int is not a valid
replacement for ellipsis terminator. As such, the correct answer is that it's
undefined behaviour.

(of course, most implementations will allow this code, but strictly speaking
as a language lawyer it is undefined behaviour)

~~~
Koshkin
Interestingly, with the K&R version of C we would not have such discussions at
all - printf as well as many other functions did not require declarations. So,
as long as we can assume that nobody would call printf with the format string
as, say, the second argument, everything would work without anyone giving much
of a thought. This is but one example of how simple tools reduce the
unnecessary cognitive load.

------
madhadron
I know most of this trivia except where it crossed to C++, which I don't
pretend to understand. And instead of a candidate having a deep knowledge of
this, I would rather have the compiler refuse to compile most of the examples.
Computers are good at repetitively checking things. Why should I have to?

~~~
0xcde4c3db
For C++ especially, most of these conventions and techniques co-evolved with
the language, so requiring the compiler to produce an error for failing to
follow them would be a breaking change. You might be able to configure your
particular environment to produce more errors than the standard requires,
which is what some of the mentioned warning flags are about. GCC and LLVM both
support syntax like this to flag specific warnings as errors: -Werror=reorder
(this specific one will error out if member variables aren't declared and
initialized in the same order).

~~~
goerz
I yearn for compilers that can break backwards-compatiblity and strictly
enforce best practices. This should be a achieved with a relatively simple
command line flag to the compiler (instead of hunting for the right
combination of dozens of -Werror=*, which then still let a lot of unclean code
through)

~~~
setr
Won't see widespread usage outside of learning because you can no longer trust
any compiler upgrade not requiring a rewrite to your code, as it arbitrarily
decides what the latest best practice.

What might be useful though is something like -wall printing all flags
representing current best practices, so you can have a snapshot of it when
writing new code (the problem with current -wall is again potentially
arbitrary changes on compiler updates as new warnings get added, if it
actually did warn on everything)

Then you can choose when you want to update to whatever modern best practices

~~~
goerz
That would be fine! Although, I also don't think "best practices" would be
something that changes every 6 months (maybe every 5 years), and being forced
by the compiler to rewrite code to keep up with best practices is absolutely a
good thing!

I've been the lead on a fairly large Fortran project, and we make a point to
run our test suite on as many compilers as possible, with the strictest flags
to avoid any kind of "undefined" behavior (which is rarer in Fortran than in
C). I believe it's the only way to ensure long term maintainability.

------
MichailP
The knowledge presented in the slides will advance where you are going with
your programming in the same way as knowing from which wood chess board is
made will improve your ELO rating. Although learning about wood may be more
worthwhile goal then learning about topics in these slides.

What is this hoping to accomplish? To convince candidates that they can't
print a number? Maybe instead of intimidating people they should be inspiring
them?

~~~
dreta
The knowledge presented in the slides isn’t deep; it’s essential. Can’t
imagine a C programmer not knowing that parts of an expression can be executed
in arbitrary order. Especially that this is aimed at embedded C programmers.
It’s more like playing chess not knowing what „en passant” is.

~~~
FeepingCreature
Knowledge of standards before C99 is not "essential" unless you're on a legacy
codebase. Which, to be fair, embedded, is very plausible. But then you're
looking for an expert candidate anyways.

~~~
alxlaz
There are actively-used platforms that do not have a fully C99-compliant
compiler (unpleasant, I know, but such is life :-( ). Also, many products in
the embedded field have very long lifetimes (10-15 years), during which they
at least need to be maintained. There are _a lot_ of actively-used platforms
that did not have a C99-compliant compiler ten years ago, and they are not
exactly legacy codebases.

Edit: oh -- and (this I find truly revolting) there are companies that have
not updated their coding standards and mandate that all written code should
target an older standard (usually C89).

~~~
semi-extrinsic
IIRC Visual Studio still doesn't fully support C99. In particular variable
length arrays and the _Complex type are unsupported.

~~~
marcoperaza
Both of which are now optional features in C11.

------
Koshkin
There is nothing especially deep about C. Its design is intentionally
extremely primitive, almost to the point of being a thin layer of syntactic
sugar on top of assembly. In fact, the creation of C was a reaction to a real
deep sea monstrosity that had been gaining wide-spread popularity at the time,
PL/I - a devilish mixture of COBOL, Fortran, and even assembly, all dressed in
a horrible ad-hoc syntax, yet intended for the use in both systems and
application programming - basically, C++ of its time. PL/I was selected as the
implementation language of the Multics operating system which, in turn,
precipitated the creation of UNIX.

~~~
shakna
PL/I is probably the most unpleasant language I have ever had to deal with. It
makes C, without warnings, look as safe as a nuclear bunker.

The best quote that resonates with me, has to come from an unattributed UNIX
fortune file:

> Speaking as someone who has delved into the intricacies of PL/I, I am sure
> that only Real Men could have written such a machine-hogging, cycle-
> grabbing, all-encompassing monster. Allocate an array and free the middle
> third? Sure! Why not? Multiply a character string times a bit string and
> assign the result to a float decimal? Go ahead! Free a controlled variable
> procedure parameter and reallocate it before passing it back? Overlay three
> different types of variable on the same memory location? Anything you say!
> Write a recursive macro? Well, no, but Real Men use rescan. How would a
> language so obviously designed and written by Real Men not be intended for
> Real Man use?

C, with it's almost-assembly and fairly predictable semantics was an absolute
blessing.

------
fallingfrog
Personally I find that I don't ever use any of the more oop features of c++,
especially copy constructors and assignment operators.

If I'm in a situation where I need to make a deep copy of an object, I need to
ask myself why am I making an exact copy of a complex object with lots of
internal pointers and state, an expensive operation. If I'm going to modify
the result of the copy, perhaps what I should do is write a function that
generates a new, different object based on the original one, in which case I
would be passing in a const reference to the old object and maybe some other
data about the new object I want to create. And if I'm not modifying the
object, again why not just pass a const reference? Also, the exact behavior of
all these hidden functions can be really hard to figure out. Who wants to try
and figure out how many times the copy and assignment operator is called if
you do a=f(g(a)); and pass by value? No thanks.

------
flavio81
I got up to slide 80, it was really fun; although i'm not sure if the
'knowledge' given in this slide is of really high importance (for example, it
explains that static variables are automatically set to 0 upon declaration;
i'd better wish my team would always make sure variables are initialized after
declaration, unless for performance reasons.)

~~~
alxlaz
It's a useful knowledge to have in the sense that it implies these variables
are stored in the BSS section. Unless there are highly specific reasons why
one would do this, though, I certainly agree that relying on this property is
not a good idea.

It _can_ be useful, though. Many years ago, we used it to trim the twenty
bytes or so that prevented an updated version of our firmware from fitting
into the tiny flash space of a device that had been on the market for quite
some time.

It is at the very least useful to know it when debugging code written by
programmers who thought this feature was nice and relied on it throughout the
code (either because they thought it was a good optimization to make, or
because it further obfuscated their code and thus contributed to the security
of their jobs).

------
splittingTimes
Previous discussions:

[https://news.ycombinator.com/item?id=3093323](https://news.ycombinator.com/item?id=3093323)

[https://news.ycombinator.com/item?id=6596855](https://news.ycombinator.com/item?id=6596855)

------
EliRivers
I seem to recall that one of the previous times this set of slides was
presented here (
[https://news.ycombinator.com/item?id=3093323](https://news.ycombinator.com/item?id=3093323)
,
[https://news.ycombinator.com/item?id=6596855](https://news.ycombinator.com/item?id=6596855))
, some people commented that they would hire the less knowledgeable candidate
rather than the more knowledgeable candidate. I genuinely can't remember what
the justification was. A hope that by not knowing the details of the language
and how it was implemented on typical hardware, he was a better programmer, I
think.

------
starchild_3001
It feels like some people do programming for the sake of programming (like
linguists). Others create poems and exquisite novels with it (authors). I’d
argue you don’t need to be a linguist to be a nobel (or pulitzer) prize
winning author. Why? It’d be a distraction. You’d start focusing on the wrong
kinds of things.

Linguists are still highly respectible people. Society needs them. Just that
being a great author does not require you to be one.

------
bjourne
On slide 24 they claim that your main should be declared int main(void):
[https://de.slideshare.net/olvemaudal/deep-c/24-What_will_hap...](https://de.slideshare.net/olvemaudal/deep-c/24-What_will_happen_if_you)

Is there any good reason for writing "void" in empty parameter lists? I have
never seen one and, unless there is one, that declaration is just useless line
noise.

~~~
tom_mellior
int main() is not a prototype. int main(void) is a prototype. int main()
declares main with an unknown (at this point) but fixed number of arguments
(i.e., non-variadic). Callers must guess the correct number and types of
arguments, the compiler does not enforce anything. In contrast, int main(void)
declares main with exactly 0 arguments, to be enforced by the compiler.

For main it doesn't matter much since usually you don't call it yourself, but
consider:

    
    
        int f();    // no arguments, apparently
    
        int g(int a, int b, int c) {
            return f(a) + f(b, c);  // at least one of these is fishy...
        }
    
        int f(double d) {   // oh, the caller guessed wrong. twice.
            return (d != 0.0 ? 0 : 1);
        }
    

GCC and Clang do not complain about this program even with -Wall (they do with
-Wstrict-prototypes).

------
badsectoracula
I find this interesting, but for another reason than what the authors probably
intended: the guy, who is presented as the "dumb" one (after a while the
invisible interviewer is even making jokes about him), actually shows what
many people would think intuitively would happen in their C code, so he is a
good guideline for a compiler that wants to take that into account.

Either a brand new compiler or an existing compiler like gcc/clang that adds a
new flag that performs its regular optimizations as long as they wouldn't
break common assumptions about what their C code would do. Of course it would
be hard to find these assumptions, but the linked presentation is a good
start.

Personally if i was to do something like this i'd use a simple rule: what
would be the dumbest, most straightforward way to implement a C compiler? What
_effect_ would some expression have in that compiler? Then this is what the
"unsurprising" compiler mode should do - perform any optimizations as long as
they do not interfere with that effect.

I think it would be a win/win situation for compilers to do that: they'd get
to play their performance game and also provide a surprise-free mode without
fully abandoning performance.

------
d0mine
The real answer for the first example: compiler error due to fancy quotes (“”
U+201C and U+201D).

------
Const-me
The authors discusses intricate details of the language and yet fails to fix
the obvious error in most code snippets:

error C3873: '0x201c': this character is not allowed as a first character of
an identifier

error C2065: '“': undeclared identifier

------
saagarjha
I understand why knowing what the standard is useful, but why should one try
to know what happens in a case where the behavior is undefined? It's platform,
compiler, and optimization specific and is literally just trivia.

------
tyingq
445 slides. I'm curious how long this takes to present in person.

~~~
nerdponx
Glad I checked the comments after a few dozen slides.

I think this presentation is supposed to convince you that it’s important to
have a deep understanding of both the implementation details and official
specification of your language of choice. However my take away is that C is
really complicated and has no respect for the least astonishment principle.

~~~
skrebbel
I think C predates that principle.

------
nonsince
When I opened that page my laptop's fan started audibly spinning at maximum
speed. I can't imagine what it's doing to my battery.

~~~
shakna
Well the site feels the need for analytics from LinkedIn, Bizographics,
NewRelic, Google and Score Card Research. Which seems a little excessive.

------
jwilk
Weirdly, there are code examples in the C99 and C11 standards that use "int
main()".

------
username223
PDF link, for those of us who loathe Slideshare:
[http://www.pvv.org/~oma/DeepC_slides_oct2011.pdf](http://www.pvv.org/~oma/DeepC_slides_oct2011.pdf)

~~~
sctb
Thanks! We've updated the link from
[http://de.slideshare.net/olvemaudal/deep-c](http://de.slideshare.net/olvemaudal/deep-c)
to this.

~~~
lelandbatey
After updating the link, I think the PDF file server can't handle the load. I
cannot load the PDF at all, I keep getting a "connection timeout".

~~~
stablemap
The GitHub link posted here might be better.

------
tekjenitalor
In this short example you compile, the compiler complains and then you
troubleshoot the errors reported. You don't extrapolate on the variations
possible in another dialect.

You don't optimize early, you don't overthink. You can then add platform
convention, indentation and other sugar to the base code to fulfill whatever
workplace standard or best practice you need to match.

------
jwilk
Can we replace "de." with "www." in the URL?

I bet most HN readers don't speak German.

