在C语言项目中访问MySQL,最常用也最稳定的方式是通过官方提供的MySQL C API。这套接口以动态库形式发布,头文件为mysql.h,核心结构体是MYSQL连接句柄。借助它,我们可以完成网络连接、认证、执行SQL以及处理结果集等全部操作。下面先给出基础环境准备与连接示例。

一、环境准备与编译配置
在Linux系统下,需要安装MySQL客户端开发包,通常包名是libmysqlclient-dev或者mariadb-connector-c-dev。安装后,系统中会出现/usr/include/mysql/mysql.h以及对应的动态库文件。如果没有这个开发包,编译阶段就会报找不到头文件的错误。
编译时除了包含头文件目录,还必须链接mysqlclient库。例如使用gcc编译时可以写:gcc main.c -o app $(mysql_config --cflags) $(mysql_config --libs)。mysql_config工具能自动输出正确的include和lib参数,避免手动写错路径。Windows下则需要在Visual Studio里配置包含目录和库目录,并额外链接libmysql.lib。
二、建立数据库连接
连接MySQL前,先调用mysql_init初始化一个MYSQL结构体指针,再使用mysql_real_connect填入主机、用户、密码、库名和端口。这个函数返回同样的指针表示成功,返回NULL则表示失败,此时要用mysql_error查看具体原因。下面是一段最小可运行的连接代码。
#include <mysql.h>
#include <stdio.h>
int main() {
MYSQL *conn = mysql_init(NULL);
if (conn == NULL) {
printf("mysql_init失败n");
return 1;
}
// 连接本地MySQL,用户root,密码123456,库名test,端口3306
if (mysql_real_connect(conn, "127.0.0.1", "root", "123456", "test", 3306, NULL, 0) == NULL) {
printf("连接错误: %sn", mysql_error(conn));
mysql_close(conn);
return 1;
}
printf("连接成功n");
mysql_close(conn);
return 0;
}
上面的代码演示了最基础的连接与断开。需要注意,mysql_real_connect的第七个参数是unix socket路径,在跨网络访问时传NULL即可;第八个参数是客户端标志,一般填0。如果连接的是远程服务器,要确保MySQL服务端开启了远程账号权限,并且防火墙放行了3306端口。
另外,建议在连接后立刻执行mysql_set_character_set(conn, "utf8mb4"),避免后续插入中文出现乱码。MySQL 8.0默认认证插件改为caching_sha2_password,老版本的C客户端库可能连不上,此时要么升级客户端库,要么将账号改回mysql_native_password。
三、执行增删改查操作
对于INSERT、UPDATE、DELETE这类不返回结果集的语句,直接调用mysql_query发送即可,返回0代表执行成功。如果影响行数为0,并不代表出错,只是没有匹配到记录。通过mysql_affected_rows可以拿到受影响行数。
#include <mysql.h>
#include <stdio.h>
int main() {
MYSQL *conn = mysql_init(NULL);
mysql_real_connect(conn, "127.0.0.1", "root", "123456", "test", 3306, NULL, 0);
mysql_set_character_set(conn, "utf8mb4");
const char *sql = "INSERT INTO user(name, age) VALUES('张三', 20)";
if (mysql_query(conn, sql) != 0) {
printf("插入失败: %sn", mysql_error(conn));
} else {
printf("插入成功,影响行数: %lldn", mysql_affected_rows(conn));
}
mysql_close(conn);
return 0;
}
查询操作稍微复杂一些。执行SELECT后必须调用mysql_store_result把结果集拉到本地内存,然后用mysql_fetch_row逐行获取,mysql_num_fields拿到列数。处理完结果要调用mysql_free_result释放内存,否则会造成泄漏。
#include <mysql.h>
#include <stdio.h>
int main() {
MYSQL *conn = mysql_init(NULL);
mysql_real_connect(conn, "127.0.0.1", "root", "123456", "test", 3306, NULL, 0);
mysql_query(conn, "SELECT id, name FROM user");
MYSQL_RES *res = mysql_store_result(conn);
if (res == NULL) {
printf("查询出错: %sn", mysql_error(conn));
return 1;
}
MYSQL_ROW row;
while ((row = mysql_fetch_row(res)) != NULL) {
printf("id=%s, name=%sn", row[0], row[1]);
}
mysql_free_result(res);
mysql_close(conn);
return 0;
}
对于包含用户输入的SQL,绝对不能用字符串拼接的方式,否则会产生注入风险。正确做法是使用mysql_stmt_init配合mysql_stmt_prepare和mysql_stmt_bind_param实现预处理语句,既能防注入也能提升重复执行效率。删除和更新语句的写法与插入类似,只是SQL文本不同,同样通过mysql_affected_rows确认效果。
四、错误处理与资源释放
任何MySQL C API调用失败都要通过mysql_error(conn)获取人类可读的错误描述,mysql_errno(conn)则返回数字错误码,方便日志分类。长连接程序中,如果网络抖动导致连接断开,mysql_ping可以检测并重连。
所有打开的资源——连接句柄、结果集、预处理语句——都必须成对释放。忘记mysql_free_result或mysql_stmt_close会在长时间运行的服务里累积内存占用。写工具函数时建议统一封装连接、执行和关闭逻辑,业务代码只关心SQL与绑定参数,这样既安全又易维护。
| 操作类型 | 核心函数 | 是否需要释放结果 |
|---|---|---|
| 连接 | mysql_real_connect | 否,但需mysql_close |
| 写操作 | mysql_query | 否 |
| 读操作 | mysql_store_result | 是,mysql_free_result |
| 预处理 | mysql_stmt_prepare | 是,mysql_stmt_close |
掌握了上述连接、执行与释放的基本模式,C语言操作MySQL就不再神秘。实际工程中建议进一步封装连接池、日志与重试机制,让数据库层更健壮。
C语言MySQL连接MySQL_C_API修改时间:2026-08-01 20:54:30