MySQL CAPI核心操作全解析

MySQL C API 核心知识点精讲

同学们,今天我们来学习MySQL C API ,简单说,它就是 MySQL 官方提供的C 语言操作 MySQL 数据库的函数库------ 之前我们都是用 MySQL 命令行执行 SQL,而实际开发中(比如写 C/C++ 项目),需要通过代码直接操作数据库,这时候就要用到 MySQL C API 了。

整个知识点我们按 **「环境准备→核心 API 分模块讲解→固定使用流程→示例程序拆解」** 来讲,API 部分会讲清作用、参数、返回值、注意事项 ,同时标注高频考点、易错点,保证大家能理解、会用、能调试。

所有内容基于 Linux 环境(Ubuntu/CentOS),Windows 环境仅库文件配置不同,API 用法完全一致。

一、前置准备:环境搭建与编译规则

使用 MySQL C API 的第一步是搞定环境,这是基础,代码写得再对,环境错了也跑不起来,核心就 3 点:装库、引头文件、编译加链接选项

1. 安装依赖库

Linux 下安装libmysqlclient-dev(MySQL 客户端开发库,包含头文件和链接库):

cpp 复制代码
sudo apt install libmysqlclient-dev  # Ubuntu/Debian
# CentOS/RHEL用这个:sudo yum install mysql-devel

2. 代码中引入头文件

所有使用 MySQL C API 的 C 文件,必须在开头包含头文件,它定义了 API 函数、MYSQL 相关结构体:

cpp 复制代码
#include <mysql/mysql.h>

3. 编译时的链接选项

gcc 编译时,必须加-lmysqlclient,告诉编译器链接 MySQL 客户端库,否则会报「未定义的引用」错误。编译命令模板:

cpp 复制代码
gcc 你的代码文件.c -o 可执行程序名 -lmysqlclient

比如测试版本的代码编译:

cpp 复制代码
gcc t_mysql_version.c -o t_mysql_version -lmysqlclient

小测试:验证环境是否正常

写一段简单代码调用mysql_get_client_info()(获取 MySQL 客户端版本),编译执行能输出版本号,说明环境没问题:

cpp 复制代码
#include <stdio.h>
#include <mysql/mysql.h>
int main() {
    printf("MySQL Client Version: %s\n", mysql_get_client_info());
    return 0;
}

二、核心 API 分模块讲解

MySQL C API 的函数是按操作流程设计 的,我们按「库初始化→对象初始化→建立连接→执行 SQL→错误诊断→处理结果→释放资源 / 关闭连接」的顺序讲,每个函数讲清干什么、怎么用、注意什么

先明确一个核心概念:MYSQL 结构体 MYSQL 是 MySQL C API 的核心句柄(handle),相当于操作 MySQL 数据库的「总把手」,后续所有操作(连接、执行 SQL、获取结果)都要基于这个结构体的对象,必须先初始化才能用。

模块 1:MySQL 库的全局初始化与终止

作用:对 MySQL 客户端库做全局的初始化 / 释放 ,是所有 MySQL C API 调用的前置 / 后置操作,整个程序只需要执行一次。

1. mysql_library_init():初始化 MySQL 库
cpp 复制代码
int mysql_library_init(int argc, char** argv, char** groups);
  • 返回值:成功返回 0,失败返回非 0;

  • 参数 :新版本中argc=0、argv=NULL、groups=NULL 即可(旧版本用于嵌入式服务器,现已废弃);

  • 核心注意点线程不安全 !必须在程序创建任何子线程之前调用,否则会出现未知错误;

  • 用法

    cpp 复制代码
    if (mysql_library_init(0, NULL, NULL)) {
        fprintf(stderr, "MySQL库初始化失败!\n");
        exit(1); // 失败直接退出
    }
2. mysql_library_end():终止 MySQL 库
cpp 复制代码
void mysql_library_end(void);
  • 作用:释放 MySQL 库占用的全局资源,避免内存泄漏;
  • 调用时机 :程序结束前,所有 MySQL 操作完成、连接关闭后调用;
  • 无参数、无返回值,直接调用即可。

模块 2:MYSQL 对象的初始化(操作数据库的句柄)

作用:初始化一个MYSQL 类型的对象,后续建立连接、执行 SQL 都要用到这个对象,相当于为「数据库连接」创建一个载体。

cpp 复制代码
MYSQL* mysql_init(MYSQL* mysql);
  • 参数 :传入一个 MYSQL 指针,分两种情况:

    1. NULL:函数会在堆上自动申请内存创建并初始化 MYSQL 对象,返回对象地址;
    2. 传已定义的 MYSQL 对象地址:函数直接初始化该对象,返回原地址;
  • 返回值:成功返回初始化后的 MYSQL * 指针,失败返回 NULL(内存不足导致);

  • 常用用法 (推荐传 NULL,无需手动定义对象):

    cpp 复制代码
    MYSQL* mysql = mysql_init(NULL);
    if (mysql == NULL) {
        fprintf(stderr, "MYSQL对象初始化失败!\n");
        exit(1);
    }

模块 3:建立与 MySQL 服务器的连接

作用:通过初始化后的 MYSQL 对象,连接到指定的 MySQL 服务器 ,指定要操作的数据库、用户名、密码等。核心函数:mysql_real_connect()

cpp 复制代码
MYSQL* mysql_real_connect(
    MYSQL* mysql,        // 已初始化的MYSQL对象指针
    const char* host,    // 主机名/IP:NULL/localhost=连接本地MySQL
    const char* user,    // MySQL用户名(如root)
    const char* passwd,  // MySQL密码
    const char* db,      // 要连接的数据库名(如test)
    unsigned int port,   // 端口:0=使用默认3306
    const char* unix_socket, // 本地套接字:填NULL即可
    unsigned long client_flag // 连接标志:填0即可
);
  • 返回值 :成功返回第一个参数mysql(原对象指针),失败返回 NULL;

  • 核心注意点 :连接失败时,必须用错误诊断函数查原因(后面讲),不要只打印 "连接失败";

  • 常用用法 (连接本地 MySQL,数据库 test,用户名 root,密码 1234):

    cpp 复制代码
    if (mysql_real_connect(mysql, NULL, "root", "1234", "test", 0, NULL, 0) == NULL) {
        // 连接失败,打印错误信息
        fprintf(stderr, "连接失败:%s\n", mysql_error(mysql));
        exit(1);
    }

模块 4:执行 SQL 语句

作用:通过已建立连接的 MYSQL 对象,执行任意 SQL 语句(增删改查、建表、删库等),提供了两个函数,根据场景选择即可。

1. mysql_query():执行普通 SQL 语句(最常用)
cpp 复制代码
int mysql_query(MYSQL* mysql, const char* stmt);
  • 参数
    • mysql:已连接的 MYSQL 对象指针;
    • stmt:要执行的 SQL 语句字符串,注意 3 点 :① 以\0结尾(C 语言普通字符串即可);② 不能以;或 \g 结尾 (和命令行不同);③ 只能执行无二进制数据的 SQL(如普通增删改查);
  • 返回值:成功返回 0,失败返回非 0;
2. mysql_real_query():执行含二进制数据的 SQL
cpp 复制代码
int mysql_real_query(MYSQL* mysql, const char* stmt, unsigned long length);
  • 适用场景 :SQL 语句中包含二进制数据 (如图片、文件内容,可能含\0字符),mysql_query()会把\0当作字符串结尾,导致 SQL 截断,此时用这个函数;
  • 参数 :多了length,表示 SQL 语句的字节长度 (手动指定,避免\0截断);
  • 额外优势 :比mysql_query()快,因为不需要调用strlen()计算字符串长度;
  • 返回值:成功返回 0,失败返回非 0;
通用用法(执行 SELECT 查询)
cpp 复制代码
char sql[] = "SELECT * FROM employees"; // 无;,普通SQL
if (mysql_query(mysql, sql)) { // 失败返回非0,直接判断
    fprintf(stderr, "执行SQL失败:%s\n", mysql_error(mysql));
    exit(1);
}

模块 5:错误诊断(调试必备!)

作用:所有 MySQL C API 函数执行失败后,都要用这两个函数查具体原因 ,避免只知道 "失败",不知道为什么失败,是调试的核心函数。这两个函数的规则:返回最近一次 MySQL API 操作的错误信息,操作成功则返回 "无错误"。

  • mysql_errno() :用来判断有没有出错。
  • mysql_error() :用来描述错在哪里。
1. mysql_errno():获取错误码
cpp 复制代码
unsigned int mysql_errno(MYSQL* mysql);
  • 返回值:0 = 无错误,非 0 = 错误码(可查 MySQL 官方文档对应错误原因);
2. mysql_error():获取错误描述字符串
cpp 复制代码
const char* mysql_error(MYSQL* mysql);
  • 返回值 :空字符串""= 无错误,非空 = 具体的错误原因(如 "访问被拒绝""表不存在");
核心用法

所有 API 执行后,失败则调用这两个函数,示例:

运行

cpp 复制代码
if (mysql_real_connect(...) == NULL) {
    // 同时打印错误码和错误描述,调试更方便
    fprintf(stderr, "错误码:%d,错误原因:%s\n", mysql_errno(mysql), mysql_error(mysql));
    exit(1);
}

模块 6:处理 SQL 执行结果

执行 SQL 后,分两种场景 ,处理方式完全不同,这是 MySQL C API 的重点和难点,一定要区分清楚:

场景 1:执行查询类 SQL (SELECT/SHOW/DESC)→ 有结果集需要提取和遍历
场景 2:执行修改类 SQL (INSERT/UPDATE/DELETE/CREATE)→ 无结果集,只需获取受影响的行数
子模块 1:处理查询类 SQL 的结果集(核心)

步骤:提取结果集→获取行 / 列数→遍历结果→释放结果集,对应一组函数,缺一不可。

1. mysql_store_result():提取并保存结果集

把 MySQL 服务器返回的查询结果,一次性读取到客户端内存中 ,返回结果集句柄MYSQL_RES*

cpp 复制代码
MYSQL_RES* mysql_store_result(MYSQL* mysql);
  • 返回值 :① 成功有结果→返回MYSQL_RES*(结果集指针);② 成功无结果(如查询无数据)→返回 NULL;③ 失败(如 SQL 错误、网络问题)→返回 NULL;

  • 核心难点返回 NULL 时,如何区分「无结果」和「执行失败」? 解决方案:调用mysql_errno()非 0 = 失败,0 = 无结果

    cpp 复制代码
    MYSQL_RES* result = mysql_store_result(mysql);
    if (result == NULL) {
        if (mysql_errno(mysql) != 0) {
            // 失败,打印错误
            fprintf(stderr, "提取结果集失败:%s\n", mysql_error(mysql));
            exit(1);
        } else {
            // 无结果,正常提示
            printf("查询结果为空!\n");
            return 0;
        }
    }
2. mysql_num_rows():获取结果集的记录数(行数)
cpp 复制代码
uint64_t mysql_num_rows(MYSQL_RES* result);
  • 参数mysql_store_result()返回的结果集指针;
  • 返回值:结果集的行数,永远成功(无结果则返回 0);
3. mysql_num_fields():获取结果集的字段数(列数)
cpp 复制代码
unsigned int mysql_num_fields(MYSQL_RES* result);
  • 参数:结果集指针;
  • 返回值:结果集的列数,永远成功;
4. mysql_fetch_row():遍历结果集,获取下一条记录
cs 复制代码
MYSQL_ROW mysql_fetch_row(MYSQL_RES* result);
  • 核心概念MYSQL_ROW字符指针数组char**),数组的每个元素对应记录的一列值,列值以字符串形式存储(MySQL 会自动把数值转字符串);
  • 返回值 :成功返回当前记录的MYSQL_ROW无更多记录则返回 NULL
  • 用法 :用while循环遍历,直到返回 NULL;
5. mysql_free_result():释放结果集(必做!)
cpp 复制代码
void mysql_free_result(MYSQL_RES* result);
  • 作用 :释放mysql_store_result()申请的内存,避免内存泄漏
  • 调用时机:结果集遍历完成后,立即调用;
  • 无返回值,直接传结果集指针即可。
结果集处理完整示例
cpp 复制代码
// 提取结果集
MYSQL_RES* result = mysql_store_result(mysql);
if (result == NULL) { /* 判错逻辑 */ }
// 获取行/列数
uint64_t row_num = mysql_num_rows(result);
unsigned col_num = mysql_num_fields(result);
printf("共%d行,%d列\n", row_num, col_num);
// 遍历结果集
MYSQL_ROW row;
while ((row = mysql_fetch_row(result))) { // 取一条记录,直到NULL
    for (int i = 0; i < col_num; i++) {   // 遍历每一列
        printf("%s\t", row[i]);           // 列值是字符串,直接打印
    }
    printf("\n");
}
// 释放结果集
mysql_free_result(result);
子模块 2:处理修改类 SQL 的结果(无结果集)

执行 INSERT/UPDATE/DELETE 后,不需要结果集,只需知道有多少行数据被修改 ,用mysql_affected_rows()

cpp 复制代码
uint64_t mysql_affected_rows(MYSQL* mysql);
  • 参数:已连接的 MYSQL 对象指针;

  • 返回值 :① 成功→返回受影响的行数(0 = 无行被修改,如 UPDATE 的条件不匹配);② 失败→返回(uint64_t)-1

  • 用法 (执行 INSERT 后获取插入行数):

    cpp 复制代码
    char sql[] = "INSERT INTO employees(department) VALUES('研发部')";
    if (mysql_query(mysql, sql)) { /* 判错 */ }
    uint64_t n = mysql_affected_rows(mysql);
    printf("成功插入%d行数据\n", n); // 输出:成功插入1行数据

模块 7:关闭数据库连接

作用:断开与 MySQL 服务器的连接,释放 MYSQL 对象的内存 (如果是mysql_init(NULL)创建的对象)

cpp 复制代码
void mysql_close(MYSQL* mysql);
  • 参数:已建立连接的 MYSQL 对象指针;
  • 核心注意点
    1. 调用后,该 MYSQL 对象不能再被使用
    2. 如果mysql_init()传的是 NULL(堆上创建的对象),mysql_close()会自动释放对象的内存,无需手动 free;
    3. 调用时机:所有数据库操作完成、结果集释放后。

三、MySQL C API 固定使用流程

所有用 MySQL C API 操作数据库的 C 程序,流程都是固定的,记熟这个流程,写代码就不会乱,按步骤来即可:

plaintext

cpp 复制代码
1. 初始化MySQL库:mysql_library_init(0, NULL, NULL)
2. 初始化MYSQL对象:mysql_init(NULL)
3. 建立数据库连接:mysql_real_connect(...)
4. 执行SQL语句:mysql_query()/mysql_real_query()
5. 处理执行结果:
   - 查:提取结果集→遍历→释放结果集
   - 增删改:获取受影响行数
6. 释放资源:
   - 有结果集:mysql_free_result()
7. 关闭数据库连接:mysql_close()
8. 终止MySQL库:mysql_library_end()

核心原则先初始化,再使用;先释放,再关闭,避免内存泄漏和资源占用。

四、完整示例程序拆解(查询 employees 表)

结合我们之前创建的employees员工表,把文档中的示例程序逐核心步骤拆解,对应上面的流程,让大家把代码和知识点对应起来,理解每一步的作用:

cpp 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <mysql/mysql.h>

int main() {
    // 步骤1:初始化MySQL库
    if (mysql_library_init(0, NULL, NULL)) {
        fprintf(stderr, "MySQL库初始化失败\n");
        exit(1);
    }

    // 步骤2:初始化MYSQL对象
    MYSQL* mysql = mysql_init(NULL);
    if (mysql == NULL) {
        fprintf(stderr, "MYSQL对象初始化失败\n");
        exit(1);
    }

    // 步骤3:建立连接(本地root,密码1234,数据库test)
    if (mysql_real_connect(mysql, NULL, "root", "1234", "test", 0, NULL, 0) == NULL) {
        fprintf(stderr, "连接失败:%s\n", mysql_error(mysql));
        exit(1);
    }

    // 步骤4:执行SQL(查询employees表所有记录)
    char sql[] = "SELECT * FROM employees";
    if (mysql_query(mysql, sql)) {
        fprintf(stderr, "执行SQL失败:%s\n", mysql_error(mysql));
        exit(1);
    }

    // 步骤5:处理查询结果------提取结果集
    MYSQL_RES* result = mysql_store_result(mysql);
    if (result == NULL) {
        if (mysql_errno(mysql)) { // 区分失败和无结果
            fprintf(stderr, "提取结果集失败:%s\n", mysql_error(mysql));
            exit(1);
        } else {
            printf("查询结果为空\n");
            mysql_close(mysql);
            mysql_library_end();
            return 0;
        }
    }

    // 处理查询结果------获取行/列数并打印
    uint64_t rows = mysql_num_rows(result);
    unsigned fields = mysql_num_fields(result);
    printf("总记录数:%lu\n", rows);

    // 处理查询结果------遍历结果集
    MYSQL_ROW row;
    while ((row = mysql_fetch_row(result))) {
        for (unsigned i = 0; i < fields; i++) {
            printf("%s\t", row[i]);
        }
        printf("\n");
    }

    // 步骤6:释放结果集
    mysql_free_result(result);

    // 步骤7:关闭连接
    mysql_close(mysql);

    // 步骤8:终止MySQL库
    mysql_library_end();

    return 0;
}

编译执行

cpp 复制代码
gcc test_mysql.c -o test_mysql -lmysqlclient
./test_mysql

就能在终端打印出employees表的所有记录。

五、高频易错点总结(避坑必看)

  1. mysql_library_init()必须在创建线程前调用,线程不安全;
  2. SQL 语句不能以;或 \g 结尾,和 MySQL 命令行的区别;
  3. mysql_store_result()返回 NULL 时,一定要判错,区分「无结果」和「失败」;
  4. 结果集使用后必须调用mysql_free_result(),否则内存泄漏;
  5. 连接失败 / 执行 SQL 失败时,必须用mysql_errno()/mysql_error()查原因,不要只打印简单提示;
  6. 二进制数据的 SQL 必须用mysql_real_query() ,不能用mysql_query()
  7. mysql_close()会自动释放mysql_init(NULL)创建的 MYSQL 对象,无需手动 free;
  8. 整个程序中,mysql_library_init()mysql_library_end()只需要执行一次
相关推荐
Apple_羊先森2 小时前
ORACLE数据库巡检SQL脚本--23、检查Oracle数据库中被锁定的数据库对象
数据库·sql·oracle
时光书签2 小时前
数据库服务器磁盘存储扩容
数据库
—Miss. Z—2 小时前
计算机软件资格考试—第六章 数据库基础知识
数据库
m0_738120722 小时前
sqli-labs过关解析(17- 20附带源码解析)
数据库·sql·web安全·php·ctf·安全性测试
Apple_羊先森3 小时前
ORACLE数据库巡检SQL脚本--21、正在执行的长耗时操作
数据库·sql·oracle
xiaoliuliu123453 小时前
MONyog-5.6.9-0数据库监控安装步骤详解(附MySQL连接与监控设置教程)
数据库·mysql
yzs874 小时前
PgSQL的外连接选择率计算
数据库
此生只爱蛋4 小时前
【MySQL】变量
数据库·mysql
柒.梧.4 小时前
拆解Spring核心:IOC与AOP底层原理
数据库