【问题标题】:Strange things happen after using std::strtod使用 std::strtod 后发生奇怪的事情
【发布时间】:2017-01-26 02:47:20
【问题描述】:

这是一个小的 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 是一个类,它具有 void 类型的属性 valueint 类型的属性 line,以及自定义枚举 token_type 的属性 type .在第一行中,我初始化了一个新的传递给它的构造函数的值的空指针(因为我们仍然必须创建它),枚举的值token_typenumber,因为这个令牌属于那种类型),以及我们在 lambda 中捕获的当前行。

i 是一个const char* 类型的指针,它指向我们正在检查的当前字符。当我们调用这个 lambda 函数时,保证它会指向一个可打印的字符。

printfs 用于调试目的;

所以我想做的是:创建一个内部类型为number的新Token;问strtod是否可以从当前位置建立一个数字;然后检查strtod是否返回一个非零值,在这种情况下,我们将该值存储在新的Token中,将我们指向的字符的位置提前到数字后面的字符,并返回@ 987654339@.

如果strtod 返回了一个零值,我们必须仔细检查该零是因为找到了实际的 0,还是因为无法构建数字。所以我们检查指向数字后面的字符的指针是否指向与当前位置相同的字符:如果为真,则意味着strtok 没有推进该指针,这意味着根本没有找到数字(我们删除分配的值并返回一个空指针来表示)。否则,这意味着找到了一个 0,因此我们将其存储、前进并返回。

问题是,当我尝试尊重after_number 时,我会收到一个Segfault 错误每次:也就是说,如果我在i 指向像@987654345 这样的字符串时调用buildnumber @,在打印 "Advancing position" 后出现段错误。如果我在i 指向"+0.0""abc()" 之类的字符串时调用它,则在打印"Value was 0, comparing position to after number" 后会出现段错误。

为什么会这样? after_number 指向的指针会发生什么变化?我做错了什么?

【问题讨论】:

  • i == *after_number 导致未定义的行为 - 取消引用空指针
  • 这不小。
  • @n.m 好吧,如果你去掉 printf 行,它不会太长。您还可以使用&& 将“比较为0”之后的两个ifs 压缩成一个。

标签: c++ c++11 segmentation-fault strtod


【解决方案1】:

您已将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'

【讨论】:

  • 我发誓我曾经这样做过,但它引发了一些错误,我切换到错误的方式认为它会解决它。现在它无论如何都在工作。非常感谢。
  • 那不是UB吗?如果strtod 检查给定指针是否为nullptr,这意味着它必须读取该变量并且读取不确定值(char *after_number;)是UB。
  • @Timo:&after_number是明确定义的,是指针变量本身的地址,而这个变量在这里的栈中存在,所以不能是nullptr .确实after_number 可能是任意值,但这与此处无关。
  • ofc 这是有道理的。出于某种原因,我的印象是检查 after_number 指针是否为空,而不是检查指向该变量的指针。我的坏
猜你喜欢
  • 2013-02-27
  • 1970-01-01
  • 1970-01-01
  • 2022-06-19
  • 1970-01-01
  • 2018-08-10
  • 1970-01-01
  • 2011-12-05
  • 2022-01-22
相关资源
最近更新 更多