《MySQL基础——C 语言链接》

一、联动前提:准备工作与库文件解析

要实现 C 语言与 MySQL 的连接,需先完成环境配置和库文件准备,这是后续开发的基础。

1. 三大核心准备步骤

确保 MySQL 服务正常运行:C 语言程序通过网络(TCP/IP)与 MySQL 交互,需先确认数据库服务已启动(如 Linux 下执行 systemctl start mysqld),否则会因连接超时导致失败

下载 MySQL C 接口库需从 MySQL 官网下载适配当前操作系统(Windows/Linux/macOS)的 Connector/C 库,该库包含 C 语言操作 MySQL 所需的头文件和函数实现。

理解库文件结构:下载后的库文件分为 include 和 lib 两个核心目录,分别对应方法声明和实现,结构如下(以 Linux 环境为例):

sql 复制代码
.
├── include/          # 头文件目录,包含所有接口方法声明
│   ├── mysql.h       # 核心头文件,包含多数函数和结构体定义
│   ├── errmsg.h      # 错误信息定义
│   ├── mysql_com.h   # 通信协议相关定义
│   └── ...(其他辅助头文件)
└── lib/              # 库文件目录,包含编译后的函数实现
    ├── libmysqlclient.a       # 静态库文件
    ├── libmysqlclient.so      # 动态库文件(Linux)
    └── libmysqlclient.dylib   # 动态库文件(macOS)

其中,include 目录的头文件用于代码编译时的语法校验,lib 目录的库文件用于链接阶段,将函数实现注入可执行程序。

二、第一步:验证库文件引入(编译与运行)

库文件下载后,需先通过简单代码验证是否能成功引入,避免后续开发因环境问题浪费时间。

1. 验证代码:获取 MySQL 客户端版本

通过调用 mysql_get_client_info() 函数(该函数返回当前使用的 MySQL 客户端版本),验证库文件是否正确关联:

cpp 复制代码
#include <stdio.h>
#include <mysql.h>  // 引入 MySQL C 接口核心头文件

int main() {
    // 输出 MySQL 客户端版本
    printf("MySQL Client Version: %s\n", mysql_get_client_info());
    return 0;
}

2. 编译命令与参数解析

在 Linux 环境下,需通过 gcc 编译代码,同时指定头文件路径(-I)和库文件路径(-L),命令如下:

bash 复制代码
gcc -o test_mysql test_mysql.c -I./include -L./lib -lmysqlclient
-I./include:#告诉编译器从当前目录的 include 文件夹查找头文件(如 mysql.h);
-L./lib:#指定库文件的搜索路径为当前目录的 lib 文件夹;
-lmysqlclient:#链接名为 mysqlclient 的库文件(编译器会自动匹配 libmysqlclient.a 或 libmysqlclient.so)。

3. 解决动态库加载问题

编译成功后,直接运行可执行文件(./test_mysql)可能出现以下错误:

sql 复制代码
./test_mysql: error while loading shared libraries: libmysqlclient.so.18: cannot open shared object file: No such file or directory

原因是系统默认的动态库搜索路径中没有 libmysqlclient.so,需通过环境变量 LD_LIBRARY_PATH 临时指定:

sql 复制代码
export LD_LIBRARY_PATH=./lib  # 将当前目录的 lib 文件夹加入动态库搜索路径
./test_mysql                  # 再次运行,成功输出版本信息

成功执行后,会输出类似以下结果,说明库文件引入正常:

sql 复制代码
MySQL Client Version: 6.1.6

至此,C 语言与 MySQL 的环境联动已完成,可进入核心接口开发阶段。

三、核心流程:C 语言操作 MySQL 的 6 步曲

通过 C 语言操作 MySQL 需遵循固定流程:初始化 → 连接数据库 → 执行 SQL → 处理结果 → 释放资源 → 关闭连接。以下按步骤详解每个环节的接口使用。

1. 步骤 1:初始化 MySQL 环境(mysql_init)

在执行任何数据库操作前,必须先初始化 MySQL 句柄 (MYSQL 结构体指针),该句柄将贯穿整个数据库交互过程,存储连接参数、状态等信息。

函数原型:

cpp 复制代码
MYSQL *mysql_init(MYSQL *mysql);

参数说明:若传入 NULL,函数会自动分配内存创建新的 MYSQL 句柄;若传入已存在的句柄,会对其重置。

示例代码:

cpp 复制代码
MYSQL *mysql_conn = mysql_init(NULL);  // 初始化句柄
if (mysql_conn == NULL) {
    fprintf(stderr, "mysql_init failed: %s\n", mysql_error(mysql_conn));
    return 1;  // 初始化失败,退出程序
}

初始化失败通常是因内存不足,需通过 mysql_error() 函数获取具体错误信息。

2. 步骤 2:连接 MySQL 数据库(mysql_real_connect)

初始化句柄后,通过 mysql_real_connect 函数建立与数据库的 TCP 连接 ,需传入数据库地址、用户名、密码等核心参数。

函数原型:

cpp 复制代码
MYSQL *mysql_real_connect(
    MYSQL *mysql,        // 已初始化的 MYSQL 句柄
    const char *host,    // 数据库主机地址(localhost/IP)
    const char *user,    // 数据库用户名
    const char *passwd,  // 数据库密码
    const char *db,      // 要连接的数据库名
    unsigned int port,   // 数据库端口(默认 3306)
    const char *unix_socket,  // Unix 域套接字(NULL 表示使用 TCP/IP)
    unsigned long clientflag  // 客户端标志(0 表示默认)
);

关键注意:连接成功后,若查询中文数据出现乱码,需通过 mysql_set_character_set(mysql_conn, "utf8"); 设置字符集(默认字符集为 latin1)。

示例代码:

cpp 复制代码
// 连接数据库
if (mysql_real_connect(
        mysql_conn,
        "localhost",  // 本地数据库
        "root",       // 用户名
        "123456",     // 密码
        "test_db",    // 要连接的数据库
        3306,         // 端口
        NULL,         // 不使用 Unix 套接字
        0             // 默认客户端标志
    ) == NULL) {
    fprintf(stderr, "Connect failed: %s\n", mysql_error(mysql_conn));
    mysql_close(mysql_conn);  // 关闭句柄,释放资源
    return 1;
}

// 设置字符集为 UTF-8,解决中文乱码
mysql_set_character_set(mysql_conn, "utf8");

3. 步骤 3:执行 SQL 语句(mysql_query)

连接成功后,通过 mysql_query 函数执行 SQL 语句 (支持查询、插入、更新、删除等所有操作)。

函数原型:

cpp 复制代码
int mysql_query(MYSQL *mysql, const char *q);
  • 返回值:成功返回 0,失败返回非 0(可通过 mysql_error() 获取错误原因)。
  • 参数说明:q 为要执行的 SQL 字符串(无需在末尾添加分号)。

示例代码:

cpp 复制代码
const char *sql = "SELECT id, name, age FROM student";  // 查询学生表
if (mysql_query(mysql_conn, sql) != 0) {
    fprintf(stderr, "Query failed: %s\n", mysql_error(mysql_conn));
    mysql_close(mysql_conn);
    return 1;
}

若执行插入 / 更新 / 删除等写操作(如 INSERT INTO student(name, age) VALUES('Alice', 20)),只需将 sql 字符串替换为对应语句,通过返回值判断执行结果即可。

4. 步骤 4:获取查询结果(mysql_store_result)

对于 SELECT 等查询类 SQL,执行 mysql_query 后需通过 mysql_store_result 函数获取结果集 ,该函数会将查询结果从数据库服务器读取到本地内存。

函数原型:

cpp 复制代码
MYSQL_RES *mysql_store_result(MYSQL *mysql);
  • 返回值:成功返回 MYSQL_RES 类型的结果集指针,失败返回 NULL。
  • 注意事项:结果集占用的内存需手动释放(通过 mysql_free_result()),否则会导致内存泄漏。 示例代码:
cpp 复制代码
MYSQL_RES *result = mysql_store_result(mysql_conn);
if (result == NULL) {
    fprintf(stderr, "Get result failed: %s\n", mysql_error(mysql_conn));
    mysql_close(mysql_conn);
    return 1;
}

5. 步骤 5:解析结果集(4 个核心函数)

获取 MYSQL_RES 结果集后,需通过以下函数解析数据,包括行数、列数、列名和具体字段值。

函数名称 原型 功能说明
mysql_num_rows my_ulonglong mysql_num_rows(MYSQL_RES *res) 获取结果集的总行数
mysql_num_fields unsigned int mysql_num_fields(MYSQL_RES *res) 获取结果集的总列数
mysql_fetch_fields MYSQL_FIELD *mysql_fetch_fields(MYSQL_RES *res) 获取所有列的信息(如列名、类型)
mysql_fetch_row MYSQL_ROW mysql_fetch_row(MYSQL_RES *res) 逐行读取结果集,返回当前行的字段值(二维数组)

解析示例代码:

cpp 复制代码
// 1. 获取行数和列数
my_ulonglong row_count = mysql_num_rows(result);  // 总行数
unsigned int field_count = mysql_num_fields(result);  // 总列数
printf("Total Rows: %llu, Total Columns: %u\n", row_count, field_count);

// 2. 获取列名并输出
MYSQL_FIELD *fields = mysql_fetch_fields(result);
printf("Columns: ");
for (unsigned int i = 0; i < field_count; i++) {
    printf("%s ", fields[i].name);  // 输出列名(如 id name age)
}
printf("\n");

// 3. 逐行读取数据并输出
MYSQL_ROW row;
while ((row = mysql_fetch_row(result)) != NULL) {
    for (unsigned int i = 0; i < field_count; i++) {
        printf("%s ", row[i] ? row[i] : "NULL");  // 输出字段值(处理 NULL)
    }
    printf("\n");
}
sql 复制代码
Total Rows: 2, Total Columns: 3
Columns: id name age 
1 Alice 20 
2 Bob 22 

其中,mysql_fetch_row 每次调用会移动到下一行,返回 NULL 表示已读取完所有数据。

6. 步骤 6:释放资源与关闭连接

操作完成后,需按顺序释放结果集、关闭数据库连接,避免资源泄漏。

  • 释放结果集:mysql_free_result(result);(必须在关闭连接前执行);
  • 关闭连接:mysql_close(mysql_conn);(释放 MYSQL 句柄占用的内存)。

示例代码:

cpp 复制代码
mysql_free_result(result);  // 释放结果集
mysql_close(mysql_conn);    // 关闭数据库连接

四、进阶拓展:事务支持

MySQL C 接口同样支持事务操作,通过以下函数实现事务的提交和回滚,适用于需要保证数据一致性的场景(如转账、订单创建等):

函数名称 原型 功能说明
mysql_autocommit my_bool mysql_autocommit(MYSQL *mysql, my_bool auto_mode) 设置自动提交模式(0 关闭自动提交,1 开启)
mysql_commit my_bool mysql_commit(MYSQL *mysql) 提交事务
mysql_rollback my_bool mysql_rollback(MYSQL *mysql) 回滚事务

事务示例代码:

cpp 复制代码
// 关闭自动提交,开启事务
mysql_autocommit(mysql_conn, 0);

// 执行多个 SQL 操作(如转账:A 减 100,B 加 100)
const char *sql1 = "UPDATE account SET balance = balance - 100 WHERE name = 'A'";
const char *sql2 = "UPDATE account SET balance = balance + 100 WHERE name = 'B'";

if (mysql_query(mysql_conn, sql1) == 0 && mysql_query(mysql_conn, sql2) == 0) {
    mysql_commit(mysql_conn);  // 两个操作都成功,提交事务
    printf("Transaction committed\n");
} else {
    mysql_rollback(mysql_conn);  // 任一操作失败,回滚事务
    fprintf(stderr, "Transaction rolled back: %s\n", mysql_error(mysql_conn));
}

// 恢复自动提交模式
mysql_autocommit(mysql_conn, 1);

五、总结与注意事项

1. 核心流程回顾

C 语言操作 MySQL 的核心是 "句柄初始化 → 连接 → 执行 SQL → 处理结果 → 释放资源" 的闭环流程 ,每个步骤都依赖特定的接口函数,且需严格按顺序执行(如必须先初始化再连接,先释放结果集再关闭连接)。

2. 关键注意事项

  • 内存管理:mysql_store_result 分配的结果集必须通过 mysql_free_result 释放,否则会导致内存泄漏;
  • 错误处理:每个接口函数调用后都需判断返回值,通过 mysql_error(mysql_conn) 获取具体错误信息,便于调试;
  • 字符集:连接成功后务必设置 utf8 字符集,避免中文乱码
  • 动态库路径:运行程序时若提示 "找不到动态库",需通过LD_LIBRARY_PATH(Linux)或DYLD_LIBRARY_PATH(macOS)指定库文件路径。
相关推荐
KIDAKN7 小时前
Redis 分布式锁
数据库·redis·分布式
程序新视界7 小时前
如何为MySQL中的JSON字段设置索引
数据库·mysql
Ultipa7 小时前
查询语言的进化:SQL之后,为什么是GQL?数据世界正在改变
数据库·sql·图数据库·gql
LB21127 小时前
SQL隐式链接显式连接
大数据·数据库·sql
隔壁阿布都8 小时前
spring boot + mybatis 使用线程池异步修改数据库数据
数据库·spring boot·mybatis
阿让啊13 小时前
C语言strtol 函数使用方法
c语言·数据结构·c++·单片机·嵌入式硬件
MAGICIAN...16 小时前
【Redis】--持久化机制
数据库·redis·缓存
我真的是大笨蛋16 小时前
JVM调优总结
java·jvm·数据库·redis·缓存·性能优化·系统架构
Florence2317 小时前
计算机组成原理:GPU架构、并行计算、内存层次结构等
c语言