C++自定义SQLite3类查询结果指针传递异常问题求助
问题根源及解决方案
核心问题:临时指针失效
你遇到的乱码是因为sqlite3_column_text()返回的指针指向SQLite内部的临时缓冲区,该缓冲区仅在当前sqlite3_step()调用期间、或sqlite3_stmt对象被sqlite3_finalize()销毁前有效。当db_query函数执行完毕回到main后,后续调用析构函数会执行sqlite3_finalize(stmt),此时缓冲区被释放,glb中保存的指针变为野指针,访问时自然出现乱码。
其他代码问题
- 手动调用析构函数错误:
db->~dbase();属于违规操作,用new创建的对象必须用delete销毁,手动调用析构函数会导致内存泄漏,且对象内存无法被正确释放。 - 内存释放方式错误:
delete glb;应改为delete[] glb;,因为glb是通过new[]分配的数组。 - 缺少SQLite操作错误检查:
sqlite3_open、sqlite3_prepare_v2等函数的返回值未被校验,无法排查操作失败的情况。 - 内存泄漏:
db_query中的pr数组未被释放,会造成内存泄漏。
修正后的代码
#include <sqlite3.h> #include <iostream> #include <cstring> using namespace std; class dbase { public: dbase(const char* dbfile); // 修改返回值,返回查询到的行数,方便main处理 int db_query(unsigned char*** pas_t, const char* sql); virtual ~dbase(); private: sqlite3* db = nullptr; sqlite3_stmt* stmt = nullptr; }; dbase::dbase(const char* dbfile) { int rc = sqlite3_open(dbfile, &db); if (rc != SQLITE_OK) { cerr << "无法打开数据库: " << sqlite3_errmsg(db) << endl; sqlite3_close(db); db = nullptr; } } int dbase::db_query(unsigned char*** pas_t, const char* sql){ if (!db) return -1; int rc = sqlite3_prepare_v2(db, sql, -1, &stmt, nullptr); if (rc != SQLITE_OK) { cerr << "SQL准备失败: " << sqlite3_errmsg(db) << endl; return -1; } int row_count = 0; // 先统计行数,方便分配内存 while (sqlite3_step(stmt) == SQLITE_ROW) { row_count++; } sqlite3_reset(stmt); // 重置stmt,重新遍历 if (row_count == 0) { *pas_t = nullptr; return 0; } // 分配存储字符串指针的数组 *pas_t = new unsigned char*[row_count + 1]; // 最后一个元素设为NULL作为结束标志 int i = 0; while ((sqlite3_step(stmt)) == SQLITE_ROW) { const unsigned char* col_text = sqlite3_column_text(stmt, 0); if (col_text) { // 复制字符串到自己分配的内存 (*pas_t)[i] = new unsigned char[strlen((const char*)col_text) + 1]; strcpy((char*)(*pas_t)[i], (const char*)col_text); } else { (*pas_t)[i] = nullptr; } cout << "pas_t = " << (*pas_t)[i] << " " << endl; i++; } (*pas_t)[row_count] = nullptr; // 设置结束标志 cout << "\nend\n" << endl; sqlite3_finalize(stmt); // 提前finalize,避免后续析构重复操作 stmt = nullptr; return row_count; } dbase::~dbase() { if (stmt) { sqlite3_finalize(stmt); } if (db) { sqlite3_close(db); } } int main() { dbase* db = new dbase("./Test.db"); unsigned char** glb = nullptr; int row_num = db->db_query(&glb, "SELECT Nome FROM Test"); if (glb) { int n = 0; while(glb[n] != NULL){ cout << "glb -- " << glb[n] << "--" << n << endl; // 释放每个字符串的内存 delete[] glb[n]; n++; } // 释放指针数组的内存 delete[] glb; } // 正确销毁db对象 delete db; return 0; }
关键修改说明
- 复制字符串而非保存临时指针:用
strcpy将sqlite3_column_text返回的内容复制到自主分配的堆内存中,确保字符串生命周期不受SQLite stmt的影响。 - 统计查询行数:先遍历一次stmt统计行数,再分配对应内存空间,避免固定数组大小的限制。
- 修正内存管理:用
delete[]释放数组和字符串,用delete销毁db对象,避免内存泄漏。 - 添加错误处理:检查每个SQLite操作的返回值并打印错误信息,便于调试。
- 重置stmt:统计行数后用
sqlite3_reset重置stmt,确保可以重新遍历查询结果。
内容的提问来源于stack exchange,提问作者Berico
相关产品推荐
相关产品推荐

