🎬 个人主页:艾莉丝努力练剑
❄专栏传送门:《C语言》《数据结构与算法》《C/C++干货分享&学习过程记录》 《Linux操作系统编程详解》《笔试/面试常见算法:从基础到进阶》《Python干货分享》
⭐️为天地立心,为生民立命,为往圣继绝学,为万世开太平
🎬 艾莉丝的简介:

文章目录
- 1 ~> SQLite C API 编程实战:学生表 CRUD 案例
-
- 1.1 SQLite 基础背景
- 1.2 整体业务案例需求
- 1.3 C++ 源码:可编译
- 1.4 SQLite C API 标准执行流程
- 1.5 关键 API 详细释义
-
- 1.5.1 数据库打开关闭
- 1.5.2 prepare 预处理
- 1.5.3 bind 参数绑定系列
- 1.5.4 step 执行
- 1.5.5 column 读取结果集
- 1.5.6 错误信息获取
- 1.5.7 sqlite3_exec 简易执行接口
- 1.6 Linux 编译运行要点
-
- 1.6.1 编译命令
- 1.6.2 运行
- 1.7 RAII 封装要点与坑点
- 1.8 SQLite 补充面试拓展知识点
- 1.9 简述使用 SQLite C API 执行带参数 SELECT 查询
- 结尾

1 ~> SQLite C API 编程实战:学生表 CRUD 案例
本案例使用 C++ 封装 SQLite3 C 原生 API,完成数据库创建、建表、增删改查完整流程,梳理 API 标准使用范式。
1.1 SQLite 基础背景
- SQLite 是C 语言实现的文件型嵌入式数据库,无独立服务进程,数据库就是磁盘上一个.db文件。
- 核心对象
- sqlite3*:数据库连接句柄,代表打开的数据库实例。
- sqlite3_stmt*:预处理语句句柄,保存预编译后的 SQL,用于参数绑定、执行、读取结果集,必须调用**sqlite3_finalize**释放,否则内存泄漏。
- 核心返回码
- SQLITE_OK:操作成功
- SQLITE_DONE:语句执行完成(无返回行,insert/update/delete)
- SQLITE_ROW:查询到一行结果,可读取列数据
1.2 整体业务案例需求
创建 / 打开 SQLite 数据库文件
创建学生表Student
| stuid | INTEGER | 主键,自增 AUTOINCREMENT |
| name | TEXT | 学生姓名 |
| gender | TEXT | 学生性别 |
| age | INTEGER | 学生年龄 |
| gap | REAL | 学生学分 |
CRUD 操作:插入学生、按姓名单条查询、查询全部学生、按姓名更新、按姓名删除
RAII 封装:C++ 类构造打开数据库,析构自动关闭数据库连接
1.3 C++ 源码:可编译
#include <sqlite3.h>
#include <string>
#include <iostream>
#include <cassert>
// 学生信息结构体
struct StudentInfo
{
std::string name;
std::string gender;
int age;
double gap;
};
// SQLite学生数据库封装类 RAII
class StudentDB
{
public:
// 构造函数:打开数据库 + 初始化数据表
explicit StudentDB(const std::string& dbName)
{
int rc = sqlite3_open(dbName.c_str(), &_db);
if (rc != SQLITE_OK)
{
std::cerr << "打开数据库失败: " << sqlite3_errmsg(_db) << std::endl;
sqlite3_close(_db);
_db = nullptr;
return;
}
// 创建学生表
if (!initDataBase())
{
std::cerr << "初始化数据表失败" << std::endl;
sqlite3_close(_db);
_db = nullptr;
}
}
// 析构函数:自动关闭数据库连接
~StudentDB()
{
if (_db != nullptr)
{
sqlite3_close(_db);
_db = nullptr;
}
}
// 禁止拷贝,数据库句柄不可复制
StudentDB(const StudentDB&) = delete;
StudentDB& operator=(const StudentDB&) = delete;
// 插入学生信息
bool insertStudentInfo(const StudentInfo& studentInfo)
{
if (_db == nullptr) return false;
const std::string insertSQL = R"(
INSERT INTO Student(name, gender, age, gap)
VALUES(?,?,?,?);
)";
sqlite3_stmt* stmt = nullptr;
int rc = sqlite3_prepare_v2(_db, insertSQL.c_str(), –1, &stmt, nullptr);
if (rc != SQLITE_OK)
{
std::cerr << "prepare失败:" << sqlite3_errmsg(_db) << std::endl;
return false;
}
// 参数绑定:占位符下标从1开始
sqlite3_bind_text(stmt, 1, studentInfo.name.c_str(), –1, SQLITE_TRANSIENT);
sqlite3_bind_text(stmt, 2, studentInfo.gender.c_str(), –1, SQLITE_TRANSIENT);
sqlite3_bind_int(stmt, 3, studentInfo.age);
sqlite3_bind_double(stmt, 4, studentInfo.gap);
rc = sqlite3_step(stmt);
if (rc != SQLITE_DONE)
{
std::cerr << "执行insert失败:" << sqlite3_errmsg(_db) << std::endl;
sqlite3_finalize(stmt);
return false;
}
sqlite3_finalize(stmt);
return true;
}
// 根据姓名查询单个学生
bool queryStudentInfo(const std::string& name)
{
if (_db == nullptr) return false;
const std::string querySQL = R"(
SELECT stuid, name, gender, age, gap FROM Student WHERE name=?;
)";
sqlite3_stmt* stmt = nullptr;
int rc = sqlite3_prepare_v2(_db, querySQL.c_str(), –1, &stmt, nullptr);
if (rc != SQLITE_OK)
{
std::cerr << "prepare失败:" << sqlite3_errmsg(_db) << std::endl;
return false;
}
sqlite3_bind_text(stmt, 1, name.c_str(), –1, SQLITE_TRANSIENT);
rc = sqlite3_step(stmt);
if (rc != SQLITE_ROW)
{
std::cerr << "未查询到该学生或者执行错误:" << sqlite3_errmsg(_db) << std::endl;
sqlite3_finalize(stmt);
return false;
}
// 读取结果集列下标从0开始
int stuid = sqlite3_column_int(stmt, 0);
std::string qName(reinterpret_cast<const char*>(sqlite3_column_text(stmt, 1)));
std::string qGender(reinterpret_cast<const char*>(sqlite3_column_text(stmt, 2)));
int qAge = sqlite3_column_int(stmt, 3);
double qGap = sqlite3_column_double(stmt, 4);
std::cout << "==== 查询单个学生 ====" << std::endl;
std::cout << "stuid:" << stuid << std::endl;
std::cout << "name:" << qName << std::endl;
std::cout << "gender:" << qGender << std::endl;
std::cout << "age:" << qAge << std::endl;
std::cout << "gap:" << qGap << std::endl;
sqlite3_finalize(stmt);
return true;
}
// 查询全部学生
bool queryAllStudentInfo()
{
if (_db == nullptr) return false;
const std::string queryAllSQL = R"(
SELECT stuid, name, gender, age, gap FROM Student;
)";
sqlite3_stmt* stmt = nullptr;
int rc = sqlite3_prepare_v2(_db, queryAllSQL.c_str(), –1, &stmt, nullptr);
if (rc != SQLITE_OK)
{
std::cerr << "prepare失败:" << sqlite3_errmsg(_db) << std::endl;
return false;
}
std::cout << "\\n==== 全部学生列表 ====" << std::endl;
rc = sqlite3_step(stmt);
while (rc == SQLITE_ROW)
{
int stuid = sqlite3_column_int(stmt, 0);
std::string qName(reinterpret_cast<const char*>(sqlite3_column_text(stmt, 1)));
std::string qGender(reinterpret_cast<const char*>(sqlite3_column_text(stmt, 2)));
int qAge = sqlite3_column_int(stmt, 3);
double qGap = sqlite3_column_double(stmt, 4);
std::cout << "stuid:" << stuid
<< " | name:" << qName
<< " | gender:" << qGender
<< " | age:" << qAge
<< " | gap:" << qGap
<< std::endl;
rc = sqlite3_step(stmt);
}
if (rc != SQLITE_DONE)
{
std::cerr << "遍历结果集异常:" << sqlite3_errmsg(_db) << std::endl;
sqlite3_finalize(stmt);
return false;
}
sqlite3_finalize(stmt);
return true;
}
// 更新学生:根据姓名更新其他字段
bool updateStudentInfo(const std::string& name, const StudentInfo& newInfo)
{
if (_db == nullptr) return false;
const std::string updateSQL = R"(
UPDATE Student SET gender=?, age=?, gap=? WHERE name=?;
)";
sqlite3_stmt* stmt = nullptr;
int rc = sqlite3_prepare_v2(_db, updateSQL.c_str(), –1, &stmt, nullptr);
if (rc != SQLITE_OK)
{
std::cerr << "prepare失败:" << sqlite3_errmsg(_db) << std::endl;
return false;
}
// 绑定顺序和SQL占位符一一对应
sqlite3_bind_text(stmt,1, newInfo.gender.c_str(),–1,SQLITE_TRANSIENT);
sqlite3_bind_int(stmt,2, newInfo.age);
sqlite3_bind_double(stmt,3, newInfo.gap);
sqlite3_bind_text(stmt,4, name.c_str(),–1,SQLITE_TRANSIENT);
rc = sqlite3_step(stmt);
if (rc != SQLITE_DONE)
{
std::cerr << "update执行失败:" << sqlite3_errmsg(_db) << std::endl;
sqlite3_finalize(stmt);
return false;
}
sqlite3_finalize(stmt);
return true;
}
// 根据姓名删除学生
bool deleteStudentInfo(const std::string& name)
{
if (_db == nullptr) return false;
const std::string deleteSQL = R"(
DELETE FROM Student WHERE name=?;
)";
sqlite3_stmt* stmt = nullptr;
int rc = sqlite3_prepare_v2(_db, deleteSQL.c_str(), –1, &stmt, nullptr);
if (rc != SQLITE_OK)
{
std::cerr << "prepare失败:" << sqlite3_errmsg(_db) << std::endl;
return false;
}
sqlite3_bind_text(stmt, 1, name.c_str(), –1, SQLITE_TRANSIENT);
rc = sqlite3_step(stmt);
if (rc != SQLITE_DONE)
{
std::cerr << "delete执行失败:" << sqlite3_errmsg(_db) << std::endl;
sqlite3_finalize(stmt);
return false;
}
sqlite3_finalize(stmt);
return true;
}
private:
sqlite3* _db = nullptr;
// 私有:初始化数据表,创建Student表
bool initDataBase()
{
const std::string createTableSQL = R"(
CREATE TABLE IF NOT EXISTS Student(
stuid INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT,
gender TEXT,
age INTEGER,
gap REAL
);
)";
char* errMsg = nullptr;
int rc = sqlite3_exec(_db, createTableSQL.c_str(), nullptr, nullptr, &errMsg);
if (rc != SQLITE_OK)
{
std::cerr << "建表失败:" << errMsg << std::endl;
sqlite3_free(errMsg);
return false;
}
return true;
}
};
int main()
{
StudentInfo info1{"张三", "男", 18, 3.5};
StudentInfo info2{"李四", "女", 19, 3.8};
StudentInfo info3{"王五", "男", 20, 4.0};
StudentInfo info4{"赵六", "女", 21, 4.2};
// RAII打开数据库,出作用域自动关闭
StudentDB db("studentDB.db");
db.insertStudentInfo(info1);
db.insertStudentInfo(info2);
db.insertStudentInfo(info3);
db.insertStudentInfo(info4);
db.queryAllStudentInfo();
// 修改王五学分
info3.gap = 4.5;
db.updateStudentInfo("王五", info3);
db.queryStudentInfo("王五");
// 删除赵六
db.deleteStudentInfo("赵六");
db.queryAllStudentInfo();
return 0;
}
1.4 SQLite C API 标准执行流程
所有带参数 SQL 统一遵循该 6 步流程,禁止字符串拼接 SQL,防止 SQL 注入
1.5 关键 API 详细释义
1.5.1 数据库打开关闭
- int sqlite3_open(const char *filename, sqlite3 **ppDb)
- filename:数据库文件名;文件名不存在会自动创建磁盘文件。
- ppDb:输出数据库句柄指针。
- 返回值:SQLITE_OK成功;失败依然会返回非空句柄,必须调用sqlite3_close释放。
- int sqlite3_close(sqlite3* db):关闭数据库连接。
1.5.2 prepare 预处理
- int sqlite3_prepare_v2(sqlite3 *db, const char *zSql, int nByte, sqlite3_stmt **ppStmt, const char **pzTail)
- nByte=-1自动计算 SQL 字符串长度;
- ppStmt输出预处理语句指针;
- pzTail=nullptr,忽略多余 SQL 文本。
1.5.3 bind 参数绑定系列
sqlite3_bind_int(stmt, idx, intVal);
sqlite3_bind_double(stmt, idx, doubleVal);
sqlite3_bind_text(stmt, idx, text, len, destructor);
- destructor 参数:
- SQLITE_TRANSIENT:SQLite 内部拷贝字符串;
- SQLITE_STATIC:字符串生命周期由调用方保证,SQLite 不拷贝。
1.5.4 step 执行
- int sqlite3_step(sqlite3_stmt* stmt)
- SQLITE_DONE:语句执行完毕,无更多结果;
- SQLITE_ROW:获取到一行查询结果,可以读取 column。
1.5.5 column 读取结果集
int sqlite3_column_int(sqlite3_stmt*, int iCol);
double sqlite3_column_double(sqlite3_stmt*, int iCol);
const unsigned char* sqlite3_column_text(sqlite3_stmt*, int iCol);
- iCol:列下标从 0 开始,对应 SELECT 查询字段顺序。
1.5.6 错误信息获取
- const char* sqlite3_errmsg(sqlite3* db):获取最近一次错误文本。
- void sqlite3_free(void* ptr):释放sqlite3_exec返回的 errMsg 内存。
1.5.7 sqlite3_exec 简易执行接口
- 适合无参数 DDL 语句(建表),不适合带用户输入 DML,无参数绑定,拼接字符串会产生注入漏洞。
1.6 Linux 编译运行要点
1.6.1 编译命令
# 需要链接sqlite3库
g++ testSqlite3.cpp -o testSqlite3 -lsqlite3
- 报错fatal error: sqlite3.h: No such file or directory:需要安装开发库
sudo apt install libsqlite3-dev
- 报错找不到文件:必须 cd 到源码所在目录执行编译命令。
1.6.2 运行
./testSqlite3
运行成功后当前目录生成studentDB.db数据库文件,可以使用 sqlite3 命令行工具打开查看:
sqlite3 studentDB.db
1.7 RAII 封装要点与坑点
1.8 SQLite 补充面试拓展知识点
- SQLite 默认线程模式:默认单线程模式,多线程使用必须配置打开线程安全编译选项。
- 事务:默认每条 DML 语句自动开启事务,批量插入大量数据手动开启事务可以极大提升性能。
- 数据类型:SQLite 是动态类型数据库,列声明类型只是建议,实际存储可以存放任意类型值。
- 自增主键:AUTOINCREMENT只用于 INTEGER 主键,不写 AUTOINCREMENT 也会自动递增,但是会复用删除过的 ID;写 AUTOINCREMENT 保证 ID 单调永不复用。
1.9 简述使用 SQLite C API 执行带参数 SELECT 查询
**注意:**增删改操作和查询流程高度相似,区别在于 step 期待返回码为SQLITE_DONE,不需要读取 column 列数据。所有 SQLite C API 程序都遵循 prepare -> bind -> step -> finalize 范式。
结尾
uu们,本文的内容到这里就全部结束了,艾莉丝在这里再次感谢您的阅读!
|
艾莉丝努力练剑
C/C++ & Linux 底层探索者 | 一个正在努力练剑的技术博主 👀 ❤️ ⭐ 💬 不要忘记给博主“一键四连”哦! “今日练剑达成!” “技术之路难免有困惑,但同行的人会让前进更有方向。” |
结语:希望对学习Linux相关内容的uu有所帮助,不要忘记给博主“一键四连”哦!
往期回顾:
【AI大模型接入SDK】SQLite基础概念与C的API开发
🗡博主在这里放了一只小狗,大家看完了摸摸小狗放松一下吧!🗡
૮₍ ˶ ˊ ᴥ ˋ˶₎ა



