删除const指针(非指向const的指针)是否合法?代码崩溃分析
成员声明为
Lexer* const lexer_时触发段错误的问题分析 排查代码段错误时发现:将类成员声明为Lexer* const lexer_会导致程序崩溃,去掉const修饰后程序运行正常,特此询问该位置是否允许使用const。
编辑记录
- Edit1:代码已遵循“rule of five”规则,问题仍存在。
- Edit2:仅向printf的
%p格式符传递void*类型参数,问题仍存在。 - Edit3:经确认,这是一个GCC编译器Bug。
编译环境与命令
测试环境为Linux x86_64平台,使用g++ v11.3编译,编译命令如下:
g++ main.cpp -o main.x -fsanitize=address -Werror=all -Werror=extra
错误输出(带const修饰时)
运行程序触发AddressSanitizer的堆使用后释放错误,输出如下:
0x602000000010 = new Lexer 0x602000000030 = new Token 0x7ffca90b51f0 = new Expression 0x7ffca90b51f0 = start delete Expression 0x602000000010 = start delete Lexer 0x602000000030 = delete Token 0x602000000010 = done delete Lexer ================================================================= ==1232849==ERROR: AddressSanitizer: heap-use-after-free on address 0x602000000030 at pc 0x556fc889953d bp 0x7ffca90b5190 sp 0x7ffca90b5180 READ of size 8 at 0x602000000030 thread T0 #0 0x556fc889953c in ExpressionParser::Expression::~Expression() (.../main.x+0x153c) ... 0x602000000030 is located 0 bytes inside of 8-byte region [0x602000000030,0x602000000038) freed by thread T0 here: #0 0x7f5258f6f22f in operator delete(void*, unsigned long) .../libsanitizer/asan/asan_new_delete.cpp:172 #1 0x556fc889965f in ExpressionParser::Lexer::~Lexer() (.../main.x+0x165f) ... previously allocated by thread T0 here: #0 0x7f5258f6e1c7 in operator new(unsigned long) .../libsanitizer/asan/asan_new_delete.cpp:99 #1 0x556fc8899588 in ExpressionParser::Lexer::tokenize() (.../main.x+0x1588) ... SUMMARY: AddressSanitizer: heap-use-after-free (/home/john/own/C/mp-gmp/const-problem/main-2.x+0x153c) in ExpressionParser::Expression::~Expression() ...
正常输出(去掉const修饰时)
通过编译选项-D CONST=移除const修饰后,程序运行正常,输出如下:
0x602000000010 = new Lexer 0x602000000030 = new Token 0x7ffff44937e0 = new Expression 0x7ffff44937e0 = start delete Expression 0x602000000010 = start delete Lexer 0x602000000030 = delete Token 0x602000000010 = done delete Lexer 0x7ffff44937e0 = end delete Expression
额外现象
添加virtual ~Lexer();声明也能解决问题,但Lexer类原本没有任何虚方法,这一现象令人困惑。
源代码
#include <cstdio> #ifndef CONST #define CONST const #endif class ExpressionParser { public: class Token; class Lexer; class Expression { friend ExpressionParser; Expression (Token *token) : expression_(token) { printf ("%p = new Expression\n", (void*) this); } Expression (const Expression&) = delete; Expression (Expression&&) = delete; void operator= (const Expression&) = delete; void operator= (Expression&&) = delete; ~Expression(); Token *expression_; }; static void eval(); }; using EP = ExpressionParser; class EP::Lexer { public: Token *tokens_ = nullptr; Lexer() { printf ("%p = new Lexer\n", (void*) this); } Lexer (const Lexer&) = delete; Lexer (Lexer&&) = delete; void operator= (const Lexer&) = delete; void operator= (Lexer&&) = delete; ~Lexer(); void tokenize(); }; class EP::Token { friend ExpressionParser; Lexer * CONST lexer_; Token (Lexer *lexer) : lexer_(lexer) { printf ("%p = new Token\n", (void*) this); } Token (const Token&) = delete; Token (Token&&) = delete; void operator= (const Token&) = delete; void operator= (Token&&) = delete; ~Token() { printf ("%p = delete Token\n", (void*) this); } }; void EP::eval() { Lexer *lexer = new Lexer(); lexer->tokenize(); (void) Expression (lexer->tokens_); } EP::Expression::~Expression() { printf ("%p = start delete Expression\n", (void*) this); delete expression_->lexer_; printf ("%p = end delete Expression\n", (void*) this); } void EP::Lexer::tokenize() { tokens_= new Token (this); } EP::Lexer::~Lexer() { printf ("%p = start delete Lexer\n", (void*) this); delete tokens_; printf ("%p = done delete Lexer\n", (void*) this); } int main (void) { ExpressionParser::eval(); }
内容的提问来源于stack exchange,提问作者emacs drives me nuts
相关产品推荐
相关产品推荐

