Professional Documents
Culture Documents
Puzzles: Interesting Blurbs of C: Problem 1 - The Loop
Puzzles: Interesting Blurbs of C: Problem 1 - The Loop
For the C or C++ programmers out there, heres some cute brain teasers to work through, study, decipher, then use to astound your friends. I present solutions for all of the problems h just ere; look a couple pages forward. Have fun! Note, at this point I only have a few problems. If you know any other good ones, Id love to hear from you. J
To me this was non-obvious, though just running through one or two loop iterations should reveal the magic readily enough.
No, it isnt lisp, no matter how many parenthesis I put in it. Here are your questions: What does this macro do? Why is it a really cool way to do it?
You show this to a colleague and she says, Well, why dont you use a single pointer to keep track of both the previous and next items? You intelligently reply, Huh? So she makes a challenge out of it. Replace prev and next with a single pointer while still keeping it doubly linked. The new pointer must take up no more space than one of the two old pointers. Write a bit of code to walk the resulting list. What restriction do you end up with that you didnt have before? Remember, this must be universal ISO C code, so taking advantage of platform features like half-sized near pointers is not allowed.
The goal is simple: print a minus sign 20 times. Now geek pride says you would love to fix his program by changing only one character. And upon a little thinking, you realize you can do just that. In fact, there are three different ways to change only one character so that the code works like expected. Find these way.
SOLUTIONS
The next pages show solutions. No cheating now
In other words, when the loop terminates, b holds the count of the number of bits that had been set high (1) in the starting value of a. In this example, the number 42 has three high bits.
3) Take the address of b. Since the a* were using points to address 0, and b is some offset above the start of the a-structure this means were getting the offset of b within a plus the address of a, which is zero. That is, we get the offset of b within a. 4) Typecast that number to an integer. So, in short, a is a type of structure with some member b. This macro returns the byte-offset of b within a.
struct Bar_s { int x, y, z; }; /* For 32 bit integers, FOO( Bar_s, z ) == 12, unless your compiler does odd memory layout optimizations. Hence the reason for using a macro -- it's more portable than trying to guess how your compiler aligns data. */
So why is this a cool way of finding the offset? Note that we dont actually have a real instance of the structure floating around, just the typename. In other words, this whole block of operations is a constant expression and gets reduced to a single constant number by the compiler. There is absolutely no CPU time spent calculating this expression at run-time. Ive found this macro is more quickly recognized by someone whos done embedded systems work, though it came to me by means of game programmers. It can actually be found in one of the MS Windows header files though, which helps to show how generically useful it is.
By the rules of XOR above, if you are sitting with a single node, and you know the prev pointer already in some external variable, you can extract the next pointer by this:
next = prev ^ listNode.sibling;
So at what point do you have the next or prev pointer in an external variable? At one of the ends of the list, of course, where NULL is used to terminate the list. And then you can walk along the list in either direction from there, unraveling as you go.
ListNode * head; /* initialize the list */ ListNode * next, * prev, * current; current = head; prev = NULL; while( current != NULL ) { next = current->sibling ^ prev; /* Do what processing you need with the current node */ All three pointers (next, prev, current) are valid right here */ prev = current; current = next; }
So, the added restriction is that your doubly linked list cant be circular. This is a pretty meaningless statement because I never said it was circular in the first place. But ask your geeky
friends this question. In my experience, at least half will hear doubly linked and think circularly doubly linked automatically.
2. Math
A = B A; B = B A; A = B + A;
3. Order of Operations
B = (((A + A) / 2) + (A = B) B);
The trick in this solution is that ((A + A) / 2) y ields the value A without being stored in the variable A anymore. So to this we add (A = B), which in turn evaluates to B. So the total expression has become A + B. To make this only A, we have to subtract off the extra B.
THANKS
In general these problems were presented to me as things for me to solve. Credit though is due to the people who first brought each problem or solution under my nose.
All errors are my own. Send comments, complaints, criticisms, or other problems/solutions to me alone: wyvern@dracat.net Thanks for playing. =)