使用 std::strtod 后发生奇怪的事情
Strange things happen after using std::strtod
这是我在词法分析器的代码中用作辅助函数的一个小 lambda 函数:
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
是一个 class,其属性 value
类型为 void
,属性 line
类型为 [=16] =],以及自定义枚举 token_type
的属性 type
。在第一行我初始化一个新的,传递给它的构造函数一个值的空指针(因为我们仍然必须创建它),枚举的值 token_type
(number
因为这个标记是那种类型),以及我们在 lambda 中捕获的当前行。
i
是一个 const char*
类型的指针,它指向我们正在检查的当前字符。当我们调用这个 lambda 函数时,它保证指向一个可打印的字符。
printf
用于调试;
所以我要做的是:创建一个新的 Token
内部类型 number
;询问 strtod
我们是否可以从当前位置建立一个数字;然后检查 strtod
return 是否为非零值,在这种情况下,我们将该值存储在新的 Token
中,将我们指向的字符的位置提前到字符紧跟在数字之后,return Token
.
如果 strtod
return 改为输入零值,我们必须仔细检查该零是否是因为找到了实际的 0,或者是否无法构建数字。所以我们检查指向数字后面字符的指针是否指向与当前位置相同的字符:如果是,则意味着 strtok
没有推进该指针,这意味着根本没有找到数字(然后我们删除分配的值和 return 一个空指针来表示)。否则,这意味着找到了一个 0,所以我们存储它,前进,并且 return.
问题是,我每次 尝试引用 after_number
时都会收到段错误字面意思 :也就是说,如果我在 buildnumber
时调用 buildnumber
=21=] 指向像 "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 == '[=19=]'
.
这是我在词法分析器的代码中用作辅助函数的一个小 lambda 函数:
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
是一个 class,其属性 value
类型为 void
,属性 line
类型为 [=16] =],以及自定义枚举 token_type
的属性 type
。在第一行我初始化一个新的,传递给它的构造函数一个值的空指针(因为我们仍然必须创建它),枚举的值 token_type
(number
因为这个标记是那种类型),以及我们在 lambda 中捕获的当前行。
i
是一个 const char*
类型的指针,它指向我们正在检查的当前字符。当我们调用这个 lambda 函数时,它保证指向一个可打印的字符。
printf
用于调试;
所以我要做的是:创建一个新的 Token
内部类型 number
;询问 strtod
我们是否可以从当前位置建立一个数字;然后检查 strtod
return 是否为非零值,在这种情况下,我们将该值存储在新的 Token
中,将我们指向的字符的位置提前到字符紧跟在数字之后,return Token
.
如果 strtod
return 改为输入零值,我们必须仔细检查该零是否是因为找到了实际的 0,或者是否无法构建数字。所以我们检查指向数字后面字符的指针是否指向与当前位置相同的字符:如果是,则意味着 strtok
没有推进该指针,这意味着根本没有找到数字(然后我们删除分配的值和 return 一个空指针来表示)。否则,这意味着找到了一个 0,所以我们存储它,前进,并且 return.
问题是,我每次 尝试引用 after_number
时都会收到段错误字面意思 :也就是说,如果我在 buildnumber
时调用 buildnumber
=21=] 指向像 "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 == '[=19=]'
.