user6245072 user6245072 - 2 months ago 6
C++ Question

Strange things happen after using std::strtod

Here's a small lambda function I use as an helper function in the code for my lexical analyzer:

auto buildnumber = [&line, &i] () -> Token* {
Token* new_number = new Token(nullptr, number, line);

char** after_number = nullptr;
double* value = new double(std::strtod(i, after_number));

printf("Value got: %f\n", *value);
printf("Comparing it to 0\n");
if (*value == 0.0) {
printf("value was 0, comparing position to after number\n");
if (i == *after_number) {
printf("No number detected\n");
delete value;
delete new_number;
return nullptr;
}
}

printf("Value was different from 0, storing it in Token\n");
new_number->value = (void*) value;
printf("Advancing position\n");
i = *after_number;
printf("Returning\n");

return new_number;
};


Here's a little background:
Token
is a class with an attribute
value
of type
void
, an attribute
line
of type
int
, and an attribute
type
of a custom enum
token_type
. In the first line I initialize a new one passing to its constructor a null pointer for the value (since we still have to create it), a value of the enum
token_type
(
number
because this token is of that type), and the current line we captured in the lambda.

i
is a pointer of type
const char*
which points to the current characyer we're examining. When we call this lambda function, it's guaranteed that it will point to a printable character.

The
printf
s are for debugging purpose;

So what I am trying to do is: create a new
Token
of internal type
number
; ask
strtod
if we can build a number from the current position; then check if
strtod
returned a non-zero value, in which case we store that value in the new
Token
, advance the position of the character we're pointing to to the character directly after the number, and return the
Token
.

If
strtod
returned a zero value instead, we must doublecheck if the zero is because an actual 0 was found, or if instead no number could be built. So we check if the pointer to the character after the number points to the same character as the current positions: if it's true, it means that
strtok
didn't advance that pointer and it means no number at all was found (and we delete the allocated values and return a null pointer to signal that). Otherwise, it means that a 0 was found and so we store it, advance, and return.

The problem is, I get a Segfault error literally every time I try to deference
after_number
: that is, if I call
buildnumber
when
i
points to a string like
"111"
, I get a segfault after it prints
"Advancing position"
. And if I call it when
i
points to a string like
"+0.0"
or
"abc()"
, I get a segfault after it prints
"Value was 0, comparing position to after number"
.

Why is it? What happens to the pointer pointed to by
after_number
? What am I doing wrong?

Answer

You've defined after_number as a pointer to a character pointer and then used that directly, when it doesn't actually point to anything useful.

The problem with that is that you're basically telling strtod not to actually store the end pointer (because you've provided nullptr as the address to store it in), then you go ahead and try to dereference it anyway - it will still be nullptr and you'll therefore get undefined behaviour.

The correct way to do it is as follows:

char *after_number;
double *value = new double(std::strtod(i, &after_number));

This actually creates a char * variable into which strtod can place the end pointer, and passes the address of it to strtod. Upon exit, after_number will hold the pointer to the end character that stopped the numeric evaluation (hopefully, that will be a pointer to the end of the actual string supplied, *after_number == '\0'.

Comments