Linux C如何连接操作MySQL数据库?

99ANYc3cd6
预计阅读时长 32 分钟
位置: 首页 C语言 正文

我们将涵盖从环境准备、核心 API 使用、完整代码示例到编译和常见问题解决的全过程。

linux c语言 mysql
(图片来源网络,侵删)

目录

  1. 准备工作
    • 安装 MySQL/MariaDB 服务器
    • 安装 C 语言开发库
    • 创建测试数据库和用户
  2. 核心概念与 API
    • mysql_init() - 初始化连接句柄
    • mysql_real_connect() - 建立连接
    • mysql_query() / mysql_real_query() - 执行 SQL 语句
    • mysql_store_result() / mysql_use_result() - 获取结果集
    • mysql_fetch_row() - 遍历结果集
    • mysql_num_fields() / mysql_num_rows() - 获取结果集信息
    • mysql_free_result() - 释放结果集
    • mysql_close() - 关闭连接
  3. 完整 C 语言示例代码
    • 创建表
    • 插入数据
    • 查询数据
    • 更新数据
    • 删除数据
  4. 如何编译 C 程序
    • 使用 gcc-lmysqlclient 选项
  5. 处理错误与最佳实践
    • 检查每个 API 调用的返回值
    • 使用 mysql_error() 获取错误信息
  6. 进阶话题
    • 预处理语句 防止 SQL 注入
    • 多线程安全

准备工作

在开始编写 C 代码之前,必须确保你的 Linux 系统上安装了必要的软件。

a. 安装 MySQL/MariaDB 服务器

以 Ubuntu/Debian 为例:

# 更新软件包列表
sudo apt update
# 安装 MySQL 服务器
sudo apt install mysql-server
# 安装后,安全配置向导(建议运行)
sudo mysql_secure_installation

以 CentOS/RHEL 为例:

# 安装 MySQL 官方仓库
sudo yum localinstall https://dev.mysql.com/get/mysql80-community-release-el8-7.noarch.rpm -y
# 安装 MySQL 服务器
sudo yum install mysql-community-server -y
# 启动并设置开机自启
sudo systemctl start mysqld
sudo systemctl enable mysqld

b. 安装 C 语言开发库和 MySQL 客户端开发头文件

这个包包含了编译 C 程序所需的头文件(如 mysql.h)和库文件(如 libmysqlclient.so)。

linux c语言 mysql
(图片来源网络,侵删)

Ubuntu/Debian:

sudo apt install libmysqlclient-dev

CentOS/RHEL:

sudo yum install mysql-devel

c. 创建测试数据库和用户

为了安全,不要使用 root 用户连接数据库,我们创建一个专用的数据库和用户。

  1. 登录到 MySQL:

    linux c语言 mysql
    (图片来源网络,侵删)
    mysql -u root -p
  2. 执行以下 SQL 命令:

    -- 创建一个名为 test_db 的数据库
    CREATE DATABASE test_db;
    -- 创建一个新用户 'c_user',密码设为 'your_password',并授予其对 test_db 的所有权限
    CREATE USER 'c_user'@'localhost' IDENTIFIED BY 'your_password';
    -- 授予权限
    GRANT ALL PRIVILEGES ON test_db.* TO 'c_user'@'localhost';
    -- 刷新权限使生效
    FLUSH PRIVILEGES;
    -- 退出
    EXIT;

核心概念与 API

C 语言操作 MySQL 的基本流程是:初始化 -> 连接 -> 执行查询 -> 处理结果 -> 释放资源 -> 关闭连接

API 函数 作用 关键点
MYSQL *mysql_init(MYSQL *mysql) 初始化一个 MYSQL 句柄结构体。 在连接前必须调用,如果参数为 NULL,它会分配一个新的结构体并返回指针。
MYSQL *mysql_real_connect(...) 尝试与 MySQL 服务器建立连接。 需要传入 mysql_init() 返回的句柄,以及主机、用户、密码、数据库等信息。
int mysql_query(MYSQL *mysql, const char *stmt_str) 执行一个 SQL 语句。 语句以字符串形式传入,不能包含二进制数据,对于简单查询足够。
int mysql_real_query(...) 执行一个 SQL 语句。 stmt_length 长度的字符串形式传入,可以包含二进制数据,更安全。
MYSQL_RES *mysql_store_result(MYSQL *mysql) 立即获取并完整存储查询结果集。 对于 SELECT 查询使用,如果结果集很大,会消耗较多内存。
MYSQL_RES *mysql_use_result(MYSQL *mysql) 逐行获取查询结果集,不一次性加载到内存。 适用于非常大的结果集,可以节省内存,但会占用服务器资源,且不能在获取结果时执行其他查询。
MYSQL_ROW mysql_fetch_row(MYSQL_RES *result) 从结果集中获取下一行数据。 MYSQL_ROW 实际上是一个 char **,即字符串数组。
unsigned int mysql_num_fields(MYSQL_RES *result) 获取结果集中的列数。 用于确定 mysql_fetch_row() 返回的数组长度。
my_ulonglong mysql_num_rows(MYSQL_RES *result) 获取结果集中的行数。 只有在调用 mysql_store_result() 之后才能准确获取。
void mysql_free_result(MYSQL_RES *result) 释放由 mysql_store_result()mysql_use_result() 分配的结果集内存。 非常重要! 忘记释放会导致内存泄漏。
void mysql_close(MYSQL *mysql) 关闭与 MySQL 服务器的连接并释放 MYSQL 句柄。 程序结束时必须调用。

完整 C 语言示例代码

下面是一个完整的 main.c 文件,它演示了创建表、增删改查所有基本操作。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <mysql/mysql.h> // 包含 MySQL 头文件
// 定义数据库连接信息
#define DB_HOST "localhost"
#define DB_USER "c_user"
#define DB_PASS "your_password"  // <-- 修改成你自己的密码
#define DB_NAME "test_db"
// 打印错误信息并退出
void finish_with_error(MYSQL *con) {
    fprintf(stderr, "Error: %s\n", mysql_error(con));
    mysql_close(con);
    exit(1);
}
int main(int argc, char **argv) {
    MYSQL *con; // 定义一个 MySQL 连接句柄
    MYSQL_RES *result; // 定义一个结果集指针
    MYSQL_ROW row;     // 定义一个行指针
    // 1. 初始化连接句柄
    con = mysql_init(NULL);
    if (con == NULL) {
        fprintf(stderr, "mysql_init() failed\n");
        exit(1);
    }
    // 2. 建立连接
    if (mysql_real_connect(con, DB_HOST, DB_USER, DB_PASS, DB_NAME, 0, NULL, CLIENT_MULTI_STATEMENTS) == NULL) {
        finish_with_error(con);
    }
    printf("Successfully connected to MySQL database.\n");
    // --- 3. 执行 SQL 语句 ---
    // a. 创建表
    if (mysql_query(con, "DROP TABLE IF EXISTS employees;")) {
        finish_with_error(con);
    }
    if (mysql_query(con, "CREATE TABLE employees (id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(50), age INT, salary DOUBLE);")) {
        finish_with_error(con);
    }
    printf("Table 'employees' created or already exists.\n");
    // b. 插入数据 (使用多条语句)
    const char *insert_sql = "INSERT INTO employees (name, age, salary) VALUES ('Alice', 30, 75000.50);"
                             "INSERT INTO employees (name, age, salary) VALUES ('Bob', 25, 62000.00);"
                             "INSERT INTO employees (name, age, salary) VALUES ('Charlie', 35, 90000.75);";
    if (mysql_query(con, insert_sql)) {
        finish_with_error(con);
    }
    printf("3 records inserted.\n");
    // c. 查询数据
    if (mysql_query(con, "SELECT * FROM employees")) {
        finish_with_error(con);
    }
    // 获取结果集
    result = mysql_store_result(con);
    if (result == NULL) {
        finish_with_error(con);
    }
    // 获取列数
    int num_fields = mysql_num_fields(result);
    // 打印表头
    MYSQL_FIELD *field;
    printf("\n--- Employee Data ---\n");
    while ((field = mysql_fetch_field(result))) {
        printf("%-15s", field->name);
    }
    printf("\n");
    // 遍历并打印每一行数据
    while ((row = mysql_fetch_row(result))) {
        for(int i = 0; i < num_fields; i++) {
            printf("%-15s", row[i] ? row[i] : "NULL");
        }
        printf("\n");
    }
    printf("---------------------\n");
    // 释放结果集
    mysql_free_result(result);
    // d. 更新数据
    const char *update_sql = "UPDATE employees SET salary = salary * 1.1 WHERE name = 'Alice';";
    if (mysql_query(con, update_sql)) {
        finish_with_error(con);
    }
    printf("Alice's salary updated.\n");
    // e. 删除数据
    const char *delete_sql = "DELETE FROM employees WHERE age < 30;";
    if (mysql_query(con, delete_sql)) {
        finish_with_error(con);
    }
    printf("Employees under 30 deleted.\n");
    // 再次查询以显示更新后的结果
    if (mysql_query(con, "SELECT id, name, salary FROM employees")) {
        finish_with_error(con);
    }
    result = mysql_store_result(con);
    if (result == NULL) {
        finish_with_error(con);
    }
    num_fields = mysql_num_fields(result);
    printf("\n--- Updated Employee Data ---\n");
    while ((field = mysql_fetch_field(result))) {
        printf("%-10s", field->name);
    }
    printf("\n");
    while ((row = mysql_fetch_row(result))) {
        for(int i = 0; i < num_fields; i++) {
            printf("%-10s", row[i] ? row[i] : "NULL");
        }
        printf("\n");
    }
    printf("-----------------------------\n");
    mysql_free_result(result);
    // 4. 关闭连接
    mysql_close(con);
    printf("MySQL connection closed.\n");
    return 0;
}

如何编译 C 程序

使用 gcc 编译器时,需要链接 MySQL 客户端库,关键选项是 -lmysqlclient

打开终端,进入你的代码所在目录,然后运行:

gcc main.c -o mysql_app -lmysqlclient
  • gcc main.c: 指定要编译的源文件。
  • -o mysql_app: 指定输出的可执行文件名为 mysql_app
  • -lmysqlclient: 告诉链接器去链接 libmysqlclient.so 库,这个库通常在 /usr/lib/x86_64-linux-gnu/ 或类似路径下。

如果编译时提示找不到头文件或库,请确保你已经正确安装了 libmysqlclient-dev 包。

运行编译好的程序:

./mysql_app

你应该能看到程序输出的信息,包括连接成功、创建表、插入数据、查询结果等。


处理错误与最佳实践

  1. 始终检查返回值mysql_init, mysql_real_connect, mysql_query, mysql_store_result 等函数在失败时都会返回错误状态(NULL 或非零值),你的代码应该像示例中一样,检查每个调用的返回值,并在出错时妥善处理。
  2. 使用 mysql_error():当 API 调用失败时,mysql_error(con) 会返回一个描述性错误字符串,这对于调试至关重要。
  3. 释放资源mysql_free_result()mysql_close() 必须被调用,否则会导致内存泄漏和连接资源未释放,一个良好的做法是使用 gotodo { ... } while(0) 结构来确保清理代码一定会执行。
  4. 使用预处理语句防止 SQL 注入:上面的示例直接拼接 SQL 字符串,这非常危险,容易受到 SQL 注入攻击,在生产环境中,强烈推荐使用预处理语句

进阶话题:预处理语句

预处理语句可以将 SQL 语句和数据分开发送到服务器,服务器会先编译 SQL 语句,然后只将数据作为参数发送,这大大提高了安全性,并允许数据库服务器缓存执行计划,提高重复执行的效率。

基本流程:

  1. mysql_stmt_prepare(): 准备一个 SQL 语句模板,使用 作为占位符。
  2. mysql_stmt_param_count(): 获取参数数量。
  3. mysql_stmt_bind_param(): 将 C 语言变量绑定到 占位符。
  4. mysql_stmt_execute(): 执行语句。
  5. mysql_stmt_bind_result(): 将结果集的列绑定到 C 语言变量。
  6. mysql_stmt_fetch(): 获取结果。

这是一个更安全、更高效的编程模型,对于复杂的 C 应用程序来说是必须掌握的技能,你可以查阅 MySQL 官方文档中关于 "C API Prepared Statement" 的章节来学习更详细的使用方法。

-- 展开阅读全文 --
头像
织梦5.7后台登录空白,如何解决?
« 上一篇 01-13
织梦如何对接微信支付接口?
下一篇 » 01-13

相关文章

取消
微信二维码
支付宝二维码

目录[+]