在 C 语言开发中,若需将数据持久化到 MySQL(如存储用户信息、日志数据),需通过 MySQL 官方提供的 C 语言客户端库(Connector/C)实现交互。很多开发者初期会卡在 “库文件引入”“动态库链接” 等环境问题上,或因不熟悉接口导致内存泄漏。本文基于《MySQL 使用 C 语言链接.pdf》,从环境准备、库文件配置入手,详解核心接口(初始化、连接、查询、结果处理),帮你快速实现 C 语言与 MySQL 的高效交互。
一、环境准备:下载与配置 Connector/C 库
C 语言无法直接连接 MySQL,需依赖 MySQL 官方提供的Connector/C库(包含头文件和编译好的库文件),这是交互的基础。
1.1 下载 Connector/C 库
下载地址:MySQL 官网,选择与操作系统(Linux/Windows)、编译器匹配的版本(如 Linux 64 位、GCC 编译);
库文件结构(下载解压后):
Connector-C/
├── include/ # 头文件目录(含mysql.h等核心声明)
│ ├── mysql/ # 子目录(客户端认证、插件相关头文件)
│ ├── mysql.h # 核心头文件(声明所有交互接口)
│ └── my_global.h # 全局宏定义与工具函数声明
└── lib/ # 库文件目录(编译好的静态库/动态库)
├── libmysqlclient.a # 静态库(编译时直接打包到程序)
├── libmysqlclient.so # 动态库(运行时依赖,需指定查找路径)
└── libmysqlclient.so.18.3.0# 动态库本体(so文件软链接指向它)
1.2 验证库文件引入(关键测试)
先通过mysql_get_client_info()函数验证库是否成功引入,避免后续开发因库配置问题浪费时间。
步骤 1:编写测试代码(test.c)
#include <stdio.h>
#include "mysql.h" // 引入Connector/C核心头文件
int main() {
// 调用库接口,获取MySQL客户端版本
printf("MySQL Client Version: %s\n", mysql_get_client_info());
return 0;
}
步骤 2:编译与运行(Linux 环境)
编译时需指定 “头文件路径(-I)”“库文件路径(-L)” 和 “链接的库名(-lmysqlclient)”:
# 编译命令:-I指定include目录,-L指定lib目录,-l链接动态库
gcc -o test test.c -I./Connector-C/include -L./Connector-C/lib -lmysqlclient
# 运行程序(若报错“找不到动态库”,需指定动态库查找路径)
export LD_LIBRARY_PATH=./Connector-C/lib # 临时设置动态库路径(仅当前终端生效)
./test
成功标志
运行后输出 MySQL 客户端版本(如MySQL Client Version: 6.1.6),说明库文件引入成功;若报错 “找不到 libmysqlclient.so”,需重新检查LD_LIBRARY_PATH是否指向正确的lib目录。
二、核心接口详解:从连接到结果处理
C 语言与 MySQL 的交互流程固定:初始化→连接数据库→执行 SQL→处理结果→关闭连接,每个步骤对应专用接口,需严格按顺序调用。
2.1 1. 初始化 MySQL 句柄(mysql_init)
连接数据库前,需先初始化MYSQL结构体(句柄),它是后续所有操作的 “入口”,存储连接参数、函数指针等核心信息。
接口原型
MYSQL *mysql_init(MYSQL *mysql);
参数:若传入NULL,函数会自动分配内存创建新句柄;若传入已存在的句柄,会重置其状态;
返回值:成功返回MYSQL句柄指针,失败返回NULL(需检查返回值,避免空指针访问)。
示例代码
// 初始化MySQL句柄(传入NULL,自动分配内存)
MYSQL *mysql_handle = mysql_init(NULL);
if (mysql_handle == NULL) {
fprintf(stderr, "mysql_init failed: %s\n", mysql_error(mysql_handle));
return 1; // 初始化失败,退出程序
}
2.2 2. 连接 MySQL 数据库(mysql_real_connect)
初始化句柄后,通过该接口建立与 MySQL 服务器的 TCP 连接,需指定 IP、用户名、密码等核心参数。
接口原型
MYSQL *mysql_real_connect(
MYSQL *mysql, // 已初始化的句柄
const char *host, // MySQL服务器IP(本地用"localhost",远程用具体IP)
const char *user, // 登录用户名(如"root")
const char *passwd, // 登录密码(如"123456")
const char *db, // 要连接的数据库名(如"test")
unsigned int port, // MySQL端口号(默认3306)
const char *unix_socket, // Unix域套接字(本地连接用,远程设为NULL)
unsigned long clientflag // 客户端标志(一般设为0,无需特殊配置)
);
返回值:成功返回原句柄指针,失败返回NULL;
关键补充:连接成功后需设置字符集(默认latin1,中文会乱码),通过mysql_set_character_set()设置为utf8。
示例代码
// 连接MySQL服务器
MYSQL *conn = mysql_real_connect(
mysql_handle,
"localhost", // 本地服务器
"root", // 用户名
"123456", // 密码
"test", // 要操作的数据库
3306, // 端口号
NULL, // 远程连接,设为NULL
0 // 客户端标志
);
// 检查连接结果
if (conn == NULL) {
fprintf(stderr, "mysql_real_connect failed: %s\n", mysql_error(mysql_handle));
mysql_close(mysql_handle); // 关闭句柄,避免内存泄漏
return 1;
}
// 设置字符集为utf8,解决中文乱码问题
if (mysql_set_character_set(mysql_handle, "utf8") != 0) {
fprintf(stderr, "set charset failed: %s\n", mysql_error(mysql_handle));
mysql_close(mysql_handle);
return 1;
}
printf("MySQL connect success!\n");
2.3 3. 执行 SQL 语句(mysql_query)
连接成功后,通过该接口向 MySQL 服务器发送 SQL 语句(如查询、插入、更新),支持所有合法 SQL 语法。
接口原型
int mysql_query(MYSQL *mysql, const char *q);
参数:q为要执行的 SQL 字符串(需以\0结尾,无需加;);
返回值:成功返回 0,失败返回非 0(可通过mysql_error()查看错误信息);
注意:若 SQL 语句包含二进制数据(如图片),需用mysql_real_query()(支持二进制安全),普通文本用mysql_query()即可。
示例代码(执行查询与插入)
// 示例1:执行查询语句(查询test库的account表)
const char *select_sql = "SELECT id, name, blance FROM account";
if (mysql_query(mysql_handle, select_sql) != 0) {
fprintf(stderr, "select failed: %s\n", mysql_error(mysql_handle));
mysql_close(mysql_handle);
return 1;
}
// 示例2:执行插入语句(向account表插入数据)
const char *insert_sql = "INSERT INTO account (id, name, blance) VALUES (6, '钱七', 789.00)";
if (mysql_query(mysql_handle, insert_sql) != 0) {
fprintf(stderr, "insert failed: %s\n", mysql_error(mysql_handle));
mysql_close(mysql_handle);
return 1;
}
printf("Insert success, affected rows: %d\n", mysql_affected_rows(mysql_handle));
2.4 4. 处理查询结果(核心步骤)
若执行的是SELECT等查询语句,需通过一系列接口读取结果(如行数、列数、具体数据),这是最复杂的环节,需注意内存释放。
4.1 存储结果集(mysql_store_result)
将查询结果从 MySQL 服务器读取到本地内存,返回MYSQL_RES结构体(存储结果集),后续操作基于该结构体。
接口原型
MYSQL_RES *mysql_store_result(MYSQL *mysql);
返回值:成功返回结果集指针,失败返回NULL;
关键提醒:该函数会动态分配内存,必须调用mysql_free_result()释放,否则会导致内存泄漏。
4.2 获取结果行数 / 列数
行数:my_ulonglong mysql_num_rows(MYSQL_RES *res);(查询结果的总行数);
列数:unsigned int mysql_num_fields(MYSQL_RES *res);(每一行的列数)。
4.3 获取列名(mysql_fetch_fields)
获取结果集中所有列的名称(如 “id”“name”“blance”),方便输出时标识字段。
接口原型
MYSQL_FIELD *mysql_fetch_fields(MYSQL_RES *res);
返回值:指向列信息数组的指针,数组长度为列数,每个元素存储一列的名称、类型等信息。
4.4 获取每行数据(mysql_fetch_row)
逐行读取结果集中的数据,返回MYSQL_ROW(本质是char **,可视为字符串数组,每个元素对应一列数据)。
接口原型
MYSQL_ROW mysql_fetch_row(MYSQL_RES *result);
返回值:成功返回当前行数据指针,读取完毕返回NULL。
完整结果处理示例
// 1. 存储查询结果
MYSQL_RES *result = mysql_store_result(mysql_handle);
if (result == NULL) {
fprintf(stderr, "store result failed: %s\n", mysql_error(mysql_handle));
mysql_close(mysql_handle);
return 1;
}
// 2. 获取行数和列数
my_ulonglong row_count = mysql_num_rows(result);
unsigned int col_count = mysql_num_fields(result);
printf("查询结果:%llu 行,%u 列\n", row_count, col_count);
// 3. 获取列名并输出
MYSQL_FIELD *fields = mysql_fetch_fields(result);
for (unsigned int i = 0; i < col_count; i++) {
printf("%s\t", fields[i].name);
}
printf("\n");
// 4. 逐行读取数据并输出
MYSQL_ROW row;
while ((row = mysql_fetch_row(result)) != NULL) {
for (unsigned int i = 0; i < col_count; i++) {
// row[i]为字符串,NULL表示该列数据为NULL
printf("%s\t", row[i] ? row[i] : "NULL");
}
printf("\n");
}
// 5. 释放结果集内存(关键!避免内存泄漏)
mysql_free_result(result);
2.5 5. 关闭连接(mysql_close)
所有操作完成后,关闭 MySQL 连接并释放句柄内存,这是避免资源泄漏的最后一步。
接口原型
void mysql_close(MYSQL *mysql);
参数:已初始化的MYSQL句柄;
功能:关闭 TCP 连接,释放句柄占用的内存(包括mysql_init分配的内存)。
示例代码
// 关闭连接与句柄
mysql_close(mysql_handle);
printf("MySQL connection closed!\n");
三、完整实战案例:C 语言操作 MySQL 完整流程
整合上述接口,实现 “连接数据库→插入数据→查询数据→关闭连接” 的完整流程,代码可直接复用。
完整代码(mysql_c_demo.c)
#include <stdio.h>
#include <mysql.h>
int main() {
// 1. 初始化MySQL句柄
MYSQL *mysql_handle = mysql_init(NULL);
if (mysql_handle == NULL) {
fprintf(stderr, "mysql_init failed: %s\n", mysql_error(mysql_handle));
return 1;
}
// 2. 连接MySQL服务器
MYSQL *conn = mysql_real_connect(
mysql_handle,
"localhost",
"root",
"123456",
"test",
3306,
NULL,
0
);
if (conn == NULL) {
fprintf(stderr, "connect failed: %s\n", mysql_error(mysql_handle));
mysql_close(mysql_handle);
return 1;
}
// 设置字符集为utf8
if (mysql_set_character_set(mysql_handle, "utf8") != 0) {
fprintf(stderr, "set charset failed: %s\n", mysql_error(mysql_handle));
mysql_close(mysql_handle);
return 1;
}
printf("Connect MySQL success!\n");
// 3. 执行插入语句
const char *insert_sql = "INSERT INTO account (id, name, blance) VALUES (7, '赵八', 999.00)";
if (mysql_query(mysql_handle, insert_sql) != 0) {
fprintf(stderr, "insert failed: %s\n", mysql_error(mysql_handle));
mysql_close(mysql_handle);
return 1;
}
printf("Insert success, affected rows: %d\n", mysql_affected_rows(mysql_handle));
// 4. 执行查询语句并处理结果
const char *select_sql = "SELECT id, name, blance FROM account";
if (mysql_query(mysql_handle, select_sql) != 0) {
fprintf(stderr, "select failed: %s\n", mysql_error(mysql_handle));
mysql_close(mysql_handle);
return 1;
}
// 存储结果集
MYSQL_RES *result = mysql_store_result(mysql_handle);
if (result == NULL) {
fprintf(stderr, "store result failed: %s\n", mysql_error(mysql_handle));
mysql_close(mysql_handle);
return 1;
}
// 输出列名
unsigned int col_count = mysql_num_fields(result);
MYSQL_FIELD *fields = mysql_fetch_fields(result);
for (unsigned int i = 0; i < col_count; i++) {
printf("%s\t", fields[i].name);
}
printf("\n");
// 输出数据
MYSQL_ROW row;
while ((row = mysql_fetch_row(result)) != NULL) {
for (unsigned int i = 0; i < col_count; i++) {
printf("%s\t", row[i] ? row[i] : "NULL");
}
printf("\n");
}
// 释放结果集
mysql_free_result(result);
// 5. 关闭连接
mysql_close(mysql_handle);
printf("Connection closed!\n");
return 0;
}
编译与运行
# 编译(需替换为你的Connector/C路径)
gcc -o mysql_demo mysql_c_demo.c -I./Connector-C/include -L./Connector-C/lib -lmysqlclient
# 设置动态库路径并运行
export LD_LIBRARY_PATH=./Connector-C/lib
./mysql_demo
成功输出
Connect MySQL success!
Insert success, affected rows: 1
查询结果:7 行,3 列
id name blance
1 张三 100.00
2 李四 321.00
3 王五 5432.00
4 赵六 543.90
5 钱七 789.00
6 赵八 999.00
7 赵八 999.00
Connection closed!
四、关键注意事项与常见问题
- 动态库链接问题:运行时若报错 “找不到 libmysqlclient.so”,需通过
export LD_LIBRARY_PATH=库路径指定动态库查找路径(Linux),或把动态库放到/usr/lib目录(永久生效); - 内存泄漏:
mysql_store_result()返回的结果集必须用mysql_free_result()释放,否则会导致内存泄漏; - 中文乱码:连接成功后必须调用
mysql_set_character_set(mysql_handle, "utf8"),确保与数据库字符集一致; - 错误处理:所有接口调用后都需检查返回值,通过
mysql_error(mysql_handle)打印错误信息,方便定位问题; - 事务支持:若需事务(如转账),可通过
mysql_autocommit(mysql_handle, 0)关闭自动提交,mysql_commit()提交事务,mysql_rollback()回滚事务。
五、总结
C 语言连接 MySQL 的核心是 “依赖 Connector/C 库 + 按流程调用接口”,关键步骤可概括为 “初始化→连接→执行 SQL→处理结果→关闭”。掌握这些接口后,可实现数据的增删改查,满足 C 语言项目的持久化需求。

被折叠的 条评论
为什么被折叠?



