必须检查 sqlite3_open 返回值,错误时返回非 SQLITE_OK 码而非空指针;路径需绝对或可控;SQL 执行应优先用 prepare/step 防注入;读取字段前须校验类型;多线程需确认 sqlite3_threadsafe() 返回 1 并避免共享连接。

用 sqlite3_open 打开数据库文件前必须检查返回值
很多人写完 sqlite3_open("data.db", &db) 就直接开始执行 SQL,结果程序在后续调用 sqlite3_exec 时崩溃或返回 SQLITE_MISUSE —— 其实根本没连上。SQLite 的 C 接口不抛异常,所有错误都靠返回值判断。sqlite3_open 成功返回 SQLITE_OK,否则是错误码(比如 SQLITE_CANTOPEN 表示路径无效或权限不足)。
实操建议:
- 永远用
if (rc != SQLITE_OK)判断sqlite3_open结果,别只看指针是否为nullptr - 路径尽量用绝对路径或确保工作目录可控;相对路径如
"./db/app.db"在 IDE 调试时容易因启动目录不同而失败 - Windows 下注意反斜杠转义:
"C:\data\app.db"或用正斜杠"C:/data/app.db"更安全 - 如果文件不存在,
sqlite3_open默认会创建它;但父目录不存在就会失败,得提前用_mkdir(Windows)或mkdir(POSIX)建好
执行 SQL 用 sqlite3_exec 还是 sqlite3_prepare_v2?
sqlite3_exec 看似简单,但只适合一次性、无参数、不关心结果结构的语句(比如建表、插入固定数据)。只要涉及用户输入、查询结果遍历、或需要复用语句,就必须用 sqlite3_prepare_v2 + sqlite3_step 流程,否则极易被 SQL 注入或内存泄漏。
常见错误现象:
立即学习“C++免费学习笔记(深入)”;
- 用
sqlite3_exec拼接字符串处理用户名:"INSERT INTO user VALUES ('" + name + "')→ 直接崩坏 - 查出多行结果却只调一次
sqlite3_exec回调,漏掉后续数据 - 忘记调
sqlite3_finalize,prepare 后的语句对象持续占用内存
性能影响:对同一语句反复 sqlite3_prepare_v2 开销不小;应缓存 sqlite3_stmt* 指针,在连接生命周期内复用。
读取查询结果时,sqlite3_column_type 和 sqlite3_column_text 的调用顺序不能错
直接调 sqlite3_column_text(stmt, 0) 而不先确认字段类型,遇到 NULL 或非 TEXT 字段(比如 INTEGER、BLOB)会返回 nullptr,但很多人没判空就直接传给 std::string 构造函数,触发未定义行为。
正确做法:
- 先用
sqlite3_column_type(stmt, col_idx)检查类型,再选对应取值函数:sqlite3_column_int、sqlite3_column_double、sqlite3_column_text等 -
sqlite3_column_text返回的是 UTF-8 编码的const char*,指向 SQLite 内部缓冲区,**不能长期持有或 free**;需立即拷贝(如std::string(reinterpret_cast<const char>(ptr))</const>) - BLOB 字段要用
sqlite3_column_blob+sqlite3_column_bytes配合读取,别当成字符串硬解
多线程环境下,sqlite3_threadsafe() 返回值和编译选项必须匹配
默认编译的 SQLite 是单线程模式(SQLITE_THREADSAFE=0),此时在多个线程里共用一个 sqlite3* 连接会随机 crash。即使你用了互斥锁保护调用,底层仍可能因内部静态变量冲突出问题。
验证和解决方式:
- 运行时调
sqlite3_threadsafe(),返回 0 表示不支持多线程,不是“暂时没开”,而是库本身编译时禁用了 - Linux/macOS 下链接系统 SQLite(如
-lsqlite3)通常启用了线程安全;Windows 下若用预编译 DLL,得确认它是否带-DSQLITE_THREADSAFE=1 - 最稳妥的做法:每个线程单独
sqlite3_open,用 WAL 模式(PRAGMA journal_mode=WAL)提升并发读性能 - 别依赖 “我加了锁就没事”——SQLite 的某些内部状态(比如 busy handler)在线程间共享,锁不住
真正麻烦的从来不是怎么连上,而是连上之后没意识到 SQLite 的 C API 对错误容忍度极低,每一步返回值都是开关,漏一个就卡在奇怪的地方。尤其跨平台时,路径、编码、线程模型这三块最容易静默失败。










