Parsing a string as a 64-bit integer, somehow
blogs.msdn.com
blogs.msdn.com
I'd write something like this:
long long parseLongSomehow(char *s) {
if (isspace(s[0])) {
return parseLongSomehow(s + 1);
} else if (s[0] == '-') {
return -parseLongSomehow(s + 1);
} else if (s[0] == '0' && s[1] == 'x') {
return strtoull(s + 2, 16);
} else {
return strtoull(s, 10);
}
}
That doesn't handle negative values smaller than LONG_LONG_MIN gracefully, though. Also doesn't do error handling. char *v = malloc(100000000);
memset(v, '-', 99999999);
v[99999999] = 0;
parseLongSomehow(v);
What happens?In the first branch, can compilers really not eliminate a tail call?
> What happens?
A stack overflow, presumably.
Umm, I really disagree. Recursion and tail-call semantics are enough to make it equivalent to iteration, except the state is stored in the calls instead of explicitly outside the loop. What sucks is buggy code that does not handle cases. Depending on how you view functional languages, for instance, you could argue that iteration is necessarily implemented with recursion, and not vice-versa, and that explicit looping is only necessary to serialize it into steps (instructions).
Nonetheless, with both approaches you need to guard against multiple negative signs. Not much to do with recursion. His approach would have been fine if he had a signed? flag.
There is a reason why high-reliability coding standards forbid or severely restrict recursion. It's difficult to reason about the resource usage of recursive code, and it also leads to non-obvious failure modes like this.
And I don't think it is difficult to reason about resource usage. Tail-calls are very intuitive. Some languages (e.g. scala) allow you to force a call to be a tail call (or fail with an error).
I suspect that preferring explicit over implicit is why coding standards forbid recursion. However, many algorithms become straight-up awkward when having to manipulate the "call stack" manually: see traversal of graphs.
Finally, what you were really complaining about was the fact that he didn't handle multiple dashes. It has very little to do with recursion, which is just a different way to fold the same semantics you'd still have wrong with an iterative version. It would never overflow the stack because why would you accept unlimited minuses? You're not evaluating an expression, you're parsing a known format and can fail fast.
From the article: "I had been worrying about things like -0x12345 and -9223372036854775809 and 9999999999999999999, even though those numbers would not be something a programmer would be likely to encounter"
For his project, negative hex numbers were an explicit non-goal.
Why on earth would you do this instead of just letting the underlying library call throw? Note that it will still throw for strings that parse to a value that cant fit into a long/unsigned long.