简介:这份资源面向在 Linux 环境下做 C/C++ 开发、需要连接 MySQL 数据库的开发者,尤其适合不想额外安装 mysql-devel 或 MySQL 客户端与服务端、希望开箱即用的场景。压缩包内同时提供 32 位与 64 位两套头文件和库文件,共 77 个文件,以 68 个 .h 头文件、8 个 .a 静态库和 1 个 .so 动态库为主,整体约 16.49MB,解压后可按系统位数选择对应目录,将头文件与库文件复制到工程同级目录即可参与编译链接。资源已积累 1144 人学习,说明其在嵌入式与后端开发群体中有一定实用价值。读者可直接获得 mysqlclient 链接所需的声明与二进制库,省去查找依赖、配置 include 与 lib 路径的麻烦,配合编译参数即可完成本地构建,适合快速验证数据库连接代码或搭建轻量开发环境。
1. 编译报错先别急着改代码:Linux 下连 MySQL 的头文件和库文件到底缺在哪
上周帮一位同事看一个 C 项目,make一跑就红了一片,满屏fatal error: mysql.h: No such file or directory,他第一反应是去改#include路径,改了两小时没结果。我让他先跑一句mysql_config --cflags --libs,输出直接是command not found——问题根本不在代码,在于这台机器上只装了 MySQL 服务端,没装开发用的头文件和库文件。这个场景太常见了:Linux 下用 C/C++ 连 MySQL,编译期要mysql.h这类头文件,链接期要libmysqlclient.so这类库文件,两者缺一不可,而且 32 位和 64 位程序要的还不是同一套。装不对,轻则编译不过,重则链接期报undefined reference,或者程序跑起来直接段错误。这篇就按我实际部署的顺序,把版本选择、头文件库文件安装、编译参数配置、32/64 位区分和排错一次讲透,适合刚在 Linux 上写数据库程序、正卡在编译链接这一步的开发者。
2. 装之前先对齐环境:发行版、位数和 MySQL 版本三件事
2.1 先确认你的系统位数和包管理器
动手装任何东西之前,先把这三条命令跑一遍,输出记下来,后面选包全靠它。
# 查看系统架构,x86_64 是 64 位,i686/i386 是 32 位 uname -m # 查看发行版和版本号,决定用 apt 还是 yum/dnf cat /etc/os-release # 查看已安装的 MySQL 相关包,避免重复装或版本冲突 # Debian/Ubuntu 系 dpkg -l | grep -i mysql # RHEL/CentOS 系 rpm -qa | grep -i mysqluname -m输出x86_64说明主系统是 64 位,但这不代表你只能编 64 位程序——如果装了 32 位运行库和 32 位开发包,照样能编 32 位。/etc/os-release里的ID和VERSION_ID决定你后面用apt还是dnf,别在 CentOS 上照着 Ubuntu 的教程敲apt-get,那是白费功夫。最后那条查已装包的命令很关键,很多机器上残留着旧版本 MySQL 的客户端包,和新装的头文件版本对不上,链接时就会出玄学问题。
2.2 头文件和库文件分别是什么,为什么要分开装
很多人分不清这两个东西,我用一句话说清:头文件(mysql.h、mysql_version.h等)是给编译器看的,告诉它函数长什么样、结构体怎么定义;库文件(libmysqlclient.so、libmysqlclient.a)是给链接器用的,里面是函数的真实实现。编译阶段只需要头文件,链接阶段才需要库文件,所以你会看到两种典型报错:编译期mysql.h: No such file,链接期undefined reference to 'mysql_init'。前者缺开发头文件,后者缺库文件或者链接参数没写对。
在 Linux 上,这两样东西通常打包在同一个开发包里,Debian/Ubuntu 叫libmysqlclient-dev,RHEL/CentOS 叫mysql-devel或mariadb-devel。装一个包,头文件和库文件一起到位,这是最省事的做法。但要注意,如果你要编 32 位程序,光装 64 位的开发包不够,还得装对应的 32 位版本,这个后面单独讲。
2.3 版本对照表:别让客户端库和服务端版本打架
MySQL 的客户端库和服务端有一定兼容性要求,但不必完全一致。我一般遵循这个原则:开发包的版本不低于服务端主版本,小版本尽量对齐。下面是我实际用过的几组搭配,供参考。
| 发行版 | 服务端版本 | 开发包名 | 头文件路径 | 库文件路径 |
|---|---|---|---|---|
| Ubuntu 20.04/22.04 | MySQL 8.0 | libmysqlclient-dev | /usr/include/mysql/ | /usr/lib/x86_64-linux-gnu/ |
| Debian 11/12 | MariaDB 10.x | libmariadb-dev | /usr/include/mariadb/ | /usr/lib/x86_64-linux-gnu/ |
| CentOS 7 | MySQL 5.7 | mysql-devel | /usr/include/mysql/ | /usr/lib64/mysql/ |
| Rocky/Alma 8/9 | MySQL 8.0 | mysql-devel | /usr/include/mysql/ | /usr/lib64/mysql/ |
注意 MariaDB 和 MySQL 的头文件路径不一样,#include写法也有差别:MySQL 是#include <mysql/mysql.h>,MariaDB 有时是#include <mariadb/mysql.h>。如果你在 Debian 上装了libmariadb-dev却照着 MySQL 的教程写 include,编译就会找不到头文件。这是新手最容易翻车的地方之一。
3. 安装头文件和库文件:apt 与 dnf 两条路走通
3.1 Debian/Ubuntu 系:一条命令装齐开发包
在 Ubuntu 或 Debian 上,装 MySQL 开发包就一条命令,但装之前建议先更新索引,避免拿到过期的包信息。
# 更新包索引 sudo apt-get update # 安装 MySQL 客户端开发包(含头文件和库文件) sudo apt-get install -y libmysqlclient-dev # 如果系统用的是 MariaDB,装这个 sudo apt-get install -y libmariadb-devlibmysqlclient-dev这个包会自动带上libmysqlclient20或libmysqlclient21运行库作为依赖,所以不用单独装运行库。装完之后,头文件落在/usr/include/mysql/,库文件在/usr/lib/x86_64-linux-gnu/下,文件名类似libmysqlclient.so和libmysqlclient.so.21。-y参数是自动确认,脚本化部署时必加,手动装可以不加,看个人习惯。
装完立刻验证一下,别等编译时才发现问题:
# 确认头文件存在 ls /usr/include/mysql/mysql.h # 确认库文件存在 ls /usr/lib/x86_64-linux-gnu/libmysqlclient.so* # 用 mysql_config 查看编译和链接参数(最省心的方式) mysql_config --cflags mysql_config --libsmysql_config --cflags输出的是编译参数,通常是-I/usr/include/mysql;mysql_config --libs输出链接参数,类似-L/usr/lib/x86_64-linux-gnu -lmysqlclient。这两个输出直接抄进 Makefile 就行,比手动猜路径靠谱得多。如果mysql_config命令不存在,说明开发包没装成功,回去检查上一步。
3.2 RHEL/CentOS/Rocky 系:注意 devel 包和 mysql_config 的位置
红帽系的做法略有不同,包名是mysql-devel,而且 CentOS 7 之后默认仓库里往往是 MariaDB 的替代包,装之前要看清。
# CentOS 7 用 yum,Rocky/Alma 8+ 用 dnf sudo yum install -y mysql-devel # 或者 sudo dnf install -y mysql-devel # 如果仓库里只有 MariaDB 的开发包 sudo dnf install -y mariadb-devel装完之后,头文件在/usr/include/mysql/,库文件在/usr/lib64/mysql/下。注意 64 位系统上库文件目录是lib64不是lib,这是红帽系的惯例,写 Makefile 时路径别写错。mysql_config一般在/usr/bin/mysql_config,直接调用即可。
# 验证安装 ls /usr/include/mysql/mysql.h ls /usr/lib64/mysql/libmysqlclient.so* mysql_config --cflags --libs如果mysql_config报command not found但头文件明明在,可能是包只装了头文件没装工具,检查一下mysql-devel是否完整安装,必要时rpm -ql mysql-devel看看包里到底有什么。
3.3 32 位开发包:多架构环境下怎么补
这是标题里点名的重点。很多人在 64 位机器上编 32 位程序,编译时加-m32,结果一链接就报cannot find -lmysqlclient或者skipping incompatible。原因很简单:你只装了 64 位的库,链接器找不到 32 位版本。
Debian/Ubuntu 上要先开多架构支持,再装 32 位开发包:
# 启用 i386 架构支持 sudo dpkg --add-architecture i386 sudo apt-get update # 安装 32 位 MySQL 开发包 sudo apt-get install -y libmysqlclient-dev:i386装完之后,32 位的库文件会在/usr/lib/i386-linux-gnu/下,头文件是共用的(头文件本身不分位数)。编译 32 位程序时,链接参数要指向 32 位库目录:
# 编译 32 位程序,显式指定 32 位库路径 gcc -m32 myapp.c -o myapp32 \ -I/usr/include/mysql \ -L/usr/lib/i386-linux-gnu -lmysqlclient-m32告诉编译器生成 32 位代码,-L指定库搜索路径,-lmysqlclient是链接库名(省略lib前缀和.so后缀)。如果链接器还是报错,用ldd或file检查一下生成的程序位数对不对。
红帽系补 32 位包稍微麻烦点,CentOS 7 上可以这样:
# 安装 32 位 MySQL 开发包(包名带 .i686 后缀) sudo yum install -y mysql-devel.i686装完后 32 位库在/usr/lib/mysql/下。Rocky/Alma 8+ 对 32 位支持收紧了,很多包不再提供 i686 版本,这种情况我一般建议要么换 64 位编译,要么用容器跑一个 32 位环境,别在宿主机上硬折腾。
4. 编译和链接参数:Makefile 里最容易写错的三处
4.1 用 mysql_config 生成参数,别手写路径
手写-I和-L路径最大的问题是换台机器就失效,而mysql_config是跟着开发包走的,路径永远对。我一般直接在 Makefile 里调用它:
# Makefile 片段:用 mysql_config 动态获取编译和链接参数 MYSQL_CFLAGS := $(shell mysql_config --cflags) MYSQL_LIBS := $(shell mysql_config --libs) myapp: myapp.c gcc -o myapp myapp.c $(MYSQL_CFLAGS) $(MYSQL_LIBS)$(shell ...)是 make 的内置函数,在解析 Makefile 时执行命令并把输出赋给变量。这样写的好处是,不管头文件在/usr/include/mysql还是别的地方,参数都自动正确。MYSQL_CFLAGS里通常包含-I路径,MYSQL_LIBS里包含-L路径和-lmysqlclient,顺序不能反——库参数必须放在源文件后面,否则链接器会报undefined reference,这是 GCC 链接顺序的经典坑。
4.2 32 位编译时参数怎么改
mysql_config默认输出的是当前系统主架构的参数,在 64 位系统上就是 64 位的。要编 32 位程序,不能直接用它,得手动指定 32 位路径:
# 32 位编译的 Makefile 片段 CC := gcc CFLAGS := -m32 -I/usr/include/mysql LDFLAGS := -m32 -L/usr/lib/i386-linux-gnu LIBS := -lmysqlclient myapp32: myapp.c $(CC) $(CFLAGS) -o myapp32 myapp.c $(LDFLAGS) $(LIBS)这里-m32在编译和链接两个阶段都要加,只加一处会出问题。-I指向头文件目录(头文件不分位数,共用即可),-L指向 32 位库目录。如果你的系统上 32 位库路径不是/usr/lib/i386-linux-gnu,用dpkg -L libmysqlclient-dev:i386 | grep '\.so'查一下实际位置。
4.3 运行时找不到库:LD_LIBRARY_PATH 和 ldconfig 二选一
编译链接都过了,程序一跑报error while loading shared libraries: libmysqlclient.so.21: cannot open shared object file,这是运行时动态链接器找不到库。两种解法,我一般优先用第二种。
第一种是临时指定库路径:
# 临时把库目录加入运行时搜索路径,只对当前 shell 有效 export LD_LIBRARY_PATH=/usr/lib/x86_64-linux-gnu:$LD_LIBRARY_PATH ./myapp第二种是让系统永久认识这个库路径,改/etc/ld.so.conf.d/下的配置:
# 写入库路径配置并刷新缓存 echo "/usr/lib/x86_64-linux-gnu" | sudo tee /etc/ld.so.conf.d/mysql.conf sudo ldconfig # 验证库是否被系统识别 ldconfig -p | grep mysqlclientldconfig -p会列出系统缓存里所有可用的库,能看到libmysqlclient.so.21就说明配置生效了。LD_LIBRARY_PATH适合临时调试,生产环境用ld.so.conf.d更稳妥,因为环境变量在 systemd 服务里不一定继承得到,这是很多人部署后服务起不来的隐藏原因。
5. 避坑与排查:五条血泪记录
5.1 编译报 mysql.h 找不到,但包明明装了
现象:fatal error: mysql.h: No such file or directory,dpkg -l显示开发包已安装。
原因:头文件实际在/usr/include/mysql/子目录下,而代码里写的是#include <mysql.h>,编译器默认只搜/usr/include,不会递归进子目录。
解决:要么把 include 改成#include <mysql/mysql.h>,要么在编译参数里加-I/usr/include/mysql。我一般选后者,代码不用动,换环境也兼容。
5.2 链接报 undefined reference to mysql_init
现象:编译通过,链接阶段一堆undefined reference to 'mysql_init'、mysql_real_connect。
原因:库参数没加,或者加了但位置不对。GCC 的链接顺序是从左到右,库必须放在引用它的源文件或目标文件之后。
解决:确认命令里-lmysqlclient在.c或.o文件后面。用mysql_config --libs的输出直接拼到命令末尾,别自己调顺序。
5.3 32 位编译报 skipping incompatible
现象:加了-m32之后链接报skipping incompatible /usr/lib/x86_64-linux-gnu/libmysqlclient.so when searching for -lmysqlclient。
原因:链接器只找到 64 位库,没有 32 位版本,位数不匹配直接跳过。
解决:按 3.3 节装 32 位开发包,然后用-L显式指向 32 位库目录。装完用file /usr/lib/i386-linux-gnu/libmysqlclient.so确认是 32 位 ELF。
5.4 MariaDB 和 MySQL 头文件混用导致结构体对不上
现象:编译通过,运行时段错误或查询结果乱码。
原因:系统上同时存在 MySQL 和 MariaDB 的头文件,编译器取了其中一个,链接时却用了另一个的库,结构体布局不一致。
解决:统一用一套。Debian 上如果装了libmariadb-dev,include 就写<mariadb/mysql.h>,链接也用 MariaDB 的库;要纯 MySQL 就把 MariaDB 开发包卸掉,避免路径冲突。用dpkg -S /usr/include/mysql/mysql.h查头文件属于哪个包,心里有数。
5.5 程序在开发机跑得好,部署到服务器就找不到库
现象:本地编译运行正常,拷到另一台机器上启动报cannot open shared object file。
原因:目标机器只装了 MySQL 客户端运行库,没装开发包,或者库版本号对不上(比如开发机是.so.21,目标机只有.so.20)。
解决:部署前用ldd myapp | grep mysql看依赖的具体库版本,目标机装对应版本的运行库包(Debian 是libmysqlclient21这类,不是-dev包)。版本对不上就统一版本,别指望兼容。
6. 装完怎么验证:一个最小连接程序跑通才算数
头文件在、库文件在、编译链接都过,不代表真能用。我习惯写一个最小连接程序做终验,能连上、能查出一条数据,这套环境才算装对了。下面这段代码不依赖业务逻辑,纯粹验证头文件和库文件是否配套。
/* mysql_conn_test.c - 最小连接验证程序 */ #include <stdio.h> #include <mysql/mysql.h> /* MariaDB 环境改成 <mariadb/mysql.h> */ int main(void) { MYSQL *conn = mysql_init(NULL); if (conn == NULL) { fprintf(stderr, "mysql_init failed\n"); return 1; } /* 参数依次为:句柄、主机、用户、密码、库名、端口、socket、客户端标志 */ if (mysql_real_connect(conn, "127.0.0.1", "testuser", "testpass", "testdb", 3306, NULL, 0) == NULL) { fprintf(stderr, "connect failed: %s\n", mysql_error(conn)); mysql_close(conn); return 1; } printf("connected, client version: %s\n", mysql_get_client_info()); mysql_close(conn); return 0; }编译命令按位数选一条:
# 64 位 gcc -o mysql_conn_test mysql_conn_test.c $(mysql_config --cflags) $(mysql_config --libs) # 32 位 gcc -m32 -o mysql_conn_test32 mysql_conn_test.c \ -I/usr/include/mysql -L/usr/lib/i386-linux-gnu -lmysqlclient跑起来之后,重点看三件事:第一,mysql_get_client_info()打印的客户端版本和你装的开发包版本是否一致,不一致说明链接到了别的库;第二,用ldd ./mysql_conn_test | grep mysql确认运行时依赖的库路径正确;第三,故意把密码改错,看是否返回明确的错误信息而不是直接崩溃,能正常报错说明错误处理链路也是通的。32 位程序额外用file ./mysql_conn_test32确认输出是ELF 32-bit。
我踩过一次坑:开发机上mysql_config --libs指向的是 MariaDB 的库,但代码 include 的是 MySQL 头文件,编译链接全过,一跑就段错误,查了半天才发现是两套东西混用。从那以后我每次装完都强制走一遍这个最小程序,并且用ldd核对实际链接的库文件路径,确认头文件和库文件来自同一个包才继续往下写业务代码。希望这套流程能帮你少走几个弯路。
本文还有配套的精品资源,点击获取