🦄 个人主页 : 小米里的大麦-CSDN博客
🎏 所属专栏 : MySQL_小米里的大麦的博客-CSDN博客
🎁 GitHub主页 : 小米里的大麦的 GitHub

文章目录
- [C/C++ 操作 MySQL](#C/C++ 操作 MySQL)
-
- [1. 准备工作](#1. 准备工作)
-
- [1. 环境准备](#1. 环境准备)
- [2. C 和 C++ 接口初识](#2. C 和 C++ 接口初识)
- [2. 在 Linux 中用 C/C++操作 MySQL](#2. 在 Linux 中用 C/C++操作 MySQL)
-
- [1. 准备工作](#1. 准备工作)
- [2. C 代码操作 MySQL](#2. C 代码操作 MySQL)
- [3. C++ 操作 MySQL](#3. C++ 操作 MySQL)
- [3. Windows 中使用 C/C++ 操作 MySQL](#3. Windows 中使用 C/C++ 操作 MySQL)
-
- [1. 准备工作:确保 Linux 上的 MySQL 允许远程连接](#1. 准备工作:确保 Linux 上的 MySQL 允许远程连接)
- [2. 在 Windows 上安装 MySQL](#2. 在 Windows 上安装 MySQL)
-
- [1. 验证 MySQL Server 是否已包含 C API 库](#1. 验证 MySQL Server 是否已包含 C API 库)
- [2. 如果缺少开发组件怎么办?](#2. 如果缺少开发组件怎么办?)
- [3. 在 Visual Studio 中配置 C/C++项目](#3. 在 Visual Studio 中配置 C/C++项目)
-
- [1. 创建项目并配置属性](#1. 创建项目并配置属性)
- [2. 放置运行时 DLL](#2. 放置运行时 DLL)
- [3. 测试代码](#3. 测试代码)
- [4. 在 VS Code 中配置 C/C++项目(使用 CMake)](#4. 在 VS Code 中配置 C/C++项目(使用 CMake))
-
- [1. 安装必要插件](#1. 安装必要插件)
- [2. 项目结构示例](#2. 项目结构示例)
- [3. 编写 CMakeLists.txt](#3. 编写 CMakeLists.txt)
- [4. 配置 CMake Tools](#4. 配置 CMake Tools)
- [5. 关于 libmysql.dll](#5. 关于 libmysql.dll)
- [4. 关于 MySQL Connector/C++ 的讨论](#4. 关于 MySQL Connector/C++ 的讨论)
- 共勉
C/C++ 操作 MySQL
1. 准备工作
1. 环境准备
安装 MySQL 的 C 语言客户端开发库(包含头文件和链接库):
bash
sudo apt update
sudo apt install libmysqlclient-dev
另一个命令(可选):
bash
sudo apt install -y build-essential pkg-config default-libmysqlclient-dev
这条命令会安装:
- build-essential:包含 gcc、g++、make 等编译工具链。
- pkg-config:用于管理库的编译和链接参数,方便 Makefile 或构建系统自动获取头文件路径和库名称。
- default-libmysqlclient-dev :这是一个"元包",在 Ubuntu 上通常依赖于具体的 MySQL 客户端开发库(如
libmysqlclient-dev),相当于自动选择并安装当前发行版推荐的 MySQL 客户端库。
如果希望在 C++ 中使用更"面向对象"的接口,可以考虑安装 MySQL Connector/C++:
bash
sudo apt install libmysqlcppconn-dev # Ubuntu 某些版本可能不包,需从源码或第三方源安装
2. C 和 C++ 接口初识
-
MySQL C API :这是 MySQL 官方提供的原生 C 语言接口,头文件为
<mysql/mysql.h>,所有函数均以 C 语言风格导出(如mysql_init()、mysql_query()等)。C++ 程序完全可以直接包含此头文件并调用这些函数,因为 C++兼容 C 语言。因此,如果使用 C API,那么头文件和函数接口在 C 和 C++中是完全相同的。 -
MySQL Connector/C++ :这是 MySQL 官方提供的面向 C++ 的专用驱动,使用面向对象的风格(如
sql::Connection、sql::Statement等),需要安装额外的开发库(如libmysqlcppconn-dev)。它与 C API 不同,头文件也不一样(如<mysql_connection.h>)。这类接口是 C++特有的,C 语言无法直接使用。
2. 在 Linux 中用 C/C++操作 MySQL
1. 准备工作
先在 MySQL 中创建对应的用户并赋予权限等信息:
sql
# 创建用户:仅允许本机登录,密码为 123456
mysql> create user 'local_minbit'@'localhost' identified by '123456';
Query OK, 0 rows affected (0.02 sec)
# 创建数据库:若不存在则创建。注意库名含横杠,必须用反引号 `` 包裹
mysql> create database if not exists `local-C-test`;
Query OK, 1 row affected (0.01 sec)
# 授权业务库:给该用户在 `local-C-test` 库下的所有权限
mysql> grant all privileges on `local-C-test`.* to 'local_minbit'@'localhost';
Query OK, 0 rows affected (0.01 sec)
# 授权系统表:允许该用户查看 mysql.user 表(用于查询用户列表)
mysql> grant select on mysql.user to 'local_minbit'@'localhost';
Query OK, 0 rows affected (0.02 sec)
# 切换数据库
mysql> use local-C-test;
mysql> CREATE TABLE IF NOT EXISTS student_score (
-> id INT AUTO_INCREMENT PRIMARY KEY, # 自增主键
-> name VARCHAR(100) NOT NULL, # 姓名,非空
-> subject VARCHAR(100) NOT NULL, # 科目,非空
-> score DECIMAL(5,2) NOT NULL # 分数,定点数(共5位,小数2位),防精度丢失
-> );
2. C 代码操作 MySQL
cpp
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <mysql/mysql.h> // MySQL C API 头文件
// 数据库连接参数宏定义,方便后续修改
#define DB_HOST "localhost" // 数据库地址,localhost表示本机
#define DB_USER "local_minbit" // 数据库用户名
#define DB_PASS "123456" // 数据库密码
#define DB_NAME "local-C-test" // 数据库名称
// 错误处理封装函数,当发生数据库错误时,打印错误信息并安全退出
void finish_with_error(MYSQL *con)
{
// mysql_error(con): 返回最近一次调用MySQL函数失败的错误描述字符串
fprintf(stderr, "%s\n", mysql_error(con));
// mysql_close(con): 关闭数据库连接,释放由con指向的结构体
// 注意:即使连接中间出错了,也需要调用此函数防止内存泄漏
mysql_close(con);
exit(1);
}
int main()
{
// 1. 初始化 MySQL 对象
// mysql_init(NULL): 初始化一个MYSQL结构体,分配并初始化内存
// 参数:NULL表示由库自动分配内存
// 返回值:成功返回新分配的句柄指针,失败返回NULL
MYSQL *con = mysql_init(NULL);
if (con == NULL)
{
fprintf(stderr, "mysql_init() failed\n");
exit(1);
}
// 2. 连接数据库
// mysql_real_connect(): 建立与MySQL服务器的物理连接
// 参数说明:mysql_init返回的句柄, 主机名或IP地址, 用户名, 密码, 数据库名, 端口(0表示默认), socket文件名, 客户端标志
// 注意:要连接的数据库名如果为NULL,连接后需手动选择库;端口号:0表示使用默认端口3306;
// Unix Socket文件名或命名管道名,通常设为NULL让系统自动选择;客户端标志位通常设为0
// 返回值:成功返回con指针,失败返回NULL
if (mysql_real_connect(con, DB_HOST, DB_USER, DB_PASS, DB_NAME, 0, NULL, 0) == NULL)
{
finish_with_error(con);
}
// 3. 执行插入操作
// mysql_query(): 执行由字符串表示的SQL语句
// 参数:连接句柄,SQL语句字符串
// 返回值:成功返回0,失败返回非0
// 注意:该函数会自动检测语句是否需要分号结尾(通常不需要)
if (mysql_query(con, "INSERT INTO student_score (name, subject, score) VALUES ('张三', '数学', 88.5), ('李四', '英语', 92.0)"))
{
finish_with_error(con);
}
// mysql_affected_rows(): 获取最近一次INSERT/UPDATE/DELETE操作影响的行数
// 返回值:my_ulonglong类型(64位无符号整数),如果没有匹配行则返回0
printf("插入成功,影响行数: %lu\n", (unsigned long)mysql_affected_rows(con));
// 4. 查询操作
if (mysql_query(con, "SELECT id, name, subject, score FROM student_score"))
{
finish_with_error(con);
}
// mysql_store_result(): 将查询的结果从服务器一次性取回到客户端,结果存储在内存中,必须在使用完后手动释放!
// 返回值:成功返回MYSQL_RES结构体指针,失败或无结果集返回NULL
MYSQL_RES *result = mysql_store_result(con);
if (result == NULL)
{
finish_with_error(con);
}
// mysql_num_fields(): 获取结果中字段(列)的数量
// 这对于遍历打印数据至关重要,因为我们不知道查回来有几列!
int num_fields = mysql_num_fields(result);
MYSQL_ROW row; // MYSQL_ROW 实际上是 char** 类型,代表一行数据的字符串数组
printf("\n当前成绩表数据:\n");
// mysql_fetch_row(): 从结果中获取下一行数据,工作原理类似于迭代器,每次调用自动移动到下一行
// 返回值:成功返回MYSQL_ROW(字符串数组),如果没有更多行则返回NULL
while ((row = mysql_fetch_row(result)))
{
for (int i = 0; i < num_fields; i++)
{
// row[i] 访问当前行的第 i 列数据
// 注意:如果数据库中该字段值为 NULL,row[i] 会是 NULL指针!
// 这里使用三元运算符防止空指针导致的段错误,如果是NULL则打印 "NULL"
printf("%s ", row[i] ? row[i] : "NULL");
}
printf("\n");
}
// mysql_free_result(): 释放结果集占用的内存
// 防止内存泄漏,非常重要。
mysql_free_result(result);
// 5. 更新操作:将张三的数学成绩改为95.0
if (mysql_query(con, "UPDATE student_score SET score = 95.0 WHERE name = '张三' AND subject = '数学'"))
{
finish_with_error(con);
}
printf("\n更新成功,影响行数: %lu\n", (unsigned long)mysql_affected_rows(con));
// 6. 删除操作:删除李四的英语记录
if (mysql_query(con, "DELETE FROM student_score WHERE name = '李四' AND subject = '英语'"))
{
finish_with_error(con);
}
printf("删除成功,影响行数: %lu\n", (unsigned long)mysql_affected_rows(con));
// 7. 再次查询,验证结果
if (mysql_query(con, "SELECT id, name, subject, score FROM student_score"))
{
finish_with_error(con);
}
result = mysql_store_result(con);
if (result == NULL)
{
finish_with_error(con);
}
printf("\n操作后的数据:\n");
// 复用之前的遍历逻辑
while ((row = mysql_fetch_row(result)))
{
for (int i = 0; i < num_fields; i++)
{
printf("%s ", row[i] ? row[i] : "NULL");
}
printf("\n");
}
mysql_free_result(result);
// 8. 关闭连接,mysql_close(): 断开与服务器的连接,并释放连接句柄占用的内存
// 这是最后一步清理工作!
mysql_close(con);
return 0;
}
makefile 文件:
makefile
C_conn_mysql: C_conn_mysql.c
gcc -o $@ $^ -I/usr/include/mysql -L/usr/lib/mysql -lmysqlclient
.PHONY: clean
clean:
rm -f C_conn_mysql
-I:用于指明头文件的搜索路径。-L:用于指明库文件的搜索路径。-l:用于指明需要连接库文件路径下的哪一个库。
输出:
text
插入成功,影响行数: 2
当前成绩表数据:
1 张三 数学 88.50
2 李四 英语 92.00
更新成功,影响行数: 1
删除成功,影响行数: 1
操作后的数据:
1 张三 数学 95.00
3. C++ 操作 MySQL
cpp
#include <iostream>
#include <memory>
#include <mysql/mysql.h> // MySQL C API 头文件,C++ 可以直接调用 C 接口
class StudentDB
{
public:
StudentDB(const std::string &host, const std::string &user,
const std::string &pass, const std::string &db)
{
// 1. 初始化连接句柄
// mysql_init(nullptr): 参数为空表示让库自动分配 MYSQL 结构体内存
conn = mysql_init(nullptr);
if (conn == nullptr)
{
std::cerr << "mysql_init() 失败!" << std::endl;
exit(1);
}
// 2. 建立连接
// 参数说明:mysql_init返回的句柄, 主机名或IP地址, 用户名, 密码, 数据库名, 端口(0表示默认), socket文件名, 客户端标志
if (mysql_real_connect(conn, host.c_str(), user.c_str(),
pass.c_str(), db.c_str(), 0, nullptr, 0) == nullptr)
{
finish_with_error(); // 连接失败,打印错误并退出
}
}
~StudentDB()
{
if (conn)
{
mysql_close(conn); // 关闭连接并释放句柄内存
}
}
// 插入操作
bool insert(const std::string &name, const std::string &subject, double score)
{
char query[256]; // 缓冲区,用于存储拼接好的 SQL 语句
// snprintf: 安全的格式化字符串函数,防止缓冲区溢出
// 注意:实际更推荐使用 mysql_real_escape_string 处理特殊字符防止 SQL 注入
snprintf(query, sizeof(query),
"INSERT INTO student_score (name, subject, score) VALUES ('%s', '%s', %.2f)",
name.c_str(), subject.c_str(), score);
// mysql_query: 执行 SQL 语句
// 返回值:非 0 表示执行失败
if (mysql_query(conn, query))
{
std::cerr << "INSERT失败: " << mysql_error(conn) << std::endl;
return false;
}
// mysql_affected_rows: 获取受影响的行数,通常用于验证插入是否真的发生
std::cout << "插入成功,影响行数: " << mysql_affected_rows(conn) << std::endl;
return true;
}
// 查询并打印所有记录:流程:执行 SQL -> 获取结果集 -> 遍历行 -> 释放结果集
void queryAll()
{
// 执行查询语句
if (mysql_query(conn, "SELECT id, name, subject, score FROM student_score"))
{
std::cerr << "SELECT失败: " << mysql_error(conn) << std::endl;
return;
}
// mysql_store_result: 将查询结果从服务器全部取回并存储在本地内存
// 返回值:MYSQL_RES 指针,失败返回 NULL
MYSQL_RES *result = mysql_store_result(conn);
if (result == nullptr)
{
std::cerr << "mysql_store_result()失败: " << mysql_error(conn) << std::endl;
return;
}
// mysql_num_fields: 获取结果集中的列数(字段数量)
int num_fields = mysql_num_fields(result);
MYSQL_ROW row; // MYSQL_ROW 实际上是 char** 类型,代表一行数据
std::cout << "-----------------------------------" << std::endl;
// mysql_fetch_row: 从结果集中逐行获取数据
// 返回值:下一行的数据,如果没有更多行则返回 NULL
while ((row = mysql_fetch_row(result)))
{
for (int i = 0; i < num_fields; i++)
{
// row[i]: 访问当前行的第 i 列
// 注意:如果数据库字段值为 NULL,row[i] 指针也是 NULL
// 这里使用三元运算符进行 NULL 指针保护
std::cout << (row[i] ? row[i] : "NULL") << "\t";
}
std::cout << std::endl;
}
std::cout << "-----------------------------------" << std::endl;
// mysql_free_result: 释放 result 占用的内存
// 非常重要:每次查询后必须释放,否则会造成严重的内存泄漏
mysql_free_result(result);
}
// 更新数据
bool update(const std::string &name, const std::string &subject, double newScore)
{
char query[256];
snprintf(query, sizeof(query),
"UPDATE student_score SET score = %.2f WHERE name = '%s' AND subject = '%s'",
newScore, name.c_str(), subject.c_str());
if (mysql_query(conn, query))
{
std::cerr << "UPDATE失败: " << mysql_error(conn) << std::endl;
return false;
}
std::cout << "更新成功,影响行数: " << mysql_affected_rows(conn) << std::endl;
return true;
}
// 删除数据
bool remove(const std::string &name, const std::string &subject)
{
char query[256];
snprintf(query, sizeof(query),
"DELETE FROM student_score WHERE name = '%s' AND subject = '%s'",
name.c_str(), subject.c_str());
if (mysql_query(conn, query))
{
std::cerr << "DELETE失败: " << mysql_error(conn) << std::endl;
return false;
}
std::cout << "删除成功,影响行数: " << mysql_affected_rows(conn) << std::endl;
return true;
}
private:
MYSQL *conn; // MySQL 连接句柄指针,操作数据库的核心对象
// 内部错误处理函数,打印错误信息、关闭连接并退出程序
// 注意:在构造函数失败时调用,因为对象可能未完全构造,无法依赖析构函数
void finish_with_error()
{
std::cerr << mysql_error(conn) << std::endl;
mysql_close(conn); // 发生错误时手动关闭连接,防止资源泄漏
exit(1);
}
};
int main()
{
// 1. 实例化数据库对象
// 构造函数会自动执行 mysql_init 和 mysql_real_connect
StudentDB db("localhost", "local_minbit", "123456", "local-C-test");
// 2. 插入测试数据
db.insert("张三", "数学", 88.5);
db.insert("李四", "英语", 92.0);
std::cout << "\n初始数据:" << std::endl;
db.queryAll();
// 3. 更新数据:将张三数学成绩修改为 95.0
db.update("张三", "数学", 95.0);
// 4. 删除数据:删除李四的英语成绩
db.remove("李四", "英语");
std::cout << "\n操作后数据:" << std::endl;
db.queryAll();
// 5. 程序结束
// main 函数结束时,局部变量 db 会被销毁,自动调用析构函数 ~StudentDB()
// 析构函数会执行 mysql_close(conn),确保连接安全关闭
return 0;
}
运行输出结果:
text
插入成功,影响行数: 1
插入成功,影响行数: 1
初始数据:
-----------------------------------
1 张三 数学 95.00
3 张三 数学 88.50
4 李四 英语 92.00
-----------------------------------
更新成功,影响行数: 1
删除成功,影响行数: 1
操作后数据:
-----------------------------------
1 张三 数学 95.00
3 张三 数学 95.00
-----------------------------------
3. Windows 中使用 C/C++ 操作 MySQL
1. 准备工作:确保 Linux 上的 MySQL 允许远程连接
-
修改 MySQL 配置文件: 更改或新增
bind-address行为0.0.0.0bashvim /etc/mysql/mysql.conf.d/mysqld.cnfsql# # The MySQL database server configuration file. # # One can use all long options that the program supports. # Run program with --help to get a list of available options and with # --print-defaults to see which it would actually understand and use. # # For explanations see # http://dev.mysql.com/doc/mysql/en/server-system-variables.html # Here is entries for some specific programs # The following values assume you have at least 32M ram [mysqld] # # * Basic Settings # user = mysql # pid-file = /var/run/mysqld/mysqld.pid # socket = /var/run/mysqld/mysqld.sock # port = 3306 # datadir = /var/lib/mysql # If MySQL is running as a replication slave, this should be # changed. Ref https://dev.mysql.com/doc/refman/8.0/en/server-system-variables.html#sysvar_tmpdir # tmpdir = /tmp # # Instead of skip-networking the default is now to listen only on # localhost which is more compatible and is not less secure. bind-address = 127.0.0.1 mysqlx-bind-address = 127.0.0.1 # ===== 自定义扩展配置 ===== # 设置默认字符集为 utf8mb4,防止中文和 emoji 乱码 character-set-server = utf8mb4 collation-server = utf8mb4_general_ci # 启用远程访问(默认仅本机),保持仅本地访问(通过 Xshell 登录服务器再操作)可将 0.0.0.0 设置为 127.0.0.1 bind-address = 0.0.0.0 mysqlx-bind-address = 0.0.0.0 # 使用传统认证插件,兼容 Navicat、旧版客户端 default-authentication-plugin = mysql_native_password # 可选:增加连接稳定性 # max_connections = 200 # wait_timeout = 600 # interactive_timeout = 600 # # * Fine Tuning # key_buffer_size = 16M # max_allowed_packet = 64M # thread_stack = 256K # thread_cache_size = -1 # This replaces the startup script and checks MyISAM tables if needed # the first time they are touched myisam-recover-options = BACKUP # max_connections = 151 # table_open_cache = 4000 # # * Logging and Replication # # Both location gets rotated by the cronjob. # # Log all queries # Be aware that this log type is a performance killer. # general_log_file = /var/log/mysql/query.log # general_log = 1 # # Error log - should be very few entries. # log_error = /var/log/mysql/error.log # # Here you can see queries with especially long duration # slow_query_log = 1 # slow_query_log_file = /var/log/mysql/mysql-slow.log # long_query_time = 2 # log-queries-not-using-indexes # # The following can be used as easy to replay backup logs or for replication. # note: if you are setting up a replication slave, see README.Debian about # other settings you may need to change. # server-id = 1 # log_bin = /var/log/mysql/mysql-bin.log # binlog_expire_logs_seconds = 2592000 max_binlog_size = 100M # binlog_do_db = include_database_name # binlog_ignore_db = include_database_name然后重启 MySQL:
bashsudo systemctl restart mysql -
创建允许从 Windows IP 连接的数据库用户(或修改现有用户)
sqlmysql> create user 'win_test'@'%' identified by '123456'; Query OK, 0 rows affected (0.01 sec) mysql> grant all privileges on *.* to 'win_test'@'%'; Query OK, 0 rows affected (0.01 sec) -
检查防火墙:确保 Linux 防火墙(如 ufw)开放 MySQL 端口(默认 3306):
bashsudo ufw allow 3306/tcp
2. 在 Windows 上安装 MySQL
-
下载 :访问 MySQL Community Downloads,选择 Windows (x86, 64-bit), ZIP Archive 或 MSI Installer。
-
安装 :如果下载 ZIP,解压后将其
C:\Program Files\MySQL\MySQL Server 8.4\bin目录添加到系统 PATH;如果使用 MSI,安装过程中仅选择 "Client Programs" 即可。 -
使用:打开命令提示符,执行:
sqlmysql -h 12x.5x.16x.x -P 3306 -u win_test -p # 用自己公网 IP
在 Windows 上通过 MySQL Installer 安装 MySQL Server 时,通常就会包含 C API 客户端库(libmysql),但需要注意安装选项。
1. 验证 MySQL Server 是否已包含 C API 库
检查安装目录: 打开 MySQL 的安装路径(C:\Program Files\MySQL\MySQL Server 8.4),查看是否存在以下文件夹和文件:
include文件夹:里面应该有mysql.h、mysql_com.h等头文件。lib文件夹:里面应该有libmysql.lib(静态库/导入库)和libmysql.dll(动态链接库)。- 如果能找到路径(如
C:\Program Files\MySQL\MySQL Server 8.4\lib\libmysql.dll),也说明已安装。
如果这些文件存在,说明 C API 库已随服务器安装。若缺少,可能是安装时未选择开发组件。
2. 如果缺少开发组件怎么办?
- 重新运行 MySQL Installer,选择 "Modify" ,然后勾选 "Development Components" 或 "C Connector",安装即可。
- 或者直接下载独立的 Connector/C 并解压到任意目录,后续配置时指向该目录。
3. 在 Visual Studio 中配置 C/C++项目
假设 MySQL 安装在 C:\Program Files\MySQL\MySQL Server 8.4(或 Connector/C 解压目录)。视频可参考:【C/C++服务器开发】C 语言/C++连接 mysql | B 站。
1. 创建项目并配置属性
- 打开 Visual Studio,创建新的控制台应用(C++)。
- 点击 项目 → 属性(注意配置选择"所有配置",平台选择"x64"或"x86"匹配 MySQL 库版本)。
- 在 VC++ 目录 中:
- 包含目录 :添加
C:\Program Files\MySQL\MySQL Server 8.4\include - 库目录 :添加
C:\Program Files\MySQL\MySQL Server 8.4\lib
- 包含目录 :添加
- 在 链接器 → 输入 → 附加依赖项 中添加
libmysql.lib。
2. 放置运行时 DLL
将 libmysql.dll 从 lib 文件夹复制到可执行文件(.exe)输出目录(例如 Debug 或 Release),可以参考我的路径:D:\Coding\Visual Studio 2022\pro\x64\Debug。
3. 测试代码
cpp
#include <iostream>
#include <mysql.h> // 注意头文件名为 mysql.h
int main()
{
MYSQL* conn = mysql_init(nullptr);
if (conn == nullptr)
{
std::cerr << "mysql_init failed" << std::endl;
return 1;
}
// 尝试连接远程Linux MySQL(替换为实际IP、用户、密码)
if (mysql_real_connect(conn, "122.51.165.8", "win_test", "123456", "local-C-test", 3306, nullptr, 0) == nullptr)
{
std::cerr << "Connection error: " << mysql_error(conn) << std::endl;
mysql_close(conn);
return 1;
}
std::cout << "Connected successfully!" << std::endl;
mysql_query(conn, "SELECT VERSION()");
MYSQL_RES* res = mysql_store_result(conn);
MYSQL_ROW row = mysql_fetch_row(res);
std::cout << "MySQL version: " << row[0] << std::endl;
mysql_free_result(res);
mysql_close(conn);
return 0;
}
点击运行,会发现报错了,说找不到 xxx.dll(具体名字忘记了),再网络上找了一大圈,找到方案:将路径 C:\Program Files\MySQL\MySQL Server 8.4\lib 中的libmysql.dll和libmysql.lib一并放到D:\Coding\Visual Studio 2022\pro\x64\Debug里面。激动的心颤抖的手点击运行,再次报错:系统错误,由于找不到 libssl-3-x64.dll,无法继续执行代码。重新安装程序可能会解决此问题。真是天塌了,于是苦苦再次寻觅互联网,才找到解决办法: 解决 libssl-3-x64.dll 问题的办法、Punishing gray raven dll 下载 。说实话,我也并不清楚原因,只是将下载下来的 2 个动态共享库文件一并放入到了 .exe同级目录下(前前后后总共放了 4 个文件),再次运行成功得到输出:

我自己也不清楚,具体原因和解决原理,所以仅仅是将我的经验供大家参考! Google 搜索多次救我于水火,如果期间有其他报错,不妨试试 Google 搜索看看!
4. 在 VS Code 中配置 C/C++项目(使用 CMake)
在 VS Code 上配置就比较灵活了,方式比较多样,我们主要使用 VS Code 配合 CMake 工具链(CMake Tools 插件),因为配置比较灵活。其他方法自行 B 站/Google 了,另外,推荐文章:VSCode 环境下连接 MySQL 8.0 数据库 (C++)。
1. 安装必要插件
- C/C++ 插件(Microsoft)
- CMake Tools 插件
2. 项目结构示例
text
test/
├── .vscode/
│ ├── settings.json (可选)
│ └── tasks.json (可选)
├── CMakeLists.txt
└── main.cpp
3. 编写 CMakeLists.txt
cmake
cmake_minimum_required(VERSION 3.10)
project(MySQLTest)
# 指定MySQL头文件和库路径(根据实际安装位置修改)
set(MYSQL_INCLUDE_DIR "C:/Program Files/MySQL/MySQL Server 8.4/include")
set(MYSQL_LIB_DIR "C:/Program Files/MySQL/MySQL Server 8.4/lib")
# 添加头文件搜索路径
include_directories(${MYSQL_INCLUDE_DIR})
# 添加可执行文件
add_executable(${PROJECT_NAME} main.cpp)
# 链接MySQL库
target_link_libraries(${PROJECT_NAME} ${MYSQL_LIB_DIR}/libmysql.lib)
# 注意:运行时需要将 libmysql.dll 放在可执行文件同级目录,或加入PATH
4. 配置 CMake Tools
- 打开项目,CMake Tools 会自动检测工具链。
- 选择配置(如 Visual Studio 2022 Release - x64)。
- 构建(F7),然后运行。
如果不想使用 CMake,也可以手动编写 tasks.json 和 launch.json 配置编译命令,但 CMake 更推荐。
5. 关于 libmysql.dll
无论使用哪种方式,都需要确保 libmysql.dll 在运行时可被找到!
- 将 DLL 复制到生成的可执行文件目录(例如
build/Debug/)。 - 或将 MySQL 的
lib目录添加到系统 PATH。
4. 关于 MySQL Connector/C++ 的讨论
| 特性 | MySQL C API | MySQL Connector/C++ |
|---|---|---|
| 底层 | C 语言接口 | C++面向对象封装 |
| 性能 | 极高 | 接近 C API,略有损耗 |
| 易用性 | 较繁琐 | 简洁,支持异常 |
| 预处理语句 | 支持但较复杂 | 原生友好支持 |
| 稳定性 | 非常成熟 | 8.x 后稳定,早期有历史问题 |
| 依赖 | 仅 libmysqlclient |
需安装专用库 |
| 流行度 | 非常广泛(尤其是老项目) | 较新,逐渐增加 |
实际 C++ 后端中,业务代码一般不会直接大量使用裸的 MySQL C API,而是会在底层基于 C API 封装一层数据库访问模块(如连接管理、SQL 执行、事务、结果集解析、连接池等)。MySQL Connector/C++ 虽然能用,但使用较少;更常见的是采用基于 C API 的第三方轻量库或公司内部封装,因为它们更稳定、更通用,也更方便控制性能和部署。
所以,大多数情况,我们只需要掌握 MySQL C API 的使用就行了,进阶基本上都是使用第三方库、个人/公司封装的版本、其他更便捷的方式。当然连接方式也会多样,也可以尝试一系列的图形化工具......
共勉

