[英]Seg. fault with std::unique_ptr and ctor
對於解析器,我實際上正在實現,我在解析器中部分地擁有這些私有函數:
解析器私有方法:
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
唯一指針的移動操作基本上可以歸結為簡單的指針副本。 沒有任何理由可以使unique_ptr
任何實現在移動它們的過程中取消引用指針。 因此,此操作導致段錯誤的可能性實際上為零。
在return-statement / constructor-call中,您確實有一個(或多個)非常明顯的指針取消引用,這是id->get_string()
調用的一部分。
首先, id
指針的創建方式如下:
const Token* const id = current_token(); // store identifier
next_token(); // consume identifier
除非能夠保證current_token()
返回的任何指針在時間結束之前(或在當前解析操作的生命周期內current_token()
都有效,否則很有可能在調用next_token()
, id
指針無效,即指向不存在或已失效的Token
對象。
即使id
指針仍然指向現有的Token
對象,它也可能處於“僵屍”狀態,並且通過get_string()
從中獲取字符串是無效的操作。
如果我是你,那將是我尋找段錯誤的來源。 您可能還想在(內存-)調試器中運行它來獲取其源代碼,它可能會將您指向get_string
函數作為其源代碼,無論是在this
指針( id
指針)的取消引用期間。或在琴弦本身的構造過程中。 如果get_string
是Token
類中的虛擬函數,它也可能將您引向虛擬表查找。 無論哪種方式,我都高度懷疑這是段錯誤的原因,因為它是您所發布內容中唯一公開的危險代碼。
如您所願,錯誤被隱藏在可疑的id指針中。 我程序中的解析器通過詞法分析器通過unique_ptr接收令牌,並將其作為當前令牌正確存儲。 因此,方法current_token()返回了一個指向unique_ptr的指針,該指針將在下次調用next_token()時立即刪除。 在id中存儲指向已刪除的令牌的無效指針會導致此問題。
我以幾種不同的方式修復了代碼。
首先,我將上述幫助方法的返回類型從“ Token const *”更改為“ Token const&”,並且id變量現在僅復制get_string值,並且不執行其他與指針相關的操作。
通過這些更改,成功解決了分割錯誤問題! =)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.