
C Programming Puzzlers - AndreyKarpov
http://stevenkobes.com/ctest.html
======
psykotic
Puzzles 9 and 12 assume sizeof(int) is 2. It's poor form for a language
lawyering quiz to make an assumption about implementation-dependent behavior.
It's especially poor form when the assumption made is long outdated and likely
to surprise--I suppose it's still true for C compilers targeting 8-bit and
16-bit microcontrollers.

Aside: I remember buying the first Programmer's Heaven CD-ROM boxed set at The
Party back in the mid-nineties. It was an incredibly valuable resource for me
in those days. The best source of programming information was through the
dial-up BBS scene, but none of the boards I frequented had a collection on the
scale of Programmer's Heaven.

~~~
X-Istence
From question 9:

"What is the output of this program on an implementation where int occupies 2
bytes?"

Question 12 is a function pointer question and doesn't say anything about ints
at all.

I'll assume you meant Question 14:

"What is the output of this program on an implementation where int and all
pointer types occupy 2 bytes?"

If you read the question you would have noticed that the assumption was put in
writing so that even if you first assumed int was 4 bytes, or pointers were 4
bytes you would still be able to find the answer correctly.

~~~
psykotic
Ah, I had clicked through to the original article which does not contain those
caveats.

Yes, I meant question 14, not 12.

------
brimpa
For question 7, can someone confirm that

    
    
      int a[][3] = {1, 2, 3, 4, 5, 6};
    

is not a typo and is, in fact, a valid array initialization? I've never come
across this and think it's rather unintuitive.

~~~
cygx
It's valid (see C99 6.7.8 §20 and §22) and equivalent to

    
    
        int a[2][3] = { { 1, 2, 3 }, { 4, 5, 6 } };

------
finnw
_Edit_ : I was wrong - see cygx's reply

\---

I'm a bit rusty on the C spec, but I think the author is wrong about #4.

The answer refers to an exception for a pointer that points one past the end
of an array.

And &a[5] would be valid based on that rule, because it points one past the
end of the array a.

However (&a + 1) is _not_ valid. It does not point one past the end of an
array (because although a is an array, it is not an _element_ of an array.)

Having said that I would be surprised if any compiler gave a result other than
"2 5". Still, it's technically undefined.

~~~
cygx
See C99 6.5.6 §7:

For the purposes of these operators, a pointer to an object that is not an
element of an array behaves the same as a pointer to the first element of an
array of length one with the type of the object as its element type.

------
grannyg00se
Stopped at #3. It seems incorrect. The answer states that foo returns x to the
power of n. But I ruled that answer out since any 0 _or negative_ n is going
to return 1.

edit: added emphasis

~~~
furyofantares
You could have ruled it out from the function signature alone:

    
    
        int foo(int x, int n);
    

This cannot be x^n, nor n^x or x*n.

~~~
raviksharma
"You could have ruled it out from the function signature alone" \- kindly
elaborate.

~~~
furyofantares
The return type is too small

~~~
raviksharma
OK. you mean result might overflow. Can't we handle it somehow? like a flag or
something.. what solution(different function signature) do you propose?

~~~
furyofantares
My point was just that if you want to nitpick that it isn't x^n for all
inputs, then the n < 0 cases are only a part of the problem since foo(10,10)
can't possibly be 10^10 regardless of the the function's body.

I'm not sure what you want me to propose a fix for. If we want a function that
calculates x^n we'd probably wouldn't write it anything like this, and we
would probably return a double and accept some inaccuracy.

Presumably what we actually want is an example of a hard to read function that
calculates a simple result so we can use it on an amusing quiz. In that case
we could change it to take unsigned n and ask what it computes in the cases
where there isn't overflow. Or even better, it would be neat if there is a
reasonably small modification that could be made so that it would calculate
x^n mod (2^32-1).

------
vainguard
In the answer to 9 I don't see why 'Evaluating ++i + ++i would produce
undefined behavior'

~~~
prodigal_erik
There's no <http://en.wikipedia.org/wiki/Sequence_point> between the two
increments, so the standard allows the compiler to do pretty much anything,
even <http://catb.org/jargon/html/N/nasal-demons.html>. The way I've heard it,
these operators were added to take advantage of pre- and post-increment
hardware support in the PDP-11 (forty years ago!), and everyone just knew what
would happen, but ANSI later wanted compilers to have enough latitude to take
advantage of other hardware which behaves differently.

~~~
vainguard
Just when I thought (after completing the puzzles) that C is reasonably
reasonable language, I learn that the execution order of 'f()+g()' is
unspecified. Wow!

------
bwarp
These are pretty much the reason that Go exists.

~~~
gue5t
These are not the problem with C. Things like a lack of support for functional
programming constructs and more powerful manipulation of structs (e.g.
iterating over fields and inspecting variable types) would be much more
improvement to C than minor cosmetic changes that would hide the details that
this quiz asks about.

~~~
bwarp
You've obviously know very little about the motivation of Go then...

~~~
gue5t
Go was obviously created in response to the problems with C; I was stating
that this exercise in particular isn't demonstrative of those problems.

