正确路径是使用sqlite的绑定机制(sqlite3_bind_*),而非字符串拼接,因其能防止sql注入、避免类型处理问题(如o'reilly破坏单引号、nullptr或浮点精度异常),并通过预编译+参数绑定实现数据与代码分离。

为什么不能直接用字符串拼接SQL
因为容易被SQL注入,而且类型处理麻烦。比如用户输入 "O'Reilly" 会破坏单引号平衡,nullptr 或浮点数精度也会出问题。SQLite 的绑定机制(sqlite3_bind_*)才是正确路径。
怎么用 sqlite3_prepare_v2 和绑定参数
核心是预编译 + 绑定,不是运行时替换字符串。占位符用 ?(位置参数)或 @name(命名参数),不能混用。
-
sqlite3_prepare_v2返回SQLITE_OK才能继续,否则检查sqlite3_errmsg(db) - 绑定前必须确保语句已成功编译,且
sqlite3_stmt*非空 - 位置从 1 开始:
sqlite3_bind_text(stmt, 1, "abc", -1, SQLITE_STATIC);命名参数用sqlite3_bind_text(stmt, sqlite3_bind_parameter_index(stmt, "@user"), ...) - 字符串推荐用
SQLITE_TRANSIENT(SQLite 自行拷贝),避免原始指针失效
常见错误:绑定后没重置就重复执行
同一个 sqlite3_stmt* 可复用,但每次执行前必须调用 sqlite3_reset(stmt),否则第二次 sqlite3_step() 会返回 SQLITE_MISUSE 或旧结果。
典型流程:
sqlite3_prepare_v2(db, "SELECT * FROM users WHERE id = ? AND active = ?", -1, &stmt, nullptr); sqlite3_bind_int(stmt, 1, 123); sqlite3_bind_int(stmt, 2, 1); sqlite3_step(stmt); // 第一次 sqlite3_reset(stmt); // 必须加这行 sqlite3_bind_int(stmt, 1, 456); sqlite3_bind_int(stmt, 2, 0); sqlite3_step(stmt); // 第二次
查询结果怎么安全取值
别在 sqlite3_step() 返回 SQLITE_ROW 之前读列,否则行为未定义。列索引从 0 开始,类型用 sqlite3_column_type() 判断再取值。
-
sqlite3_column_int(stmt, 0)读整数,sqlite3_column_text(stmt, 1)读文本(返回const unsigned char*,不是std::string) - 文本内容生命周期只在当前
sqlite3_step()调用期间有效,需立即拷贝 - 遇到 NULL 列,
sqlite3_column_type()返回SQLITE_NULL,此时调用_int或_text是未定义行为
绑定和取值的细节稍有不慎就会 crash 或读到垃圾内存,尤其是跨线程、长生命周期 stmt、或混合使用命名/位置参数时,得盯紧文档里每个函数的约束条件。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











