摘要

在现代应用开发中,数据库交互是核心环节。MySQL 作为一款流行的开源关系型数据库,C/C++ 语言提供了强大的库来实现与其的高效连接与数据操作。本文将从环境搭建入手,详细介绍如何在 C 和 C++ 中链接 MySQL,并通过代码示例演示查询、插入、更新和删除等常见操作。同时,本文也将强调安全性与最佳实践,帮助开发者构建健壮的数据库应用。


一、准备工作:安装与配置

在编写代码前,需要确保你的系统已安装 MySQL 服务器和相应的 C/C++ 开发库。

1.1 安装 MySQL 服务器

Windows
  1. 访问 MySQL 官方网站 下载 MySQL Installer
  2. 运行安装程序,推荐选择 Developer Default 或 Server Only
  3. 按提示完成安装,并设置 root 用户密码。
  4. 建议将 MySQL 服务设置为 Automatic(开机自启)。
macOS (使用 Homebrew)

bash

# 安装 Homebrew (如果未安装)
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"

# 安装 MySQL
brew install mysql

# 启动 MySQL 服务
brew services start mysql

# 运行安全脚本进行初始化
mysql_secure_installation
Linux (以 Ubuntu/Debian 为例)

bash

# 更新软件包列表
sudo apt update

# 安装 MySQL 服务器
sudo apt install mysql-server

# 启动并设置开机自启
sudo systemctl start mysql
sudo systemctl enable mysql

# 运行安全脚本进行初始化
sudo mysql_secure_installation

验证安装:在终端或命令提示符中输入 mysql --version,若能看到版本信息则表示安装成功。

1.2 安装 C/C++ 开发库

这是编译连接 MySQL 代码的关键。

C 语言开发库 (libmysqlclient)
  • Ubuntu/Debian:

    bash

    sudo apt-get install libmysqlclient-dev
    
  • CentOS/RHEL:

    bash

    sudo yum install mysql-devel
    
  • macOS (Homebrew): brew install mysql 命令已包含开发库。
  • Windows: 在安装 MySQL Server 时,勾选 "C Connector" 即可。
C++ 开发库 (MySQL Connector/C++)

C++ 的连接器需要单独安装。

  • Ubuntu/Debian:

    bash

    sudo apt-get install libmysqlcppconn-dev
    
  • macOS (Homebrew):

    bash

    brew install mysql-connector-c++
    
  • Windows: 从 MySQL 官网 下载预编译包,并在项目中配置头文件和库文件路径。

1.3 解决密码验证插件问题 (重要)

新版 MySQL 默认使用 caching_sha2_password 插件,这可能导致部分旧版 C/C++ 客户端连接失败。推荐为开发用户设置兼容性更好的 mysql_native_password

sql

-- 登录 MySQL
mysql -u root -p

-- 修改用户的密码验证插件
ALTER USER 'your_username'@'localhost' IDENTIFIED WITH mysql_native_password BY 'your_password';
FLUSH PRIVILEGES;

请将 your_username 和 your_password 替换为你的实际信息。


二、使用 C 语言链接 MySQL 数据库

C 语言通过 libmysqlclient 库与 MySQL 交互,这是最基础、性能最高的方式。

2.1 核心步骤

  1. 引入头文件#include <mysql/mysql.h>
  2. 初始化连接句柄mysql_init()
  3. 建立连接mysql_real_connect()
  4. 执行 SQL 语句mysql_query()
  5. 处理结果集mysql_store_result() / mysql_fetch_row()
  6. 释放资源mysql_free_result()
  7. 关闭连接mysql_close()

2.2 完整代码示例 (main.c)

c

#include <mysql/mysql.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// 数据库配置信息,请替换为你自己的
#define DB_HOST "localhost"
#define DB_USER "your_username"
#define DB_PASS "your_password"
#define DB_NAME "your_database"

void print_error_and_exit(MYSQL *conn, const char *message) {
    fprintf(stderr, "%s: %s\n", message, mysql_error(conn));
    mysql_close(conn);
    exit(EXIT_FAILURE);
}

int main() {
    MYSQL *conn;
    MYSQL_RES *result;
    MYSQL_ROW row;

    // 1. 初始化 MySQL 连接对象
    conn = mysql_init(NULL);
    if (conn == NULL) {
        fprintf(stderr, "mysql_init() failed\n");
        return EXIT_FAILURE;
    }

    // 2. 建立与 MySQL 数据库的连接
    if (mysql_real_connect(conn, DB_HOST, DB_USER, DB_PASS, DB_NAME, 0, NULL, 0) == NULL) {
        print_error_and_exit(conn, "mysql_real_connect() failed");
    }
    printf("C: 成功连接到 MySQL 数据库!\n");

    // 3. 执行查询操作 (SELECT)
    const char *select_query = "SELECT * FROM your_table";
    if (mysql_query(conn, select_query)) {
        print_error_and_exit(conn, "SELECT query failed");
    }
    result = mysql_store_result(conn);
    if (result == NULL) {
        print_error_and_exit(conn, "mysql_store_result() failed");
    }

    int num_fields = mysql_num_fields(result);
    printf("\n--- 查询结果 ---\n");
    while ((row = mysql_fetch_row(result))) {
        for (int i = 0; i < num_fields; i++) {
            printf("%s\t", row[i] ? row[i] : "NULL");
        }
        printf("\n");
    }
    mysql_free_result(result); // 释放结果集

    // 4. 执行插入操作 (INSERT)
    char insert_query[256];
    const char *value1 = "data1";
    const char *value2 = "data2";
    snprintf(insert_query, sizeof(insert_query),
             "INSERT INTO your_table (column1, column2) VALUES ('%s', '%s')", value1, value2);
    if (mysql_query(conn, insert_query)) {
        print_error_and_exit(conn, "INSERT query failed");
    }
    printf("\nC: 成功插入 %lu 行数据。\n", (unsigned long)mysql_affected_rows(conn));

    // 5. 关闭连接
    mysql_close(conn);
    printf("\nC: 数据库连接已关闭。\n");

    return EXIT_SUCCESS;
}

注意: 上述代码中的 your_tablecolumn1column2 等需要替换为你数据库中的实际表名和列名。

2.3 编译与运行

打开终端,使用以下命令编译 C 代码:

bash

gcc main.c -o main_c $(mysql_config --cflags --libs)

运行程序:

bash

./main_c

三、使用 C++ 语言链接 MySQL 数据库

C++ 推荐使用官方的 MySQL Connector/C++,它提供了面向对象的 API,更符合 C++ 的编程习惯,并且通过预处理语句能有效防止 SQL 注入。

3.1 核心步骤

  1. 引入头文件#include <cppconn/...>
  2. 获取驱动实例sql::mysql::get_driver_instance()
  3. 建立连接driver->connect()
  4. 创建并执行语句createStatement() 或 prepareStatement()
  5. 处理结果集ResultSet
  6. 释放资源: 使用 delete (或智能指针)
  7. 异常处理: 使用 try...catch 捕获 sql::SQLException

3.2 完整代码示例 (main.cpp)

cpp

#include <cppconn/driver.h>
#include <cppconn/exception.h>
#include <cppconn/prepared_statement.h>
#include <cppconn/resultset.h>
#include <cppconn/statement.h>
#include <iostream>

// 数据库配置信息,请替换为你自己的
const std::string DB_HOST = "tcp://127.0.0.1:3306";
const std::string DB_USER = "your_username";
const std::string DB_PASS = "your_password";
const std::string DB_NAME = "your_database";

int main() {
    sql::Driver *driver;
    sql::Connection *con;
    sql::Statement *stmt;
    sql::ResultSet *res;
    sql::PreparedStatement *prep_stmt;

    try {
        // 1. 获取 MySQL 驱动实例
        driver = sql::mysql::get_driver_instance();

        // 2. 建立连接
        con = driver->connect(DB_HOST, DB_USER, DB_PASS);
        con->setSchema(DB_NAME);
        std::cout << "C++: 成功连接到 MySQL 数据库!" << std::endl;

        // 3. 执行查询操作 (SELECT)
        stmt = con->createStatement();
        res = stmt->executeQuery("SELECT * FROM your_table");
        std::cout << "\n--- 查询结果 ---" << std::endl;
        while (res->next()) {
            // 假设表中有两列,可以使用列名或索引访问
            std::cout << "ID: " << res->getInt("id")
                      << ", Column1: " << res->getString("column1")
                      << ", Column2: " << res->getString("column2") << std::endl;
        }
        delete res;
        delete stmt;

        // 4. 执行插入操作 (INSERT) 使用预处理语句,安全!
        prep_stmt = con->prepareStatement("INSERT INTO your_table (column1, column2) VALUES (?, ?)");
        prep_stmt->setString(1, "cpp_value1");
        prep_stmt->setString(2, "cpp_value2");
        prep_stmt->executeUpdate();
        std::cout << "\nC++: 成功插入数据。" << std::endl;
        delete prep_stmt;

    } catch (sql::SQLException &e) {
        std::cerr << "\n--- SQL Exception ---" << std::endl;
        std::cerr << "Message: " << e.what() << std::endl;
        std::cerr << "SQLState: " << e.getSQLState() << std::endl;
        std::cerr << "ErrorCode: " << e.getErrorCode() << std::endl;
        return EXIT_FAILURE;
    }

    // 5. 关闭连接并释放资源
    delete con;
    std::cout << "\nC++: 数据库连接已关闭。" << std::endl;

    return EXIT_SUCCESS;
}

注意: 同样,请替换代码中的表名和列名。

3.3 编译与运行

使用以下命令编译 C++ 代码:

bash

g++ main.cpp -o main_cpp -lmysqlcppconn

运行程序:

bash

./main_cpp

四、对比与最佳实践

特性 C (libmysqlclient) C++ (Connector/C++) 推荐场景
API 风格 过程式 (Procedural) 面向对象 (Object-Oriented) C++ 项目首选 C++ API。
易用性 较低,需手动管理资源。 较高,接口更清晰,RAII 风格。 追求开发效率和代码可读性选 C++。
安全性 需手动拼接 SQL,易出错,有注入风险。 内置 PreparedStatement,天然防 SQL 注入。 强烈推荐使用预处理语句!
性能 极致性能,开销最小。 性能优秀,略高于 C API 开销,但可忽略。 对性能要求极致的底层应用可选 C。
跨平台 优秀 优秀 两者均可。

核心最佳实践

  1. 始终使用预处理语句 (Prepared Statements):这是防止 SQL 注入攻击最有效的方法,C++ 的 Connector/C++ 对此提供了原生支持。
  2. 完善的错误处理:数据库操作随时可能出错(网络问题、SQL 语法错误等)。C 中检查每个函数的返回值,C++ 中使用 try...catch 捕获异常。
  3. 资源管理:确保所有分配的资源(连接、结果集、语句对象)都被正确释放,避免内存泄漏。
  4. 配置分离:不要将数据库密码等敏感信息硬编码在代码中,应使用配置文件或环境变量。

希望这篇文章能帮助你顺利开启 C/C++ 与 MySQL 数据库的交互之旅!

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐