c++c++11segmentation-faultstrtod

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 printfs 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?


Solution

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