赛格。 std :: unique_ptr和ctor的错误

时间:2014-12-10 19:17:04

标签: c++ segmentation-fault smart-pointers move-semantics unique-ptr

对于我实际实现的解析器,我在解析器中部分拥有这些私有函数:

Parser私有方法:

    Token const* current_token() const;
    Token const* next_token();
    Token const* peek_token();

    std::unique_ptr<ast::Expression> parse_expression();
    std::unique_ptr<ast::TypeSpecifier> parse_type_specifier();
    std::unique_ptr<ast::VariableDeclarationStatement> parse_variable_declaration();
    std::unique_ptr<ast::Statement> parse_function_definition();
    std::unique_ptr<ast::Statement> parse_top_level_statement();

parse_variable_declaration方法的实现是这样的:

parse_variable_declaration():

std::unique_ptr<ast::VariableDeclarationStatement> Parser::parse_variable_declaration() {
    next_token(); // consume 'var'

    if (current_token()->get_type() != TokenTypes::identifier) {
        throw parser_error(current_token(), "", "expected identifier\n");
    }
    const auto id = current_token(); // store identifier
    next_token(); // consume identifier

    std::unique_ptr<ast::TypeSpecifier> type;
    std::unique_ptr<ast::Expression> expr;

    auto assignment_required = true;
    if (current_token()->get_type() == TokenTypes::op_colon) { // optional type specifier
        next_token(); // consume ':'

        type = parse_type_specifier();
        assignment_required = false;
    }

    if (assignment_required && current_token()->get_type() != TokenTypes::op_equals) {
        throw parser_error(current_token(), "", "expected equals operator\n");
    }

    if (current_token()->get_type() == TokenTypes::op_equals) {
        next_token(); // consume '='

        expr = parse_expression();
    }

    if (current_token()->get_type() != TokenTypes::op_semi_colon) {
        throw parser_error(current_token(), "", "expected semi-colon\n");
    }

    next_token(); // consume ';'

    DEBUG_STDERR("parsed: variable_declaration_statement\n");
    return std::make_unique<ast::VariableDeclarationStatement>(
        id->get_string(), std::move(type), std::move(expr));
}

最后一行(返回)以分段错误结束。 它基本上调用VariableDeclarationStatement的构造函数:

VariableDeclarationStatement ctor:

VariableDeclarationStatement::VariableDeclarationStatement(
    std::string const& name,
    std::unique_ptr<TypeSpecifier> type_specifier,
    std::unique_ptr<Expression> expr
):
    m_name{name},
    m_type_specifier{std::move(type_specifier)},
    m_expr{std::move(expr)}
{}

我从昨天开始调试这个东西,似乎无法找出为什么这不能按预期工作。我想构建抽象语法树(解析器输出),其中包含指向其子节点的唯一指针(因为它们是他们孩子唯一有意义的所有者) - 这就是我尝试使用它们的原因。

控制台输出:DEBUG_STDERR

parsed: primitive_type_int // from parse_type_specifier()
parsed: integral_expression // from parse_expression()
parsed: variable_declaration_statement
[1]    12638 segmentation fault (core dumped)  ./cion_compiler

2 个答案:

答案 0 :(得分:2)

对唯一指针的移动操作基本上归结为简单的指针副本。没有理由为什么unique_ptr的任何实现会在移动它们的过程中取消引用指针。因此,此操作导致seg-fault的可能性几乎为零。

在return-statement / constructor-call中,作为id->get_string()调用的一部分,您确实有一个(或更多)非常明显的指针取消引用。

首先,id指针创建如下:

  const Token* const id = current_token(); // store identifier
  next_token(); // consume identifier

除非保证current_token()返回的任何指针在时间结束之前(或在当前解析操作的生命周期内)有效,否则很有可能在调用{之后{1}},next_token()指针无效,即指向不存在或已失效的id对象。

即使Token指针仍然指向现有的id对象,它可能处于“僵尸”状态,并且通过{{1}从中获取字符串},是一个无效的操作。

如果我是你,那就是我要寻找seg-fault的来源。您可能还希望在(内存)调试器中运行它以获取它的源,它可能会指向Token函数作为它的源,在{{1的解除引用期间指针(get_string()指针)或在构造字符串本身时。如果get_stringthis类中的虚函数,它还可以指向虚拟表查找。无论哪种方式,我都非常怀疑这是seg-fault的原因,因为它是你发布的唯一明显危险的代码。

答案 1 :(得分:0)

正如你们正确建议的那样,错误隐藏在可疑的id指针中。 我的程序中的解析器通过词法分析器中的unique_ptr接收令牌,并将它们作为当前令牌存储。 因此,方法current_token()返回一个指向unique_ptr的指针,该指针在下次调用next_token()时立即被删除。 将无效指针存储到id中已经删除的令牌会导致问题。

我用几种不同的方式修改了代码。

首先,我将上面的帮助方法中的返回类型从“Token const *”更改为“Token const&amp;”并且id变量现在只复制get_string值,并且不执行其他与指针相关的操作。

通过这些更改,成功解决了分段故障问题! =)