7 ms·
So you think you know C: the Ksplice Pointer Challenge
- HeyItsDiogenes 15y agoTo be fair, you can't really ever get the first one right because you don't know what the memory address will look like. After that, you can get the others right.
- matthavener 15y agoThey 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.
- marshray 15y agoYep. I could have used the hint about what the output of %p looks like (I missed the leading 0x). Of course nobody's keeping score, but that doesn't seem essential to the question.
- rwg 15y agoOr they could use C99's int32_t in the problem's code to make the size of the integer explicit... (Or just state that sizeof(int) == 4 on this hypothetical system.)
- wdaher 15y agoI don't disagree, but I thought it was nicer for there to be a more concrete, actual memory address for the answer. I thought about explicitly stating "sizeof(int) is 4 on this system" in the intro blurb, but that primes you a bit more than I'd like for the answer to #2, so I thought it was a little cleaner not to.
- philh 15y agoI feel that anyone who doesn't already know "it depends on sizeof(int)" isn't going to get it anyway; I was briefly thrown off because I thought "surely if sizeof(int) matters like I think it does, I would have been told what it is?" But I'm more annoyed that I failed the last two by not understanding C than that I failed the second by guessing sizeof(int) incorrectly.
- calloc 15y agoMy first instinct was that sizeof(int) was the same width as the system architecture (64 bits)... specifically because you mentioned it was a 64 bit system. Either way, after I realised that sizeof(int) == 4 the test was surprisingly simple. Are pointers really that hard to grasp?
- TeMPOraL 15y agoIsn'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 15y agoAbsolutely not, on Windows 64 for example sizeof(void *) == 8 and sizeof(int) == 4.
- jedbrown 15y agoThat is true on 64-bit Linux too, but on Windows64 sizeof(long)==4. See LP64 versus LLP64.
- fmota 15y agoIt 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 15y agoThe 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.
- tptacek 15y agoIs there a common 64 bit architecture that is ILP64 as opposed to LP64?
- wdaher 15y agohttp://en.wikipedia.org/wiki/64-bit#64-bit_data_models http://en.wikipedia.org/wiki/64-bit#64-bit_data_models has a few examples -- but I think the answer is basically no?
- ajross 15y agoNo. The gotcha of course is on the other side. Win64 is P64, so a long won't (!!?@#!) hold a pointer. Edit: also, pedantry: the assignment of types to sizes is part of the ABI, not the architecture.
- apaprocki 15y agoIn AIX on POWER you can choose between LP64, LLP64, or ILP64.
- cjensen 15y agosizeof(int) is either 2 or 4 on all reasonable architectures. On all modern and reasonable architectures, it must be 4. Short answer: if sizeof(int)=8, then you lose either the 2 byte integer, or the 4 byte integer. This would make it harder to minimize memory footprint in programs which work with large amounts of data. The reason for this is that char, short, int, long, and long long are the only names you have for various integer sizes. Since sizeof(char) is fixed at 1 and sizeof(char) <= sizeof(short) <= sizeof(int) if you make int eight bytes, you must either drop the two-byte or four-byte integer since you only have one name left for it. Dropping one of those types will make programmers unhappy since they packing structs careful is a valid thing to do for memory minimization in large-memory programs. Note that int32_t and friends are not valid names since they MUST be a typedef for one of char, short, int, long or long long. Disclaimer: if C11 has fixed this, I'm ignorant of it.
- ajross 15y agoC99 specified stdint.h, which includes int16_t/uint16_t. So compliant compilers are required to support it even if it doesn't map to a built-in type. So you won't lose the short. That said, it wouldn't make it any less insane; so no one does this and in fact int is 32 bits everywhere except microcontrollers (and 8086, for the tiny handful of people writing BIOS or bootloader code).
- cjensen 15y agoEach of the intXX_t typedefs are optional. Each typedef is available iff the implementation has a type which is exactly XX bits.
- ajross 15y agoRight, which it does: we're talking about ABI variations within a single architecture. The OS and toolchain enforcing ILP64 vs. LP64 semantics on C programs has nothing to do with the i386's ability to operate on 16 bit chunks.
- 15y ago
- Erwin 15y agoWell, sizeof(int) (and short, and long!) could also be 1, and %p could print out the pointer address as a roman numeral. All legal according to standard. What commonly used platforms have a 64-bit ints? The only one I vaguely recall are really, really old versions of Solaris. After a while IIRC they decided that ILP64 was too much of a PITA and went with LP64.
- jedbrown 15y agoThe standard requires that "int" be able to represent from -32767 to +32767. Unless "byte" is redefined to contain a different number of bits, this means that sizeof(int) >= 2. Similarly, long has a range that requires at least 32 bits.
- Erwin 15y agosizeof(char) is 1 per definition and also one byte, but not necessarily 8 bits. Apparently some DSPs cannot address an 8-bit quantity -- they're not easy to find but I've found a comp.lang.c posting where Jack Klein mentions a 32-bit Sharp DSP with CHAR_BIT = 32 (so sizeof(char) == sizeof(int) == 1 !) and a Texas Instruments TMS32F28xx DSP with a 16 bit CHAR_BIT. On such a system, C99 might not define int8_t but you could use int8_least8_t. My guess is that they are more common than 64-bit ints but they might be supported by their own weird C Compiler/toolchain.
- caf 15y agoThe interesting thing about such architectures is that the common stdio idiom: int c; while ((c = getchar()) != EOF) { does not work properly on them.
- IgorPartola 15y agoNot 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 15y agoOr, 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; }
- bbq 15y agoTo be confused with: #include <stdio.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(void) { int x[5]; foo(&x); return 0; }
- teichman 15y agoCan you elaborate on this?
- matthavener 15y agoIn 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]).
- For_Iconoclasm 15y agoWell, 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 15y ago> 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 15y agoOn 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.)
- reidrac 15y agoI got all questions right, although I was not confident (so sizeof(int)is 4).
- mtoddh 15y agoSo for question 4 it was a matter of realizing that the type for &x is a pointer to an array of 5 ints: int (*y)[5]; y = &x; printf("%p, %p\n", y, y+1);
- cygx 15y agoJust 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 15y agoIndeed, 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.
- davidbalbert 15y agoThe 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 15y agoArrays 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 15y agoI 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.)
- caf 15y agoNitpick: It should be %zu, not %zd (size_t is unsigned).
- coliveira 15y agoThe key is that "&" gives a pointer to the declared type. If you have a variable of array type (int x[100]), the pointer is to the array type (pointer to int[100]), not pointer to int.
- Symmetry 15y agoI'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.
- tptacek 15y agoI 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.
- _delirium 15y agoSame here. For the cases where you really do want to find the first memory address past the end of the array (if you're doing some hackish memory management, probably), I think x + sizeof(x) is more idiomatic than &x+1, because it avoids that particular arrays-are-almost-pointers weirdness in favor of regular pointer arithmetic. Though I used to program a lot of C, I'm not a C wizard by a longshot, so I might be wrong. Are there cases where using expressions based on &x, where x is an array name, is idiomatic C? edit: Stupid mistake, see cygx's reply (sizeof(x) gives the size of the array x in bytes, not in elements).
- cygx 15y agoThe idiomatic way to get a pointer to one past the end of an array x with element-type foo is x + sizeof x / sizeof *x which is equivalent to (foo *)((char *)x + sizeof x) whereas x + sizeof x is equivalent to (foo *)((char *)x + sizeof (foo) * sizeof x)) The expression &x + 1 is not idiomatic as it has the type pointer-to-array-of-foo instead of pointer-to-foo. As to your final question: Parameter declarations discard the size of array types - they are actually pointer-declarations in disguise. To enforce a fixed array size, you need to declare a parameter of type pointer-to-array, eg void bar(int (*arg)[42]); which you'd have to call like this: int x[42] = { 0 }; bar(&x);
- caf 15y agoYou can also get the "correct" type (type of &x[0]) by using: *(&x + 1) I would be hard-pressed to call that idiomatic, however.
- deleted 15y ago[deleted]
- 15y ago
- jrockway 15y agoThis is the best article I've seen on HN in a year or so. Well done!
- nandemo 15y agoI disagree. As others have pointed out, some of the code has undefined behaviour, yet the article suggests otherwise.
- deleted 15y ago[deleted]
- derleth 15y agoI'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 15y agoWhat about *(x+1) that is equivalent to x[1]? Here, in principle, you do x+1 before applying *. EDIT HN formatting magic.
- mtoddh 15y agoFun 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; }
- mikeash 15y agoFrom the near-final draft of the C99 standard I happen to have sitting around: "Except when it is the operand of the sizeof operator or the unary & operator, or is a string literal used to initialize an array, an expression that has type ‘‘array of type’’ is converted to an expression with type ‘‘pointer to type’’ that points to the initial element of the array object and is not an lvalue. If the array object has register storage class, the behavior is undefined." In short, the moment you write a variable of array type and it's not one of those very limited cases, it immediately becomes a pointer to the first element. x+1 is completely valid.
- peq 15y agoI 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.
- zwieback 15y agoI 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.
- pajju 15y agoIn his machine int size is 32Bit - should have told the same before.
- pandaman 15y agoIn 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)).
- bcantrill 15y agoThis 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 15y agoThis is a big deal when you're passing multi-dimensional arrays into functions.
- argv_empty 15y agoThis 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. Keep in mind that this is Ksplice; for the work they do, this might not be meaningless arcana.
- caf 15y agoI don't need to run that to know that it calls foo exactly once. Function designators decay to a pointer to the function if they are not the subject of the & or sizeof operators, so the string of *s simply converts it repeatedly between a function designator and a function pointer.
- 0x09 15y agoSince you didn't provide the answer, I'll take a whack at it. It'll call foo() as usual, since function pointers dereference to themselves, right?
- Hitchhiker 15y agoThe 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 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.
- mikeocool 15y agoIf 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.
- migrantgeek 15y agoIf 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"
- krelian 15y agoI 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 15y agoThey ask for resumes right there in the blog post -- send it in. =)
- jules 15y agoThese 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.
- deleted 15y ago[deleted]
- TheTarquin 15y agoI 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.
- malkia 15y agoIgnoring 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/ https://sites.google.com/site/x32abi/
- delinka 15y ago"...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.
- deleted 15y ago[deleted]
- drudru11 15y agogotta 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.
- jeberle 15y agoThis is something I like about Forth. It uses explicit dereferencing, so it's clear what address is being used. http://home.claranet.nl/users/mhx/sf8/sf8.html http://home.claranet.nl/users/mhx/sf8/sf8.html
- jheriko 15y agoQuite 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 15y ago(and you can fail every bit of this test and write an OS just fine)