
So you think you know C: the Ksplice Pointer Challenge - wdaher
http://blogs.oracle.com/ksplice/entry/the_ksplice_pointer_challenge
======
matthavener
They really should be listing these answers in terms of sizeof(int) instead of
assuming all 64-bit platforms have sizeof(int) == 4. Pointers are confusing in
C, but so is the common assumption that sizeof(int) is always 4.

~~~
TeMPOraL
Isn't the assumption that sizeof(int) = platform type in bytes (e.g. 4 for
32-bit and 8 for 64-bit platforms)? I expected int on 64-bit platform to be 8
bytes in size.

~~~
shin_lao
Absolutely not, on Windows 64 for example sizeof(void *) == 8 and sizeof(int)
== 4.

~~~
fmota
It depends on your platform AND on your compiler, so just saying "Windows 64"
isn't enough information. Conceivably, there are compilers for "Windows 64"
such that sizeof(int) == 8.

Your point (that int isn't a qword on all 64bit architectures) still stands.
But your statement is potentially incorrect.

~~~
adestefan
The OS ABI basically defines what the compiler will do. While it's possible to
run a compiler in ILP64 mode on Windows 64, you won't get too far if you try
to pass a 64-bit integer >4^32 into a Windows system call.

~~~
jheriko
... or pass any 64-bit integer. Windows API calls are mostly "stdcall" and use
stack for parameter passing - pushing 8 bytes instead of 4 could be
disasterous in many ways - especially considering that the callee cleans the
stack in this case.

~~~
niklasl
There is no stdcall/ccall distinction on 64-bit Windows, there is only one ABI
convention that sadly uses a different set of registers for parameter passing
than Linux. Only the first four parameters uses registers, the rest is passed
on the stack.

------
cygx
Just to be an onerous bastard: As the code involves undefined behaviour, it's
superfluous to reason about any output.

Only values of type void* are valid arguments in case of the %p conversion
specifier as there need not be a uniform pointer representation.

~~~
pwaring
Indeed, and if you use -Wall -Wextra -ansi -pedantic you get:

pointer-test.c:7:3: warning: format ‘%p’ expects type ‘void * ’, but argument
2 has type ‘int * ’

Same goes for using -std=c99 instead of -ansi, although casting to (void *)
does get rid of the warning.

------
bcantrill
This is a bit of an idiotic question because one is not testing knowledge of
computing or software systems, but rather trivial knowledge of an arcane
corner of the language. Indeed, this question is an interview anti-pattern
that I have historically labelled the "where-is-the-bathroom-in-my-house"
question: if someone has not been in your house, they would not know, and if
someone were in your house and had to take a leak, I trust they could figure
it out. In my experience, these questions are most likely to be asked by
intellectual midgets who themselves would not be able to answer an equivalent
(but different) question.

So in the spirit of performing that experiment and exploring this interview
anti-pattern, here's my counter-challenge, which I argue is intellectually
equivalent:

    
    
      #include <stdio.h>
    
      void
      foo()
      {
              printf("in foo\n");
      }
    
      void
      main()
      {
              void (*func)(void) = foo;
    
              (************************************func)();
      }
    

What does that program do? Yeah, exactly: you just ran it. And you're
surprised, aren't you? And most importantly: who cares? Certainly not I when
I'm interviewing you -- where you can trust I will ask you deeper questions
than language arcana...

~~~
adestefan
This is a big deal when you're passing multi-dimensional arrays into
functions.

------
IgorPartola
Not to be confused with:

    
    
      #include <stdio.h>
      #include <stdlib.h>
      #include <string.h>
      int main() {
          int *x = malloc(sizeof(int) * 5);
          memset(x, 0, sizeof(int) * 5);
      
          printf("%d\n", *x);
          printf("%d\n", *(x+1));
          printf("%p\n", x);
          printf("%p\n", x+1);
          printf("%p\n", &x);
          printf("%p\n", &x+1);
      
          return 0;
      }

~~~
matthavener
Or, not to be confused with:

    
    
      #include <stdio.h>
      #include <stdlib.h>
      #include <string.h>
      void foo(int x[5]) {
          printf("%p\n", x);
          printf("%p\n", x+1);
          printf("%p\n", &x);
          printf("%p\n", &x+1);
      }
      int main() {
          int x[5];
          foo(x);
      
          return 0;
      }

~~~
teichman
Can you elaborate on this?

~~~
matthavener
In C, the array as a function parameter is different from an array as a
variable/struct member. So, as a parameter, sizeof(x) is sizeof(int*). As a
variable, sizeof(x) is sizeof(int[5]).

------
tptacek
I got the 4th question wrong; my only caveat is, professional C programmers
probably all learn to avoid constructions like this in favor of more explicit
ones.

~~~
zb
You are mistaken (as is _delirium). Pointers to arrays are fundamental to how
multi-dimensional arrays are implemented in C, and this question is designed
to test your understanding of them. (It is most certainly not about a fancy
way of getting a pointer to the first element after the array, since this
pointer has a completely different type.)

Multidimensional arrays are arrays of arrays, not arrays of pointers to arrays
(as in Java). Therefore when manipulating, say, rows of a 2-dimensional array
you are dealing with pointers to arrays like &x in this example.

It's not an every-day thing but it does come up, and in some specialised
fields no doubt it is very common.

~~~
Natsu
> (It is most certainly not about a fancy way of getting a pointer to the
> first element after the array, since this pointer has a completely different
> type.)

Have to be careful there. Going more than one after the end of the array is
undefined (see 6.17):

<http://c-faq.com/~scs/cgi-bin/faqcat.cgi?sec=aryptr>

Actually, that whole FAQ should be of interest to anyone who liked this
article. No matter how many times I read through it, I seem to find something
new.

------
jules
These questions just show that C is inconsistent. If you have int x[5]; then
sizeof(x) is 20, so x is some data of size 20. Yet if you do int y[5]; and
then y=x, C refuses to do this even though the types match. That's because it
is inconsistent: x is not really an object of size 20 to C, it is also partly
a pointer. But then again it is not really a pointer. Which it is depends on
confusing rules. If instead y and x were structs with 5 int fields, y=x would
work.

------
davidbalbert
The Ksplice blog is one of my all time favorite programming blogs. I am very
happy it's back. If you haven't seen it before, it's well worth looking at the
archive.

In question 2 I got tripped up because I assumed sizeof(int) == 8 on a 64 bit
system. I also got question 4 wrong because I didn't know that &x gives a
pointer to an array of size 5.

~~~
jrockway
Arrays in C have always confused me a little, because books say "an array is
just a pointer to the first element". Making matters even more confusing is
that most C programmers first deal with arrays in the form of character
buffers, which are logical arrays but are typed as pointers to the beginning
of the string.

Also confusing was the first time I saw this wonderful construct:

    
    
        void f(int x){
            char buf[x + 1];
            printf("%zd\n", sizeof(buf))
        }
    

Not only does it work, it's also _correct_ C99!

Writing C involves more care than usual because there are so many weird things
that are easy to not understand, and not understanding those will cause subtle
undefined behavior that allows people controlling the input data to 0wn your
computer. Frightening.

~~~
zb
I think you might be paraphrasing what the books say. What they usually say is
that arrays and pointers are _equivalent_ \- which is true. It's easy to read
that as saying that arrays and pointers are _identical_ , but that's not true
at all.

Just to make it confusing though, you can't pass an array by value to a
function. It gets automatically converted to a pointer. Arrays are unique in
the C language in this sense, and it contributes to the illusion that arrays
and pointers are the same thing. (Try passing an array by reference, though,
and all becomes clear.)

------
delinka
"...without the use of a computer."

And in this age, with technology so advanced, and computing resources so
inexpensive ... why? Because it's the geek's athletic challenge? Whomever's
brain holds the most memorized facts is the smartest brain in the world?

I'd guess someone would answer "because you need to _know_ this stuff to be
good at your programming job!" To which I reply: if you ever make the
assumption that you _know_ how some bit of code will work in a system, you're
well on your way to becoming the infallible coder that no one likes to work
with. This is why we have testing methodologies.

Yes, you need to be aware of this particular nuance of C. My answers were more
high-level ("the address of the beginning of the array", "the next int in
memory, not the next byte", etc) and I have no need to know the precise memory
locations when I can ask the computer to tell me.

------
mikeocool
If you're interested in even more detail and discussion on pointers vs arrays,
the book Expert C Programming by Peter van der Linden has several really good
chapters on the subject. Plus, the whole book is a really great read.

------
krelian
I knew all these and I am self tought. Never did too much actual programming
but I just enjoyed reading about this things and understanding how they work
so over the years I've gained a ton of technical knowledge that (according to
what I'm always reading on HN and reddit) the average programmer doesn't know
(or care to know).

Can I get a job with the Ksplice team? :)

~~~
price
They ask for resumes right there in the blog post -- send it in. =)

------
jheriko
Quite good - I almost got the last one wrong. I did immediately realised my
mistake and got it wrong again before nailing it but IRL there is no button to
press to tell you you got it wrong (unit test maybe?) - there are much worse
gotchas and more useful fringe functionality floating around though. What do
these print for example.

int aiFoo[ 5 ] = { 1 }; printf( "%d", aiFoo[ 1 ] ); static int ls_aiFoo[ 5 ];
printf( "%d", ls_aiFoo[ 1 ] );

or how about int i = 5; int aiFoo[ i ]; which is valid C99 but not C89?

At any rate - much more important than learning the minutia of C is learning
to get things done. If you are passionate about writing some OS you don't need
to be taught - you would already know or be learning. It costs nothing but
time...

~~~
jheriko
(and you can fail every bit of this test and write an OS just fine)

------
For_Iconoclasm
Well, it got me on the pointer arithmetic questions. I remembered that C
automatically handles pointer arithmetic (multiplying by sizeof(type)),
however:

Question 2: I didn't think that x+1 would be interpreted as a pointer for some
reason, so I guessed 0x7fffdfbf7f01. Wrong.

Question 4: I incorrectly thought that what I remembered about pointer
arithmetic would apply here. 1 * sizeof(int) = 0x04, so I guessed
0x7fffdfbf7f04. Wrong.

I don't work in C professionally, but I'd like to not forget things. My error
in question 2 shows forgetfulness, and my error in question 4 is from not ever
completely mastering every nook & cranny in C.

How did the rest of HN do?

~~~
colanderman
> I incorrectly thought that what I remembered about pointer arithmetic would
> apply here. 1 * sizeof(int) = 0x04, so I guessed 0x7fffdfbf7f04.

Your pointer arithmetic was correct. What you missed was that &x is a pointer
to an array of five ints (so, sizeof(int[5])), not a single int.

I on the other hand didn't realize sizeof(int) is not necessarily 8 on 64-bit
machines. You learn something new every day...

~~~
scott_s
On 64-bit machines, int is typically 4 bytes. Longs are typically 8 bytes. But
if you really need to assume a certain length, use the typedefs from ctype.h.
(For example, if you wanted a 4 byte signed integer, you would say int32_t. An
8 byte unsigned integer would be uint64_t.)

------
Hitchhiker
The third answer's explanation could be better than whats given :

" That is, whenever an array appears in an expression, the compiler implicitly
generates a pointer to the array's first element, just as if the programmer
had written &a[0] "

<http://c-faq.com/aryptr/aryptrequiv.html>

Also remember that much fun could be had by studying the pre-processor up
close .. another source of mind-bending fun.

------
migrantgeek
If we could turn pedantry into electrical power, geeks on the internet could
power the planet forever.

The takeaway summed the point up nicely although everyone's arguing over
sizeof(int). Sure, there are cases when sizeof(int) is not 4 but that's not
the point of the exercise. If you solved believing it was 8, you'd still be
correct in my opinion.

I'm reminded of so many great articles with comments like "it's you're and not
your"

------
malkia
Ignoring the sizeof(int) could be 4, one can code a 64-bit application that
uses 32-bit pointers - yes it's rare - but in reality it's a 64-bit
application, but just having small pointers - it's called the x32 abi (heh,
and I read about it first here from this forum)

<https://sites.google.com/site/x32abi/>

------
zwieback
I got all right except 2 because I thought that sizeof(int)==8 on 64 bit
systems.

Array vs. pointer is one of the more advanced interview questions I like to
use to probe C knowledge. I don't actually expect any correct answers, just
the knowledge that array != pointer.

------
peq
I am a java programmer and got the first and the last answer correct and the
last answer only because the explanation of the second and third one were good
enough to understand the last one.

Learned something, so thanks for the link.

------
jrockway
This is the best article I've seen on HN in a year or so. Well done!

~~~
nandemo
I disagree. As others have pointed out, some of the code has undefined
behaviour, yet the article suggests otherwise.

------
TheTarquin
I learned a lot from this, thanks for posting. Mostly I learned how rusty my C
skills have gotten in the years since I last used them.

Maybe time to bust out my K&R again.

------
Symmetry
I'd seen this on zephyr the last time a SIPB member posted it to their class,
but I still didn't remember the correct answer to the last question.

------
pandaman
In my humble opinion the only challenge here is guessing what sizeof(int) the
author had in mind. A mild challenge would be asking about something that is
not a part of the pointer arithmetic basics such as a[i] === i[a] and
obfuscating a bit (e.g. "const char* i[] = {"Hello","world"}; char c =
(2&*i[1])["Hello world"]; what is c? The guess about character encoding is as
good as the guess about sizeof(int)).

------
pajju
In his machine int size is 32Bit - should have told the same before.

------
drudru11
gotta say - they got me, yet I rarely play these tricks. For example, I
usually promote a pointer to an array index vs. using fixed sized arrays.

------
derleth
I'm pretty sure the code is undefined once you do x+1 (that is, treat an array
like a pointer). Arrays are auto-converted to pointers in, for example,
function calls, but until that is done doing arithmetic on them is undefined.

~~~
TeMPOraL
What about

    
    
      *(x+1)
    

that is equivalent to x[1]? Here, in principle, you do x+1 before applying *.

EDIT

HN formatting magic.

~~~
mtoddh
Fun tidbit - this equivalence also means you can write it as 1[x]:

    
    
      #include <stdio.h>
      int main()
      {
        int x[5] = {1,2,3,4,5};
        printf("%d, %d\n", x[1],1[x]);
        return 0;
      }

