用通义千问生成了一个测试代码,发现存入的文本为乱码,记录下问题与解决方式。
1.问题背景:
以下为问题代码片段,主要看 sqlite3_bind_text 调用文本传参的这一句即可:
cpp
//saveTimer 的实现
bool DatabaseManager::saveTimer(const Timer& timer) {
const char* insertSQL = R"(
INSERT INTO timers (name, description, expire_time, status)
VALUES (?, ?, ?, ?);
)";
sqlite3_stmt* stmt;
int rc = sqlite3_prepare_v2(db_, insertSQL, -1, &stmt, NULL);
if (rc != SQLITE_OK) {
std::cerr << "Failed to prepare statement: " << sqlite3_errmsg(db_) << std::endl;
return false;
}
/* 问题代码部分,SQLITE_STATIC ,timer.getName() 返回的是一个临时变量,语句执行完成就销毁了,就会出问题 */
sqlite3_bind_text(stmt, 1, timer.getName().c_str(), -1, SQLITE_STATIC);
sqlite3_bind_text(stmt, 2, timer.getDescription().c_str(), -1, SQLITE_STATIC);
std::string timeStr = timePointToString(timer.getExpireTime());
sqlite3_bind_text(stmt, 3, timeStr.c_str(), -1, SQLITE_STATIC);
sqlite3_bind_int(stmt, 4, static_cast<int>(timer.getStatus()));
rc = sqlite3_step(stmt);
sqlite3_finalize(stmt);
return rc == SQLITE_DONE;
}
2.问题原因与解决
timer.getName()等方法返回的都是临时变量,语句执行完成就销毁了。sqlite3_bind_text传入的指针
变成了野指针,这会导致数据库文件中的名称和描述为以乱码的形式存在。
需要修改将 SQLITE_STATIC 改为 SQLITE_TRANSIENT。
这两个参数的区别是:
- SQLITE_STATIC:告诉 SQLite 字符串指针在 SQLite 使用期间不会改变,SQLite 不会复制数据
- SQLITE_TRANSIENT:告诉 SQLite 需要复制数据,因为原始数据可能在 SQLite 使用完之前被释放或修改。
cpp
bool DatabaseManager::saveTimer(const Timer& timer) {
const char* insertSQL = R"(
INSERT INTO timers (name, description, expire_time, status)
VALUES (?, ?, ?, ?);
)";
sqlite3_stmt* stmt;
int rc = sqlite3_prepare_v2(db_, insertSQL, -1, &stmt, NULL);
if (rc != SQLITE_OK) {
std::cerr << "Failed to prepare statement: " << sqlite3_errmsg(db_) << std::endl;
return false;
}
/*
修改传入的参数为 SQLITE_TRANSIENT
*/
sqlite3_bind_text(stmt, 1, timer.getName().c_str(), -1, SQLITE_TRANSIENT);
sqlite3_bind_text(stmt, 2, timer.getDescription().c_str(), -1, SQLITE_TRANSIENT);
std::string timeStr = timePointToString(timer.getExpireTime());
sqlite3_bind_text(stmt, 3, timeStr.c_str(), -1, SQLITE_TRANSIENT);
sqlite3_bind_int(stmt, 4, static_cast<int>(timer.getStatus()));
rc = sqlite3_step(stmt);
sqlite3_finalize(stmt);
return rc == SQLITE_DONE;
}