C 语言连接 MySQL 实战:从环境搭建到接口调用,搞定数据库交互

        在 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!

四、关键注意事项与常见问题

  1. 动态库链接问题:运行时若报错 “找不到 libmysqlclient.so”,需通过export LD_LIBRARY_PATH=库路径指定动态库查找路径(Linux),或把动态库放到/usr/lib目录(永久生效);
  2. 内存泄漏mysql_store_result()返回的结果集必须用mysql_free_result()释放,否则会导致内存泄漏;
  3. 中文乱码:连接成功后必须调用mysql_set_character_set(mysql_handle, "utf8"),确保与数据库字符集一致;
  4. 错误处理:所有接口调用后都需检查返回值,通过mysql_error(mysql_handle)打印错误信息,方便定位问题;
  5. 事务支持:若需事务(如转账),可通过mysql_autocommit(mysql_handle, 0)关闭自动提交,mysql_commit()提交事务,mysql_rollback()回滚事务。

五、总结

C 语言连接 MySQL 的核心是 “依赖 Connector/C 库 + 按流程调用接口”,关键步骤可概括为 “初始化→连接→执行 SQL→处理结果→关闭”。掌握这些接口后,可实现数据的增删改查,满足 C 语言项目的持久化需求。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值