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?
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'
.