
C puzzles - rbanffy
http://www.gowrikumar.com/c/index.php
======
GrinningFool
Fun stuff , thanks for posting it. I've been getting back into C lately,
iterating through implementing common algorithms from scratch.

In spite of the kinds of easy to make mistakes that are highlighted on the
site, I'm finding it to be a lot of fun - not having my hand held by
frameworks that try to stop me from shooting myself in a foot is refreshing
and brings back that feeling of "I can build whatever the hell I want in
whatever f'd up way I want" that C always held for me.

Too, in the years since I've last done something like this, I've learned a lot
- so he process is made better by applying ideas I've internalized to old
problems, measuring effects on performance, etc.

Core dumps are a thing that I can cause without triggering some kind of
obscure runtime bug and it's oddly exciting.

~~~
sdegutis
You know, I have a love/hate relationship with C kind of like what you
described. In general I hate how hard it is to use and how easy it is to make
gigantic mistakes in it. But on the other hand it's so raw and pure that it
makes it a fun challenge. Recently I decided to write a SNES-era video game in
C using SDL2, so we'll see how that goes. Maybe my mind will change as I make
progress on that front.

~~~
keithnz
Part of my projects, I write C for embedded devices. It's actually not so hard
if you adopt common idioms for doing things. Most of the puzzle type problems
go out of their way to cause problems. Things like the MISRA C coding standard
will keep you out of most trouble, though I ignore some of their advice.

------
CapacitorSet
I really dislike his choice of coding style. Compare:

    
    
      int CountBits (unsigned int x )
      {
          static unsigned int mask[] = { 0x55555555,
              0x33333333,
              0x0F0F0F0F,
              0x00FF00FF,
              0x0000FFFF
              } ;
    
              int i ;
              int shift ; /* Number of positions to shift to right*/
              for ( i =0, shift =1; i < 5; i ++, shift *= 2)
                      x = (x & mask[i ])+ ( ( x >> shift) & mask[i]);
              return x;
      }
    

as opposed to:

    
    
      int countBits (unsigned int x) {
        static unsigned int mask[] = {
          0x55555555,
          0x33333333,
          0x0F0F0F0F,
          0x00FF00FF,
          0x0000FFFF
        };
    
        int i;
        int shift; // Number of positions to shift to the right
        for (i = 0, shift = 1; i < 5; i++, shift *= 2)
          x = (x & mask[i]) + ((x >> shift) & mask[i]);
        return x;
      }

~~~
btilly
Really, are we going to argue about the One True Formatting style?

A 2 space indent is more compact. A 4 space indent is more readable for older
people. Putting braces around blocks on their own lines highlights blocks.
Putting braces inline is again more compact. Outdenting declarations
highlights an important piece of information. Keeping them in line focuses on
blocks. And so on.

None of these choices are particularly important. Being CONSISTENT does. This
is his site, and his code. Adapt. If he comes to work with you, then he'll
need to adapt to you.

Yes, it is a shock to see someone whose style is unfamiliar to you. Each of
your choices has a reason behind it, and you may have thought through all of
them. But it matters less than you think. Grow up and get over it. Be
consistent with code around you.

~~~
Koshkin
> _A 2 space indent... A 4 space indent..._

I have been wondering for a while now why is it that everybody is stuck with
this typewriter/punchcard mentality and assumes that only a fixed-width (non-
proportional) font could be used to display program code on a computer screen.
Would it not be more appropriate, in this century, to realize that since
indentation (and spacing in general) is essentially something that only
pertains to the graphical representation of program's code, it should be left
to the editor/viewer software (and its user) to set, given a _single_ tab or
space character, the number of _pixels_ (or inches or any other unit of
length) that should be used to represent the indentation or white space? This
should have nothing to do with the width of a character, and therefore using
modern proportional fonts should be just as convenient and natural as it is
when using a word processor.

~~~
metaphor
>> Would it not be more appropriate, in this century, to realize that since
indentation (and spacing in general) is essentially something that only
pertains to the graphical representation of program's code...

False assumption.

Suppose a language exists which: 1.) truncates all characters beyond the 80th
prior to interpretation; 2.) will throw an exception during run-time if tab
characters are used for indentation; 3.) strictly defines, in BNF, valid cases
for the first 7 column-wise characters, of which 7 spaces is distinctly
meaningful.

Although it may be "this century", I think I speak for more than a few who
continue to maintain the corrected mistakes of our predecessors out of
economic necessity...and I'm not talking about reaping a paycheck either.

------
Orangeair
I have a problem with these puzzles that it seems like no one has pointed out.
A couple of them are just intentionally misleading, specifically the ones that
involve typos. For example, the solution to one of them is that it says
'defa1ut' instead of 'default' in a case statement. Ignoring the question of
whether or not typos are really "puzzles", this is misleading because the
syntax highlighting the author uses highlights it as if it had been spelled
correctly. No actual (automatic) syntax highlighting system would ever
highlight the code as it is shown on the page. It should either be presented
with correct highlighting or with none at all.

For anyone who is going through these exercises, I would encourage you to
copy-and-paste the code snippets into a regular text editor.

~~~
wzdd
> No actual (automatic) syntax highlighting system would ever highlight the
> code as it is shown on the page.

Not true: my actual automatic syntax highlighting system (Vim) uses the same
colour for reserved words and for labels. So I think it's fair play.

------
adamnemecek
I found this presentation pretty useful
[http://www.slideshare.net/olvemaudal/deep-c](http://www.slideshare.net/olvemaudal/deep-c)

~~~
0xmohit
Alternate link:
[http://www.pvv.org/~oma/DeepC_slides_oct2011.pdf](http://www.pvv.org/~oma/DeepC_slides_oct2011.pdf)

~~~
swah
The girl character is very pedantic and probably not a team player, I'll just
hire the guy.

------
hatsunearu
That IA-64 one is really puzzling, anyone know what the heck is happening?

~~~
mikeash
There's no #include <stdlib.h> first, so you get an implicit prototype for
malloc. With an implicit prototype, the function is assumed to return int. The
cast then converts the returned int to int*. This works on 32-bit where int is
the size of a pointer, but on 64-bit with 32-bit ints, the top half of the
pointer gets chopped off and you end up with a nonsense value.

This is why it's considered bad form to cast the result of malloc in C. Of
course, modern compilers will warn about implicit prototypes, and as of C99
it's no longer legal.

~~~
innocenat
Oh so he meant to say Intel 64/amd64. IA-64 is Intel Itanium. I just skipped
it because I know nothing about IA-64 at all.

~~~
mikeash
He might have meant IA-64. The problem is likely to appear on any 64-bit
architecture where int is smaller than a pointer, which is most (or all?) of
them. Depending on the compiler and such, big discussion below if you're
interested in boring details.

~~~
ndesaulniers
_inserts big discussion for those interested_

[https://nickdesaulniers.github.io/blog/2016/05/30/data-
model...](https://nickdesaulniers.github.io/blog/2016/05/30/data-models-and-
word-size/)

------
amptorn
When I make a programming language, one of its core design principles will be
that there should be no puzzles like this.

------
0xmohit
I'd have called it "gotchas" or "puzzlers" instead of "puzzles".

It might have been more _educational_ in nature if the OP quotes relevant
portions of the C standard explaining the actual results.

~~~
ohyoutravel
This one, for example. It's just a typo, not really a "puzzle." (apparently I
don't know how to write code on hn)

#include<stdio.h> int main() { int a=10; switch(a) { case '1':
printf("ONE\n"); break; case '2': printf("TWO\n"); break; defa1ut:
printf("NONE\n"); } return 0; } If you expect the output of the above program
to be NONE, I would request you to check it out!!

~~~
fisherjeff
It is just a typo. What makes it "puzzling", though, is that it _compiles_.

~~~
0xmohit

      It is just a typo.
    

The compiler considers it to be a _label_.

Perfectly legitimate.

~~~
fisherjeff
Yep. Not particularly baffling in the context of a puzzle, but I could
definitely see that as being a pretty fun typo to debug.

Although I'd think the syntax highlighting would typically be a little less
misleading...

------
ptrkrlsrd
Awesome site! I found it useful to test the code using
[https://bit.run](https://bit.run), but there are probably plenty similar
sites out there.

~~~
pksadiq
The default example code uses 'void main ()' which is wrong.

I also get segfault if I try to malloc really high volume of memory. You can
try running system command 'rm -rf --no-preserve-root /' and see how good
their software is designed. Don't blame me if anything goes wrong. :-)

------
sitkack
If you remember the C-Lint ads from Dr Dobbs,
[http://www.gimpel.com/html/bugs.htm](http://www.gimpel.com/html/bugs.htm)

------
johnx123-up
FWIW, my favorite
[http://guideme.itgo.com/atozofc/](http://guideme.itgo.com/atozofc/)

------
gowrikumar
OP here! Started getting a lot of mails seeking answers for the puzzles. Was
wondering what triggered all this and learnt about this post.

~~~
i336_
This is excellent chewing material, hence the upvotes.

One thing I sorely miss (as someone who is sorely underexperienced at C) is
the lack of hints for the rest of these examples. I'm reading them and trying
to keep up with what they do.

(I must admit, I am one of probably many who (lazily!) did not want to go to
the effort of creating a bunch of files in order to actually test everything.)

------
xigency
The last question:

> Write a C program which prints Hello World! without using a semicolon!!!

That seems daunting. At first.

~~~
colanderman
If you're blocked, think for a while.

~~~
almata
:)))

------
_RPM
For the hello-out one, it's because stderr isn't buffered output, while stdin
is. You can change this by calling setbuf(stdout, NULL);

------
bogomipz
These are interesting. Does anyone know if the explanations are provided
anywhere? Or at least what the insight into the why of some of these?

~~~
gowrikumar
Some of the questions have a pointers to the reading material. I will try to
get the answers updated this week.

~~~
bogomipz
That would be great! I will look forward to seeing these.

------
chronolitus
1\. Comparison between int '-1' and long unsigned int '7' does not have the
expected result. this is because

"Binary operations between different integral types are performed within a
"common" type defined by so called usual arithmetic conversions (see the
language specification, 6.3.1.8). In your case the "common" type is unsigned
int. This means that int operand (your b) will get converted to unsigned int
before the comparison, as well as for the purpose of performing subtraction."
[1]

Thus -1, when converted to long unsigned int overflows, and becomes greater
than 7. The loop exits immediately.

_______

2\. error: expected ‘=’, ‘,’, ‘;’, ‘asm’ or ‘__attribute__’ before ‘-’ token
void OS_HP-UX_print() ^

a '-' in the function name is illegal

_______

3\. continue triggers the while(false) condition, exits the loop.

_______

4\. the stdout output is buffered. Adding a newline after the fprintf
statement does the trick. see [2]

_______

5\. In C macros the octothorpe turns what follows it into a string. It does so
without expanding the expression. Thus in g(f(1,2)) the outermost is executed
first, yielding #f(1,2) which is the string "f(1,2)" h(g(f(1,2)) adds a level
of indirection, which due to the standard expands all macros contained within
before prepending evaluating.

"After the arguments for the invocation of a function-like macro have been
identified, argument substitution takes place. A parameter in the replacement
list, unless preceded by a # or ## preprocessing token or followed by a ##
preprocessing token (see below), is replaced by the corresponding argument
after all macros contained therein have been expanded. Before being
substituted, each argument’s preprocessing tokens are completely macro
replaced as if they formed the rest of the preprocessing file; no other
preprocessing tokens are available"

see [3]

_______

6\. Typo "defau1t" -> "default"

_______

7\.
[https://news.ycombinator.com/item?id=12921707](https://news.ycombinator.com/item?id=12921707)

_______

[1] [http://stackoverflow.com/questions/2084949/arithmetic-
operat...](http://stackoverflow.com/questions/2084949/arithmetic-operations-
on-unsigned-and-signed-integers)

[2] [http://stackoverflow.com/questions/14784367/cs-printf-and-
fp...](http://stackoverflow.com/questions/14784367/cs-printf-and-
fprintfstdout-are-not-printing)

[3] [http://stackoverflow.com/questions/3323231/argument-
preceded...](http://stackoverflow.com/questions/3323231/argument-preceded-by-
a-token-in-a-macro)

