sqlite3_open()成功返回SQLITE_OK才表示数据库连接真正建立,文件不存在时自动创建但目录须已存在;返回SQLITE_CANTOPEN多因路径含中文或目录缺失,SQLITE_BUSY表明被其他进程/线程独占;需检查返回值而非仅指针是否非空。

直接用 sqlite3 C API 是最轻量、最可控的方式,C++ 本身没有标准数据库库,别折腾封装过度的 ORM(比如 SQLiteCpp 在小项目里反而增加编译依赖和隐式行为)。
怎么连上数据库并确认是否成功
调用 sqlite3_open(),它返回 SQLITE_OK 才算真正打开成功。注意:路径是 UTF-8 字节流,Windows 下如果传宽字符路径会崩;文件不存在时它会自动创建,但目录必须已存在。
常见错误现象:sqlite3_open() 返回 SQLITE_CANTOPEN —— 八成是路径里有中文或上级目录不存在;返回 SQLITE_BUSY 则是别的进程(或本进程其他线程)正独占写入。
- 用绝对路径或确保工作目录明确,避免相对路径歧义
- 检查返回值,别只看指针是否非空:
int rc = sqlite3_open("data.db", &db); if (rc != SQLITE_OK) { /* 处理错误 */ } - 打开后立即执行
PRAGMA journal_mode = WAL;可提升并发读性能(尤其多线程只读场景)
怎么安全地执行带参数的 INSERT/UPDATE
绝不要拼接 SQL 字符串。用 sqlite3_prepare_v2() + sqlite3_bind_* 系列函数,这是防 SQL 注入和处理二进制数据(如图片 blob)的唯一可靠方式。
立即学习“C++免费学习笔记(深入)”;
参数差异:sqlite3_bind_text() 第四个参数若传 -1 表示自动计算长度(要求 C 风格 null 结尾);传具体长度则可绑定含 \0 的二进制数据;sqlite3_bind_blob() 同理。
- 预编译语句需手动
sqlite3_finalize(),否则内存泄漏 - 绑定前确保
sqlite3_step()已完成上一轮(比如没调sqlite3_reset()就重复 bind 会失败) - 示例:
sqlite3_prepare_v2(db, "INSERT INTO users(name,age) VALUES(?,?)", -1, &stmt, nullptr); sqlite3_bind_text(stmt, 1, "Alice", -1, SQLITE_STATIC); sqlite3_bind_int(stmt, 2, 30); sqlite3_step(stmt); sqlite3_finalize(stmt);
怎么读取查询结果并避免崩溃
sqlite3_step() 每次返回一行,SQLITE_ROW 表示有数据,SQLITE_DONE 表示结束。别假设列数固定——用 sqlite3_column_count() 动态获取,再用 sqlite3_column_type() 判断类型,再选对应取值函数(sqlite3_column_text()、sqlite3_column_int() 等)。
容易踩的坑:直接对 sqlite3_column_text() 返回指针做 std::string 构造没问题,但该指针生命周期只到下一次 sqlite3_step() 或 sqlite3_finalize();若需长期持有,必须 strdup() 或拷贝内容。
- 别在循环里反复调
sqlite3_prepare_v2(),应预编译一次复用 - NULL 值检测用
sqlite3_column_type(stmt, i) == SQLITE_NULL,不是靠返回指针是否为空 - 读完所有行后记得
sqlite3_finalize(stmt),否则 stmt 资源不释放
最常被忽略的是事务控制:默认每个语句都是独立事务,频繁单条 INSERT 极慢。批量操作务必包在 BEGIN IMMEDIATE; 和 COMMIT; 之间,否则 WAL 模式也救不了性能。还有,多线程访问同一个 sqlite3* 指针必须加锁,SQLite 的线程模式(SQLITE_THREADSAFE)编译选项和运行时设置不匹配会导致随机 crash。


















