C 语言连接 MySQL(基于 MySQL Connector/C)
MySQL 的基础我们已初步掌握,后续重点关注如何通过 C 语言调用 MySQL 接口实现数据库操作。要使用 C 语言连接 MySQL,需借助 MySQL 官网提供的 Connector/C 库(C 语言接口库),以下是完整的使用指南(含准备工作、库验证、核心接口说明)。
一、准备工作
- 确保 MySQL 服务已启动并正常运行(可通过
systemctl status mysqld或netstat -tulnp | grep 3306验证)。 - 下载对应平台的 MySQL Connector/C 库(官网地址:https://dev.mysql.com/downloads/connector/c/),下载后获取包含头文件和库文件的压缩包。
二、Connector/C 库目录结构
解压后库文件的目录结构如下(以实际下载版本为例):
.
├── include # 所有接口的头文件(方法声明)
│ ├── big_endian.h
│ ├── byte_order_generic.h
│ ├── byte_order_generic_x86.h
│ ├── decimal.h
│ ├── errmsg.h
│ ├── keycache.h
│ ├── little_endian.h
│ ├── m_ctype.h
│ ├── m_string.h
│ ├── my_alloc.h
│ ├── my_byteorder.h
│ ├── my_compiler.h
│ ├── my_config.h
│ ├── my_dbug.h
│ ├── my_dir.h
│ ├── my_getopt.h
│ ├── my_global.h
│ ├── my_list.h
│ ├── my_pthread.h
│ ├── mysql # 子目录:MySQL专属头文件
│ │ ├── client_authentication.h
│ │ ├── client_plugin.h
│ │ ├── client_plugin.h.pp
│ │ ├── get_password.h
│ │ ├── plugin_auth_common.h
│ │ ├── plugin_trace.h
│ │ ├── psi # 性能监控相关头文件
│ │ │ ├── mysql_file.h
│ │ │ ├── mysql_idle.h
│ │ │ ├── mysql_mdl.h
│ │ │ ├── mysql_memory.h
│ │ │ ├── mysql_ps.h
│ │ │ ├── mysql_socket.h
│ │ │ ├── mysql_sp.h
│ │ │ ├── mysql_stage.h
│ │ │ ├── mysql_statement.h
│ │ │ ├── mysql_table.h
│ │ │ ├── mysql_thread.h
│ │ │ ├── mysql_transaction.h
│ │ │ ├── psi_base.h
│ │ │ ├── psi.h
│ │ │ └── psi_memory.h
│ │ ├── service_my_snprintf.h
│ │ └── service_mysql_alloc.h
│ ├── mysql_com.h
│ ├── mysql_com_server.h
│ ├── mysqld_ername.h
│ ├── mysqld_error.h
│ ├── mysql_embed.h
│ ├── mysql.h # 核心头文件(必须包含)
│ ├── mysql_time.h
│ ├── mysql_version.h
│ ├── my_sys.h
│ ├── my_xml.h
│ ├── sql_common.h
│ ├── sql_state.h
│ ├── sslopt-case.h
│ ├── sslopt-longopts.h
│ ├── sslopt-vars.h
│ └── typelib.h
└── lib # 库文件(方法实现,静态库+动态库)
├── libmysqlclient.a # 静态库
├── libmysqlclient_r.a -> libmysqlclient.a # 静态库软链接
├── libmysqlclient_r.so -> libmysqlclient.so # 动态库软链接
├── libmysqlclient_r.so.18 -> libmysqlclient.so.18 # 动态库版本软链接
├── libmysqlclient_r.so.18.3.0 -> libmysqlclient.so.18.3.0 # 动态库版本软链接
├── libmysqlclient.so -> libmysqlclient.so.18 # 动态库软链接
├── libmysqlclient.so.18 -> libmysqlclient.so.18.3.0 # 动态库版本软链接
└── libmysqlclient.so.18.3.0 # 实际动态库文件
核心说明 :include目录包含所有 C 语言接口的声明(需在代码中#include <mysql.h>),lib目录包含编译和运行时依赖的库文件(静态库.a或动态库.so)。
三、验证库引入(测试连接)
通过调用mysql_get_client_info()函数验证库是否成功引入,该函数用于返回 MySQL 客户端版本号。
1. 测试代码(test.c)
cpp
#include <stdio.h>
#include <mysql.h> // 核心头文件,包含所有接口声明
int main() {
// 输出MySQL客户端版本号,验证库引入成功
printf("mysql client Version: %s\n", mysql_get_client_info());
return 0;
}
2. 编译命令
bash
[hb@MiWiFi-R1CL-srv lib]$ gcc -o test test.c -I./include -L./lib -lmysqlclient
-I./include:指定头文件搜索路径(指向库的include目录);-L./lib:指定库文件搜索路径(指向库的lib目录);-lmysqlclient:链接 MySQL 客户端库(动态库优先,若需静态链接可加-static)。
3. 运行测试与问题解决
(1)运行报错(动态库查找失败)
bash
[hb@MiWiFi-R1CL-srv lib]$ ./test
./test: error while loading shared libraries: libmysqlclient.so.18: cannot open shared object file: No such file or directory
原因 :系统默认的动态库搜索路径(/lib、/usr/lib)中没有libmysqlclient.so.18,需手动指定动态库查找路径。
(2)解决方案(临时设置环境变量)
bash
[hb@MiWiFi-R1CL-srv lib]$ export LD_LIBRARY_PATH=./lib # 指定动态库搜索路径为当前目录下的lib文件夹
[hb@MiWiFi-R1CL-srv lib]$ ./test
mysql client Version: 6.1.6 # 输出版本号,说明库引入成功
- 备注:
ldd test命令可查看程序依赖的动态库及查找状态(验证是否找到libmysqlclient.so)。
比特就业课
四、MySQL C API 核心接口详解
库引入成功后,需熟悉核心接口的使用流程:初始化 → 连接数据库 → 执行 SQL → 处理结果 → 关闭连接。
1. 初始化 MySQL 环境:mysql_init()
功能
初始化 MySQL 连接句柄(核心变量MYSQL),后续所有操作都依赖该句柄。
原型
cpp
MYSQL *mysql_init(MYSQL *mysql);
参数与返回值
mysql:若传入NULL,函数会自动分配内存创建新的MYSQL句柄;若传入已存在的句柄,则重置该句柄;- 返回值:成功返回
MYSQL句柄指针,失败返回NULL。
示例
cpp
MYSQL *myfd = mysql_init(NULL); // 初始化句柄,myfd为核心操作对象
if (myfd == NULL) {
printf("mysql_init failed: %s\n", mysql_error(myfd));
return -1;
}
2. 连接数据库:mysql_real_connect()
功能
基于初始化后的MYSQL句柄,建立与 MySQL 服务器的连接(底层基于 TCP/IP 协议)。
原型
cpp
MYSQL *mysql_real_connect(
MYSQL *mysql, // 已初始化的MYSQL句柄(mysql_init返回值)
const char *host, // MySQL服务器地址(localhost/127.0.0.1/远程IP)
const char *user, // 登录用户名(如root、whb)
const char *passwd, // 登录密码
const char *db, // 要连接的数据库名(如test、scott)
unsigned int port, // MySQL服务端口号(默认3306)
const char *unix_socket, // Unix域套接字(本地连接可用,远程连接填NULL)
unsigned long clientflag // 客户端标志(一般填0,特殊需求参考官方文档)
);
返回值
- 成功返回
mysql句柄指针(与传入参数一致); - 失败返回
NULL,可通过mysql_error(myfd)获取错误信息。
关键补充(中文乱码解决)
连接成功后,默认字符集为latin1,若需支持中文,需手动设置字符集为utf8:
cpp
mysql_set_character_set(myfd, "utf8"); // 设置连接字符集(必须在连接成功后调用)
示例
cpp
// 连接MySQL服务器(本地、用户名whb、密码87654321、数据库test、端口3306)
if (mysql_real_connect(myfd, "localhost", "whb", "87654321", "test", 3306, NULL, 0) == NULL) {
printf("connect failed: %s\n", mysql_error(myfd));
mysql_close(myfd); // 失败需关闭句柄释放资源
return -1;
}
// 设置字符集(解决中文乱码)
mysql_set_character_set(myfd, "utf8");
3. 执行 SQL 语句:mysql_query()
功能
向 MySQL 服务器下发 SQL 语句(查询、插入、更新、删除等)。
原型
cpp
int mysql_query(MYSQL *mysql, const char *q);
参数与返回值
mysql:已连接的MYSQL句柄;q:要执行的 SQL 语句(字符串格式,无需加分号结尾);- 返回值:成功返回
0,失败返回非 0(通过mysql_error(myfd)获取错误信息)。
示例
cpp
// 1. 查询语句(select)
const char *sql1 = "select * from account";
if (mysql_query(myfd, sql1) != 0) {
printf("query failed: %s\n", mysql_error(myfd));
mysql_close(myfd);
return -1;
}
// 2. 插入语句(insert)
const char *sql2 = "insert into account(name, blance) values('张三', 1000.00)";
if (mysql_query(myfd, sql2) != 0) {
printf("insert failed: %s\n", mysql_error(myfd));
mysql_close(myfd);
return -1;
}
4. 获取查询结果:mysql_store_result()
功能
仅用于查询语句(select) ,读取 SQL 执行后的结果集,将结果加载到内存中(需手动释放内存)。
原型
cpp
MYSQL_RES *mysql_store_result(MYSQL *mysql);
参数与返回值
mysql:已执行查询的MYSQL句柄;- 返回值:成功返回
MYSQL_RES类型的结果集指针(存储查询数据),失败返回NULL; - 注意:该函数会动态分配内存,使用完毕后必须调用
mysql_free_result()释放,否则会造成内存泄漏。
示例
cpp
// 执行查询后获取结果集
MYSQL_RES *res = mysql_store_result(myfd);
if (res == NULL) {
printf("get result failed: %s\n", mysql_error(myfd));
mysql_close(myfd);
return -1;
}
// ... 后续处理结果集 ...
mysql_free_result(res); // 释放结果集内存(必须调用)
5. 结果集相关操作(读取数据)
获取MYSQL_RES结果集后,需通过以下接口读取行数、列数、列名、具体数据:
(1)获取结果行数:mysql_num_rows()
- 功能:返回查询结果的总行数;
- 原型:
my_ulonglong mysql_num_rows(MYSQL_RES *res); - 参数:
res为mysql_store_result返回的结果集; - 示例:
my_ulonglong nums = mysql_num_rows(res); // nums为查询结果总行数。
(2)获取结果列数:mysql_num_fields()
- 功能:返回查询结果的总列数;
- 原型:
unsigned int mysql_num_fields(MYSQL_RES *res); - 示例:
unsigned int fields = mysql_num_fields(res); // fields为查询结果总列数。
(3)获取列名:mysql_fetch_fields()
- 功能:返回所有列的字段信息(含列名、数据类型等);
- 原型:
MYSQL_FIELD *mysql_fetch_fields(MYSQL_RES *res); - 返回值:
MYSQL_FIELD数组指针,数组长度为列数; - 示例(输出所有列名):
cpp
unsigned int fields = mysql_num_fields(res);
MYSQL_FIELD *field = mysql_fetch_fields(res); // 获取列信息数组
for (int i = 0; i < fields; i++) {
printf("%s ", field[i].name); // 输出列名(如id name blance)
}
printf("\n");
(4)获取行数据:mysql_fetch_row()
- 功能:逐行读取结果集中的数据;
- 原型:
MYSQL_ROW mysql_fetch_row(MYSQL_RES *result); - 返回值:
MYSQL_ROW本质是char **(字符串二维数组),每行数据存储在数组中;读取到末尾时返回NULL; - 示例(逐行输出所有数据):
cpp
my_ulonglong nums = mysql_num_rows(res); // 总行数
unsigned int fields = mysql_num_fields(res); // 总列数
MYSQL_ROW line; // 存储一行数据
// 循环读取每一行
for (int i = 0; i < nums; i++) {
line = mysql_fetch_row(res); // 读取一行数据
if (line == NULL) break;
// 输出当前行的所有列数据
for (int j = 0; j < fields; j++) {
printf("%s ", line[j]); // 列数据为字符串格式(需自行转换类型)
}
printf("\n");
}
6. 关闭数据库连接:mysql_close()
功能
关闭与 MySQL 服务器的连接,释放MYSQL句柄占用的资源。
原型
cpp
void mysql_close(MYSQL *sock);
参数
sock:已连接的MYSQL句柄(mysql_init返回值);
示例
cpp
mysql_free_result(res); // 先释放结果集(若有)
mysql_close(myfd); // 关闭连接,释放句柄资源
7. 事务相关接口(扩展)
MySQL C API 支持事务操作(需确保表引擎为 InnoDB),核心接口如下(自行了解用法):
- 开启 / 关闭自动提交:
my_bool STDCALL mysql_autocommit(MYSQL *mysql, my_bool auto_mode);auto_mode=1:自动提交(默认);auto_mode=0:关闭自动提交(需手动提交 / 回滚);
- 提交事务:
my_bool STDCALL mysql_commit(MYSQL *mysql); - 回滚事务:
my_bool STDCALL mysql_rollback(MYSQL *mysql);
事务示例框架
cpp
mysql_autocommit(myfd, 0); // 关闭自动提交
// 执行多个SQL语句(insert/update/delete)
if (执行成功) {
mysql_commit(myfd); // 提交事务
} else {
mysql_rollback(myfd); // 回滚事务
}
mysql_autocommit(myfd, 1); // 恢复自动提交
五、完整操作流程总结
- 初始化句柄:
mysql_init(); - 连接数据库:
mysql_real_connect()+ 设置字符集mysql_set_character_set(); - 执行 SQL:
mysql_query()(查询 / 插入 / 更新 / 删除); - 处理结果(仅查询):
mysql_store_result()获取结果集;mysql_num_rows()/mysql_num_fields()获取行数 / 列数;mysql_fetch_fields()获取列名;mysql_fetch_row()逐行读取数据;mysql_free_result()释放结果集;
- 关闭连接:
mysql_close()。