Вот небольшая лямбда-функция, которую я использую в качестве вспомогательной функции в коде моего лексического анализатора:
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;
};
Вот небольшой фон: Token
это класс с атрибутом value
типа void
атрибут line
типа int
и атрибут type
пользовательского перечисления token_type
, В первой строке я инициализирую новый, передавая его конструктору нулевой указатель для значения (так как мы все еще должны его создать), значение enum token_type
(number
потому что этот токен относится к этому типу), и текущая строка, которую мы записали в лямбду.
i
указатель типа const char*
который указывает на текущего персонажа, которого мы исследуем. Когда мы вызываем эту лямбда-функцию, это гарантирует, что она будет указывать на печатный символ.
printf
s для целей отладки;
Так что я пытаюсь сделать это: создать новый Token
внутреннего типа number
; просить strtod
если мы можем построить число из текущей позиции; затем проверьте, если strtod
вернул ненулевое значение, и в этом случае мы сохраняем это значение в новом Token
, продвиньте позицию символа, на который мы указываем, непосредственно после цифры и верните Token
,
Если strtod
вместо этого вернул нулевое значение, мы должны перепроверить, если ноль вызван тем, что фактический 0 был найден, или если вместо этого не может быть построено число. Поэтому мы проверяем, указывает ли указатель на символ после числа на тот же символ, что и текущие позиции: если это правда, это означает, что strtok
не продвигал этот указатель, и это означает, что число вообще не было найдено (и мы удаляем выделенные значения и возвращаем нулевой указатель, чтобы сигнализировать об этом). В противном случае это означает, что 0 был найден, и поэтому мы сохраняем его, продвигаем и возвращаем.
Проблема в том, что я получаю ошибку Segfault буквально каждый раз я пытаюсь почтить after_number
то есть если я позвоню buildnumber
когда i
указывает на строку как "111"
Я получаю сегфо после распечатки "Advancing position"
, И если я позвоню, когда i
указывает на строку как "+0.0"
или же "abc()"
Я получаю сегфо после распечатки "Value was 0, comparing position to after number"
,
Почему это? Что происходит с указателем, на который указывает after_number
? Что я делаю неправильно?
Вы определили after_number
в качестве указателя на символьный указатель, а затем использовал это непосредственно, когда это на самом деле не указывает на что-нибудь полезное.
Проблема в том, что вы в основном говорите strtod
на самом деле не хранить указатель конца (потому что вы указали nullptr
в качестве адреса для хранения), тогда вы все равно попытаетесь разыменовать его — он все равно будет nullptr
и поэтому вы получите неопределенное поведение.
Правильный способ сделать это заключается в следующем:
char *after_number;
double *value = new double(std::strtod(i, &after_number));
Это на самом деле создает char *
переменная, в которую strtod
может поместить указатель конца, и передает его адрес strtod
, При выходе, after_number
будет содержать указатель на конечный символ, который остановил числовую оценку (возможно, это будет указатель на конец фактической строки, предоставленной, *after_number == '\0'
,
Других решений пока нет …