Você está na página 1de 4

C Questions

have a friend whos just learning C. He gives you the following block of code, saying it doesnt work. int i; int n = 20; for( i = 0; i < n; i -- ) printf(-); 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 three techniques. A1. The three fixes are enumerated below: int i; int n = 20; for( i = 0; i < n; n -- ) printf(-);
int i; int n = 20; for( i = 0; i + n; i -- ) printf(-); int i; int n = 20; for( i = 0;-i < n; i -- ) printf(-);
Q1. You

Q2. What does this loop do? int a; /* initialize to something meaningful */ int b = 0; while( a ) { a &= a 1; b ++; } loop makes most sense if you look at the value of a in binary. Starting with just some nonzero value, lets say 42, heres all the loop iterations: a = 101010 b = 0 a = 101000 b = 1 a = 100000 b = 2 a = 000000 b = 3 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.
A2. This

Q3. All

coders at some point have to challenge this one. Goal: Write one or more lines of code to swap two integer variables in place. By in place I mean you are not allowed to use any third variable as temporary storage. However, tricking the compiler into implicitly holding a value in a register is fine. My friend and I sat and thought on this one, and came up with three different solutions. Im certain there are probably more out there. Let me know as you find them. ^_^

A3. Assume in

all cases the two variables are A and B. 1. XOR A ^= B ^= A ^= B; 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) yields 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.
Q4. One powerful

little macro Ive used a number of times is this: #define FOO(a,b) ((int)(&(((a*)(0))->b))) 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? by step, heres what the macro does: 1) Type-cast 0, or NULL, into a pointer of type a. So a must be a typename. 2) Member-select b from that pointer. So a must be a structure typename, and b some member property. 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 byteoffset 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. */ A4. Step

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 runtime. Q5. Lets say you have a doubly linked list of a large number of very small nodes. Space is at a premium in this scenario, so you need to minimize how many bytes you take up with your list pointer overhead. Your first implementation is this: struct ListNode { ListNode * next; ListNode * prev; int payload; }; 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. is to use a binay XOR (mathematically , or in C ^) to combine the next and prev pointers into a single sibling pointer. This works because XOR is non-destructive in that, given variables A, B, and C where C = A B, you can XOR any two of the variables to retrieve the third. If C = A B, then A = B C, and B = A C. XOR is also communitive, meaning A B = B A. Alright, translating that from math to a more intuitive gut understanding, this says that we can store in every node a single pointer: struct ListNode { ListNode * sibling; /* sibling = next ^ prev */ int payload; }; 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;
A5. The solution

/* 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.

Você também pode gostar