23、C语言与MySQL基础:MySQL C API简介、mysql_real_connect、mysql_query、mysql_store_result
说实话,很多嵌入式工程师一听到“数据库”三个字就头大。觉得那是后端开发的事,跟底层C语言不沾边。我以前也这么想,直到有一次做设备数据采集项目,客户要求把传感器数据存到远程MySQL里做分析。嗯,那会儿我才发现——C语言操作MySQL,其实没那么玄乎。
今天我们就来聊聊MySQL C API。说白了,就是MySQL官方提供的一套C语言函数库。你写C代码,调这些函数,就能连上MySQL服务器,发SQL语句,拿结果。就这么简单。
MySQL C API 是什么?
MySQL C API 是一组C语言函数,封装在 libmysqlclient 这个库里。你编译程序时链接它,就能在C代码里操作MySQL数据库。
我个人习惯把它的工作流程总结成四步:
- 初始化:分配一个连接句柄
- 连接:用
mysql_real_connect连上数据库 - 查询:用
mysql_query发送SQL语句 - 取结果:用
mysql_store_result拿到查询结果
你看,是不是很清晰?
核心要点:MySQL C API 本质上就是一组函数调用。你不需要懂数据库内核,只要会调函数、会处理返回值就行。
下面这张图,是我画的工作流程。你一看就明白。
mysql_init:第一步,拿个句柄
所有操作之前,你得先调用 mysql_init()。它返回一个 MYSQL * 指针。这个指针就是你的“数据库遥控器”。
MYSQL *conn = mysql_init(NULL);
if (conn == NULL) {
fprintf(stderr, "mysql_init 失败\n");
return -1;
}
这里有个坑。我曾经在项目里忘了检查返回值,结果后面 mysql_real_connect 直接段错误。排查了半天才发现是初始化就没成功。所以,每次调用API后,一定要检查返回值。
mysql_real_connect:连上服务器
拿到句柄后,就该连服务器了。函数原型长这样:
MYSQL *mysql_real_connect(
MYSQL *conn, // 初始化后的句柄
const char *host, // 主机地址,如 "127.0.0.1"
const char *user, // 用户名
const char *passwd, // 密码
const char *db, // 数据库名
unsigned int port, // 端口,默认3306
const char *unix_socket, // 套接字,一般传NULL
unsigned long clientflag // 标志位,一般传0
);
举个例子:
MYSQL *conn = mysql_init(NULL);
if (conn == NULL) {
fprintf(stderr, "初始化失败\n");
return -1;
}
conn = mysql_real_connect(conn, "127.0.0.1", "root", "mypass",
"testdb", 3306, NULL, 0);
if (conn == NULL) {
fprintf(stderr, "连接失败: %s\n", mysql_error(conn));
return -1;
}
printf("连接成功!\n");
小技巧:连接失败时,用 mysql_error(conn) 打印错误信息。它能告诉你具体是网络不通、密码错误还是数据库不存在。我在调试时全靠它定位问题。
mysql_query:发SQL语句
连接成功后,就可以用 mysql_query() 发送SQL了。它只接受一个参数——SQL字符串。
if (mysql_query(conn, "SELECT * FROM sensors")) {
fprintf(stderr, "查询失败: %s\n", mysql_error(conn));
return -1;
}
注意,mysql_query 不支持多条SQL语句一起发。你一次只能发一条。如果你需要批量操作,得循环调用。
警告:千万不要把用户输入直接拼接到SQL字符串里。比如 sprintf(query, "SELECT * FROM users WHERE name='%s'", user_input)。这会导致SQL注入攻击。我见过一个项目因此被删了整张表。正确的做法是用预处理语句(后面章节会讲)。
mysql_store_result:把结果拿回来
查询执行后,结果还在MySQL服务器上。你得用 mysql_store_result() 把它取到客户端内存里。
MYSQL_RES *result = mysql_store_result(conn);
if (result == NULL) {
fprintf(stderr, "取结果失败: %s\n", mysql_error(conn));
return -1;
}
取回来后,你可以用 mysql_num_fields() 获取列数,用 mysql_fetch_row() 一行一行地读数据。
int num_fields = mysql_num_fields(result);
MYSQL_ROW row;
while ((row = mysql_fetch_row(result))) {
for (int i = 0; i < num_fields; i++) {
printf("%s\t", row[i] ? row[i] : "NULL");
}
printf("\n");
}
读完别忘了释放结果集:
mysql_free_result(result);
完整示例:从连接到查询
下面是一个完整的例子。它连接数据库,查询一张叫 sensors 的表,打印所有数据。
#include <stdio.h>
#include <mysql/mysql.h>
int main() {
MYSQL *conn;
MYSQL_RES *result;
MYSQL_ROW row;
int num_fields;
// 1. 初始化
conn = mysql_init(NULL);
if (conn == NULL) {
fprintf(stderr, "初始化失败\n");
return -1;
}
// 2. 连接
conn = mysql_real_connect(conn, "127.0.0.1", "root",
"mypass", "testdb", 3306, NULL, 0);
if (conn == NULL) {
fprintf(stderr, "连接失败: %s\n", mysql_error(conn));
mysql_close(conn);
return -1;
}
// 3. 查询
if (mysql_query(conn, "SELECT id, name, value FROM sensors")) {
fprintf(stderr, "查询失败: %s\n", mysql_error(conn));
mysql_close(conn);
return -1;
}
// 4. 取结果
result = mysql_store_result(conn);
if (result == NULL) {
fprintf(stderr, "取结果失败: %s\n", mysql_error(conn));
mysql_close(conn);
return -1;
}
num_fields = mysql_num_fields(result);
while ((row = mysql_fetch_row(result))) {
for (int i = 0; i < num_fields; i++) {
printf("%s\t", row[i] ? row[i] : "NULL");
}
printf("\n");
}
// 5. 清理
mysql_free_result(result);
mysql_close(conn);
return 0;
}
常见错误与避坑
我总结了几条实战中容易踩的坑:
- 忘记链接库:编译时加
-lmysqlclient。我刚开始总是忘记,报一堆未定义引用错误。 - 忘记检查返回值:每个API调用都要检查。别偷懒。
- 忘记释放结果集:
mysql_store_result分配的内存,必须用mysql_free_result释放。否则内存泄漏。 - 连接超时:默认8小时无活动会自动断开。长连接项目要注意重连机制。
一句话总结:MySQL C API 就是 init → connect → query → store_result → free → close。记住这个流程,你就能在C语言里操作数据库了。
好了,今天就聊到这儿。下一节我们会深入 mysql_fetch_row 和 mysql_num_fields 的细节,以及如何处理更复杂的查询结果。你先把今天这几个函数练熟,后面就好办了。
公众号:蓝海资料掘金营,微信deep3321