C sizeof problem

Started by
43 comments, last by _the_phantom_ 15 years, 10 months ago
Quote:Original post by Sneftel
Pro-tip: If you're ever asked for a zero-temporary swap, for maximum points, explain to the interviewer exactly _why_ it's slower on modern processors.


The problem for someone like myself is that I lack certain lowel-level hacker knowledge :) I can simply relate that the "compiler can do better".

I wouldn't mind hearing a more precise answer.
Advertisement
"The compiler can do better" is one compelling argument. To the compiler, where possible, std::swap means "switch the register allocations", and masses-of-XOR means "masses of XOR". The big thing, though, is pipelining. a^=b unavoidably ties up both a and b for one clock cycle, and then unavoidably keeps a tied up for the next clock cycle. Since the next instruction needs to read back from a, this means that in the simplest possible case, the three instructions are completely serialized, taking six cycles.

Now consider the temp case. The first op, t=a, takes two cycles as well, but as soon as the first cycle is complete, a is no longer tied up, and a=b can start executing. Likewise, after one more cycle, b is no longer tied up, and b=t can start executing. In this same simple pipelined processor, the total is four cycles.
Quote:Original post by ukdeveloper
This is actually bugging me, it doesn't seem right. Where did I go wrong?


Pretty much everywhere, sorry to say.

Here's a reasonable "answer key" for C. Note how few lines of actual code there are.

#include <stdio.h>#include <stdlib.h>#include <string.h>/* Just because the standard library uses cryptic names for functions doesn't   mean you should. On the other hand, there's not much sense elaborating that   the char* you're passing in is a "String" in the variable name... */void reverse(char* original) {  /* We reverse the string in place, by swapping around the chars within it. */  /* First, set up two pointers, to the first and last characters (not     including the null terminator). There will be no need to do anything     special with the terminator; we just leave it in place, because the     reverse of a string is the same length as the original. */  char* begin = original;  char* end = original + strlen(original) - 1;  /* Notice that C allows you to initialize variables at declaration, even     with complicated expressions. Don't declare something and then assign it     on the next line, or "initialize" to a "zero" value and then assign on the     next line. The point of *initial*ization is to initialize something with     its *initial* value. :) */  /* Also notice the pointer arithmetic used to find the end pointer. */  /* Now we can iteratively swap pairs of characters: the first and last,     second and second-last, etc. until we get to the middle. You should be     able to convince yourself that this reverses the string. Notice that we     don't iterate each pointer over the whole string, since that would     re-reverse it. ;) So how do we find the middle? We could do some math,     but the simple way is to note that both pointers "move" at the same rate,     so 'begin' will reach or overtake 'end' exactly in the middle. So now we     are ready: */  while (begin < end) {    /* To swap, we assign the pointed-at characters to each other. But we can't       just do one assignment and then the other, because the first assignment       would mess up the second. Instead, we need to go through a temporary       variable. Its type is 'char', because those are what we're swapping. */    char temp = *begin;    *begin = *end;    *end = temp;    /* Move the pointers towards each other. */    begin++;    end--;  }}int main() {  /* Again, initialize when you can. */  /* Note that we are not allowed to modify string literals, so we can't pass     "hello" to reverse() directly. But we can do it if the string is copied     into an array. In C, the following syntax implicitly does the trick: */  char[] source = "hello";  /* There is a string literal which is "baked" into the executable, but at     runtime, the program will make an array on the stack and copy in the     literal. */  printf("original: %s\n", source);  reverse(source);  printf("reversed: %s\n", source);  /* We don't need to return 0 explicitly. But even then, don't put parentheses     around return values: return is a keyword, not a function. (If it were a      function, how would it return? */  return 0;}


EDIT: Yeah, I suppose actually moving the pointers used for iteration would be a good idea. And of course someone noticed before I could correct it. That's what you get for writing that much by way of explanation, I guess. X_X
Quote:Original post by Zahlman
Here's a reasonable "answer key" for C. Note how few lines of actual code there are.

*** Source Snippet Removed ***

There seems to be a distinct lack of modification of the begin and/or end pointers in the loop in your function.
I'd also add that Visual Studio 2005 (what I used) barked like a pack of dogs when I didn't declare everything at the top of the function in advance of actually initialising and/or assigning it. It wouldn't compile if I tried to do inline initialisation like you did apart from setting the tempString pointer to null.

Apparently it's a requirement of C99 but maybe it's just the VS compiler being picky? I don't normally use C so I probably had the compiler options set up wrong.
@ Zahlman: Your code don’t compile on my compilers. You have written char[] source but I think the only valid way to declare an array in C is putting the [] after the identifier. So it should be char source[]. I think is just a typo.

Quote:I'd also add that Visual Studio 2005 (what I used) barked like a pack of dogs when I didn't declare everything at the top of the function in advance of actually initialising and/or assigning it. It wouldn't compile if I tried to do inline initialisation like you did apart from setting the tempString pointer to null.

Apparently it's a requirement of C99 but maybe it's just the VS compiler being picky? I don't normally use C so I probably had the compiler options set up wrong.

If with inline initialization you mean the initialization at declaration than it works on Visual Studio. Can you post an example of this strange behavior?
In C89 the variables should be declared at the start of the block, C99 have removed this limitation (but Visual Studio don’t support C99). I don’t know if there are compilers with 100% C99 support. It isn’t a very successful standard.
Quote:Original post by loufoque
In C++ the more efficient and elegant way to solve that is to simply use a range adaptor that reverses the range.

make_reversed_range(your_range) and there you go, you get a range that you traverse in reverse order.
The old variable is kept unmodified, and it is O(1). An access to the first element will simply automatically be translated to an access to the last element.

A pipe syntax is nice too, your_range | adaptors::reversed.

All of this is doable with the range algorithms and adaptors available in Boost.

(For those who do not know about the range concepts, a range can be any container, a pair of iterators, or any other type that allows extracting of a begin and an end iterator)

By the way, note that your code is not const-correct, and that's bad.


Pulling that sort of stuff out in an interview I was conducting when asked to reverse a string would immediately lose you the job. Pulling in a ton of boost for a ~5 line trivial function is not doing anyone any favours.

NB: His code is const correct - the intention is to modify the string.
It's generic lazy computation that doesn't modify the original structure nor allocate a new one. It's O(1). It's extremely simple. It's a few characters of code. The name is explicit and there is no doubt to what it does.
That's one of the best solutions. The second best solution is an eager solution that modifies it in-place, but it's better to do it generically for any bidirectional range. I can't think of any case where the eager solution would be a better choice however.

That's not over-engineering. That's using the right generic tool to do the job, so that the tool only needs to be implemented once and can be reused anywhere providing you use the right abstraction.

I personally wouldn't want to work on a job that has so lame interviews anyway. It's such a trivial problem, and they expect a really specific non-elegant solution that a sane person wouldn't ever use in real life.

If they're against boost, you can rewrite it yourself. It's fairly easy. But if they have the NIH syndrome, that's another reason not to work there anyway.

Quote:NB: His code is const correct - the intention is to modify the string.

As a matter of fact, he doesn't.
His source string is a string literal anyway, and it is not allowed to modify it. In C++ string literals are const char[N] to prevent that kind of mistakes.
His code is extremely bad anyway, even for C.

Quote:I don’t know if there are compilers with 100% C99 support. It isn’t a very successful standard.

GCC has decent support for it. But only EDG has full C99 support IIRC.
Quote:Original post by loufoque
That's not over-engineering. That's using the right generic tool to do the job, so that the tool only needs to be implemented once and can be reused anywhere providing you use the right abstraction.


Unless you are working on say a PS2 which doesn't have a great compiler which will blow up at the first sign of templates.
Or an embedded system.

Or, you know, the fact they said 'do it in C' [smile]

Quote:
If they're against boost, you can rewrite it yourself. It's fairly easy. But if they have the NIH syndrome, that's another reason not to work there anyway.


Boost doesnt work everywhere and you sure as hell won't be using it on certain hardware.

Add to that generalising everything isn't a great idea; generally in the real world the task is to get it done. Fix what's in front of you, keep in mind the future by all means, but wasting time over genealising stuff is just dumb if you never use it.

NIH syndrome is a problem, but ignoring KISS and YAGNI is even worse.
Quote:Original post by loufoque
That's one of the best solutions.

Except that it's not a solution, since it doesn't actually do what the interviewer asked.
Quote:I can't think of any case where the eager solution would be a better choice however.

Then you don't actually understand what your "solution" does. Creating an adapter is just that: creating an adapter. It does not actually modify the object in question and instead adds overhead to each and every access. The cost of your adapter is O(1) in creation, but also O(1) for every access. If you happen to access each and every element once, then you've now added O(n) overhead. Here's something that you might find surprising: accessing each and every element of a string isn't what you would call an uncommon operation.

Also, your adapter is no longer compatible with functions expecting a null terminated char string. So say goodbye to passing the reversed string to almost every library function ever written that expects a const char * for strings.

Quote:
I personally wouldn't want to work on a job that has so lame interviews anyway.

Then you won't be working in the software industry. Given your attitude this is probably a good thing. Every software company I've ever interviewed with has given similar low level detail problems. You may not realize this, but one of the goals of a technical interview is to figure out if you actually have the skills necessary to do the job. And one of the goals behind giving questions like this is to test if you actually know idiomatic usage of the language in question.

Quote:It's such a trivial problem, and they expect a really specific non-elegant solution that a sane person wouldn't ever use in real life.

Questioning the sanity of anyone who programs in C isn't the best way to convince people you aren't in fact an arrogant twerp who thinks he's better than everyone else just because he knows a few tricks that he doesn't actually understands the limitations of. In real life passing a string to a function that draws text is something that actually might happen. Or, heaven forfend, someone actually might want to use the string to open a file.

This topic is closed to new replies.

Advertisement