例如,为什么SQLite C接口在使用BOOST_SCOPE_EXIT_ALL
时需要通过引用传递指针?
假设我有以下代码:
#include <boost/scope_exit.hpp>
#include <sqlite3.h>
#include <cstdlib>
#include <iostream>
int main()
{
sqlite3* db;
BOOST_SCOPE_EXIT_ALL(db)
{
if (sqlite3_close(db) != SQLITE_OK)
{
std::cerr << sqlite3_errmsg(db) << 'n';
}
};
if (sqlite3_open(":memory:", &db) != SQLITE_OK)
{
std::cerr << sqlite3_errmsg(db) << 'n';
return EXIT_FAILURE;
}
sqlite3_stmt* prepared_stmt = NULL;
BOOST_SCOPE_EXIT_ALL(db, prepared_stmt)
{
if (sqlite3_finalize(prepared_stmt) != SQLITE_OK)
{
std::cerr << sqlite3_errmsg(db) << 'n';
}
};
if (sqlite3_prepare_v2(db, "CREATE TABLE IF NOT EXISTS foo(bar TEXT, baz TEXT)", -1, &prepared_stmt, 0) != SQLITE_OK)
{
std::cerr << sqlite3_errmsg(db) << 'n';
return EXIT_FAILURE;
}
if (sqlite3_step(prepared_stmt) != SQLITE_DONE)
{
std::cerr << sqlite3_errmsg(db) << 'n';
return EXIT_FAILURE;
}
}
它在尝试调用函数时崩溃sqlite3_close
并且不会向 stderr 打印任何内容。但是,如果我改变
BOOST_SCOPE_EXIT_ALL(db)
自
BOOST_SCOPE_EXIT_ALL(&db)
它指出
由于未完成的语句或未完成的备份而无法关闭
尝试调用sqlite3_close
函数时。
如果我也通过引用传递prepared_stmt
,它会按预期工作:
BOOST_SCOPE_EXIT_ALL(db, &prepared_stmt)
为什么?我认为SQLite库只抱怨db
和prepared_stmt
指向的地址。
提前谢谢。
main(( 中的第一行
sqlite3* db;
可能是 sqlite3_close(( 崩溃的原因。 编译器可能正在重用该声明指针的内存位置,并且不一定使用空位置或将其设为空。 因此,对 sqlite3_close(( 的调用正在接收虚假指针。
https://www.sqlite.org/c3ref/close.html 的文档指出:
sqlite3_close(C( 和 sqlite3_close_v2(C( 的 C 参数必须是 从以下位置获取的 NULL 指针或 sqlite3 对象指针 sqlite3_open((、sqlite3_open16(( 或 sqlite3_open_v2((,而不是 以前关闭。调用 sqlite3_close(( 或 sqlite3_close_v2(( NULL 指针参数是无害的无操作。
因此,根据上述语句,以下代码应删除崩溃:
sqlite3* db = nullptr;
当然,如果你只是完全删除了这个不必要的代码块,所有这些都是没有意义的:
BOOST_SCOPE_EXIT_ALL(db)
{
if (sqlite3_close(db) != SQLITE_OK)
{
std::cerr << sqlite3_errmsg(db) << 'n';
}
};
没有理由关闭从未打开的数据库。
编辑:这是我尝试的函数版本。 我现在真的没时间了,所以我无法测试它,但我认为它会起作用。 我删除了提升宏,因为我不熟悉它们,但您可以重新添加它们。 希望这有帮助!
int main()
{
sqlite3* db = nullptr;
if (sqlite3_open(":memory:", &db) != SQLITE_OK)
goto ERROR_OCCURRED;
sqlite3_stmt* prepared_stmt = nullptr;
if (sqlite3_finalize(prepared_stmt) != SQLITE_OK)
std::cerr << sqlite3_errmsg(db) << 'n';
if (sqlite3_prepare_v2(db, "CREATE TABLE IF NOT EXISTS foo(bar TEXT, baz TEXT)", -1, &prepared_stmt, 0) != SQLITE_OK)
goto ERROR_OCCURRED;
if (sqlite3_step(prepared_stmt) != SQLITE_DONE)
goto ERROR_OCCURRED;
if (sqlite3_close(db) != SQLITE_OK)
{
std::cerr << sqlite3_errmsg(db) << 'n';
sqlite3_close(db);
return EXIT_FAILURE;
}
return 0;
ERROR_OCCURRED:
std::cerr << sqlite3_errmsg(db) << 'n';
sqlite3_close(db);
return EXIT_FAILURE;
}
不幸的是,我没有时间检查您对 sqlite3_finalize(( 的调用,但我也认为该函数可能还为时过早。 我认为没有理由最终确定空指针。