使用std::format_args触发AddressSanitizer栈越界访问错误求助
问题重现
代码示例:
#include <format> #include <iostream> #include <string> int main() { int i = 42; std::string s = "hello"; std::format_args args = std::make_format_args(i, s); std::cout << std::vformat("Number: {}, Word: {}", args) << '\n'; return 0; }
编译命令:clang++-17 --std=c++23 -stdlib=libc++ -fsanitize=address fmt.cpp
运行后触发AddressSanitizer的stack-use-after-scope错误,但将std::make_format_args直接传入std::vformat则运行正常,且变量i和s仍在作用域内,无法理解错误原因。
错误原因
核心问题在于std::format_args是仅持有引用的轻量级视图类型,而std::make_format_args的实现中,会为传入的参数创建一系列临时包装对象(存储在栈上),std::format_args保存的是对这些临时对象的引用,而非直接引用原变量i和s。
当std::make_format_args调用结束后,其内部创建的临时包装对象会立即被销毁,此时args持有的引用就指向了已释放的栈内存。后续调用std::vformat时,通过这些引用访问已销毁的对象,就会触发stack-use-after-scope错误。
如果直接将std::make_format_args(i, s)传入std::vformat,根据C++临时对象生命周期规则,这些临时包装对象的生命周期会被延长至整个std::vformat函数调用期间,因此不会出现内存访问问题。
正确写法
直接传入参数(推荐)
如果不需要单独存储std::format_args,直接在std::vformat调用中传入std::make_format_args的结果即可:
#include <format> #include <iostream> #include <string> int main() { int i = 42; std::string s = "hello"; std::cout << std::vformat("Number: {}, Word: {}", std::make_format_args(i, s)) << '\n'; return 0; }
存储参数集合(需复用场景)
如果确实需要存储参数集合以便后续复用,应使用std::format_args_store,它会保存参数的副本或必要的包装对象,避免引用悬空:
#include <format> #include <iostream> #include <string> int main() { int i = 42; std::string s = "hello"; auto args_store = std::make_format_args_store<std::format_context>(i, s); std::format_args args = args_store.get(); std::cout << std::vformat("Number: {}, Word: {}", args) << '\n'; return 0; }
内容的提问来源于stack exchange,提问作者Jonathan Geisler

