C语言如何连接并操作MySQL数据库?

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

目录

  1. 环境准备
    • 安装MySQL服务器
    • 安装MySQL C连接库
    • 包含头文件和链接库
  2. 核心API介绍
    • 初始化连接
    • 执行SQL语句
    • 处理查询结果
    • 释放资源
  3. 完整代码示例
    • 创建数据库和表
    • C语言完整代码(增、删、改、查)
  4. 预处理语句
    • 什么是预处理语句
    • 为什么需要它(防止SQL注入)
    • 代码示例
  5. 最佳实践与注意事项

环境准备

a. 安装MySQL服务器

你的系统上需要有一个正在运行的MySQL服务器,如果你还没有,请从 MySQL官网 下载并安装。

c语言访问mysql数据库
(图片来源网络,侵删)

b. 安装MySQL C连接库

你需要安装C语言连接MySQL所需的客户端库和头文件,这个库通常被称为 libmysqlclient

  • 在 Ubuntu/Debian 上:

    sudo apt-get update
    sudo apt-get install libmysqlclient-dev

    libmysqlclient-dev 包含了编译所需的头文件(如 mysql.h)和库文件(如 libmysqlclient.so)。

  • 在 CentOS/RHEL/Fedora 上:

    c语言访问mysql数据库
    (图片来源网络,侵删)
    sudo yum install mysql-devel
    # 或者对于较新的系统
    sudo dnf install mysql-devel
  • 在 macOS 上 (使用 Homebrew):

    brew install mysql-client

    安装后,你可能需要设置一些链接,以便编译器能找到它,可以参考 Homebrew 的输出提示。

  • 在 Windows 上: 最简单的方式是下载 "MySQL for Visual C++" 的压缩包,它包含了所有必要的库和头文件,将其解压到一个固定目录(如 C:\mysql-connector-c-6.1.x-winx64),然后在你的IDE(如Visual Studio)中配置包含目录和库目录。

c. 包含头文件和链接库

在你的C代码中,你需要包含MySQL的头文件:

c语言访问mysql数据库
(图片来源网络,侵删)
#include <mysql/mysql.h> // 在Linux/macOS上
// #include <winsock2.h> // 在Windows上,可能需要包含这个
// #include <mysql.h>   // 在Windows上,路径可能不同

在编译时,你需要链接 mysqlclient 库。

  • 使用 gcc 编译:

    gcc your_program.c -o your_program -I/usr/include/mysql -L/usr/lib/x86_64-linux-gnu -lmysqlclient
    • -I: 指定头文件路径。
    • -L: 指定库文件路径。
    • -lmysqlclient: 链接 libmysqlclient 库。

    如果你的库在标准路径下(如通过 apt 安装),-I-L 参数可以省略:

    gcc your_program.c -o your_program -lmysqlclient

核心API介绍

C语言的MySQL API是一系列C函数,你需要按顺序调用它们来完成数据库操作。

a. 初始化连接

MYSQL *mysql_init(MYSQL *mysql);

初始化一个 MYSQL 对象句柄,通常传入 NULL 让它分配一个新的。

MYSQL *mysql_real_connect(MYSQL *mysql, const char *host,
                         const char *user, const char *passwd,
                         const char *db, unsigned int port,
                         const char *unix_socket, unsigned long clientflag);

建立到数据库服务器的连接。

  • mysql: mysql_init() 返回的句柄。
  • host: 服务器地址(如 "localhost")。
  • user: 用户名。
  • passwd: 密码。
  • db: 默认连接的数据库名(可以为 NULL,后续再选)。
  • port: 端口号(通常为 0,使用默认的 3306)。
  • unix_socket: Unix域套接字路径(通常为 NULL)。
  • clientflag: 客户端标志(通常为 0)。

连接成功返回 MYSQL* 指针,失败返回 NULL

b. 执行SQL语句

int mysql_query(MYSQL *mysql, const char *stmt_str);

执行一个SQL语句,这个函数主要用于执行不返回结果集的语句(如 INSERT, UPDATE, DELETE, CREATE)。

  • 成功返回 0。
  • 失败返回非 0。
MYSQL_RES *mysql_store_result(MYSQL *mysql);

对于会返回结果集的查询(如 SELECT),在执行 mysql_query 后,调用此函数将整个结果集从服务器获取到客户端内存,返回一个 MYSQL_RES 结果集指针。

c. 处理查询结果

如果查询成功并返回了结果集(mysql_store_result 返回非 NULL),你需要遍历它。

MYSQL_ROW mysql_fetch_row(MYSQL_RES *result);

获取结果集的下一行数据,返回一个字符串数组(MYSQL_ROW),其中每个元素对应一列的数据,当没有更多行时返回 NULL

unsigned int mysql_num_fields(MYSQL_RES *result);

获取结果集中的列数。

my_ulonglong mysql_affected_rows(MYSQL *mysql);

获取 INSERT, UPDATE, DELETE 语句影响的行数。

d. 释放资源

非常重要! 忘记释放资源会导致内存泄漏。

void mysql_free_result(MYSQL_RES *result);

释放 mysql_store_result 分配的结果集内存。

void mysql_close(MYSQL *mysql);

关闭数据库连接并释放 MYSQL 句柄。


完整代码示例

这个例子将演示如何连接数据库,创建表,插入数据,查询数据,更新数据和删除数据。

a. 准备数据库

在MySQL中执行以下SQL:

CREATE DATABASE IF NOT EXISTS test_db;
USE test_db;
CREATE TABLE IF NOT EXISTS users (
    id INT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(50) NOT NULL,
    age INT,
    email VARCHAR(100)
);

b. C语言完整代码 (mysql_example.c)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <mysql/mysql.h> // 包含MySQL头文件
// 定义数据库连接信息
#define DB_HOST "localhost"
#define DB_USER "root"
#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() {
    MYSQL *con = mysql_init(NULL); // 1. 初始化连接句柄
    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, 0) == NULL) {
        finish_with_error(con);
    }
    printf("Connected to MySQL successfully!\n");
    // 3. 执行SQL - 创建表 (如果不存在)
    if (mysql_query(con, "DROP TABLE IF EXISTS users; CREATE TABLE users (id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(50), age INT, email VARCHAR(100));")) {
        finish_with_error(con);
    }
    printf("Table 'users' created or already exists.\n");
    // 4. 执行SQL - 插入数据
    const char *insert_sql = "INSERT INTO users (name, age, email) VALUES ('Alice', 30, 'alice@example.com'), ('Bob', 25, 'bob@example.com');";
    if (mysql_query(con, insert_sql)) {
        finish_with_error(con);
    }
    printf("Inserted 2 rows into 'users'.\n");
    // 5. 执行SQL - 查询数据
    if (mysql_query(con, "SELECT * FROM users")) {
        finish_with_error(con);
    }
    // 6. 获取结果集
    MYSQL_RES *result = mysql_store_result(con);
    if (result == NULL) {
        finish_with_error(con);
    }
    int num_fields = mysql_num_fields(result);
    // 7. 遍历结果集
    MYSQL_ROW row;
    MYSQL_FIELD *field;
    printf("\n--- Query Results ---\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");
    // 8. 释放结果集
    mysql_free_result(result);
    // 9. 执行SQL - 更新数据
    const char *update_sql = "UPDATE users SET age = 31 WHERE name = 'Alice'";
    if (mysql_query(con, update_sql)) {
        finish_with_error(con);
    }
    printf("Updated 1 row in 'users'.\n");
    // 10. 执行SQL - 删除数据
    const char *delete_sql = "DELETE FROM users WHERE name = 'Bob'";
    if (mysql_query(con, delete_sql)) {
        finish_with_error(con);
    }
    printf("Deleted 1 row from 'users'.\n");
    // 11. 关闭连接
    mysql_close(con);
    printf("Connection closed.\n");
    return 0;
}

c. 编译和运行

# 编译
gcc mysql_example.c -o mysql_example -lmysqlclient
# 运行 (确保你的MySQL服务正在运行)
./mysql_example

预处理语句

直接拼接SQL字符串(sprintf(sql, "INSERT ... VALUES('%s', %d)", name, age))是极其危险的,会导致 SQL注入 攻击,预处理语句是防止SQL注入的标准方法。

它的工作原理是:

  1. 发送SQL模板给服务器,其中参数用 占位。
  2. 服务器解析并编译这个模板。
  3. 程序分别发送每个参数值给服务器。
  4. 服务器将参数安全地绑定到模板中并执行。

预处理语句代码示例

// ... (前面的连接代码相同) ...
// 1. 准备预处理语句
if (mysql_query(con, "INSERT INTO users (name, age, email) VALUES (?, ?, ?)")) {
    finish_with_error(con);
}
MYSQL_STMT *stmt = mysql_stmt_init(con);
if (stmt == NULL) {
    finish_with_error(con);
}
const char *stmt_str = "INSERT INTO users (name, age, email) VALUES (?, ?, ?)";
if (mysql_stmt_prepare(stmt, stmt_str, strlen(stmt_str))) {
    fprintf(stderr, "mysql_stmt_prepare() failed\n");
    mysql_stmt_close(stmt);
    finish_with_error(con);
}
printf("Statement prepared.\n");
// 2. 绑定参数
// 定义变量来存放要绑定的数据
char name[50] = "Charlie";
int age = 28;
char email[100] = "charlie@example.com";
// 绑定变量到SQL语句的占位符
// MYSQL_BIND 结构体数组,每个元素对应一个 ?
MYSQL_BIND bind[3];
memset(bind, 0, sizeof(bind));
// 绑定 name (字符串)
bind[0].buffer_type = MYSQL_TYPE_STRING;
bind[0].buffer = (char *)name;
bind[0].buffer_length = strlen(name);
bind[0].length = &bind[0].buffer_length; // 让mysql自动计算长度
// 绑定 age (整数)
bind[1].buffer_type = MYSQL_TYPE_LONG;
bind[1].buffer = (char *)&age;
bind[1].is_null = 0; // 不为NULL
// 绑定 email (字符串)
bind[2].buffer_type = MYSQL_TYPE_STRING;
bind[2].buffer = (char *)email;
bind[2].buffer_length = strlen(email);
bind[2].length = &bind[2].buffer_length;
if (mysql_stmt_bind_param(stmt, bind)) {
    fprintf(stderr, "mysql_stmt_bind_param() failed\n");
    mysql_stmt_close(stmt);
    finish_with_error(con);
}
// 3. 执行预处理语句
if (mysql_stmt_execute(stmt)) {
    fprintf(stderr, "mysql_stmt_execute() failed\n");
    mysql_stmt_close(stmt);
    finish_with_error(con);
}
printf("Inserted 1 row using prepared statement.\n");
// 4. 关闭预处理语句
mysql_stmt_close(stmt);
// ... (关闭连接) ...

最佳实践与注意事项

  1. 错误处理:始终检查每个MySQL API函数的返回值,如果失败,立即使用 mysql_error(con) 获取错误信息并处理。
  2. 资源释放:遵循“谁分配,谁释放”的原则。mysql_init 分配的句柄由 mysql_close 释放,mysql_store_result 分配的结果集由 mysql_free_result 释放。mysql_stmt_init 分配的由 mysql_stmt_close 释放。
  3. 使用预处理语句:对于任何包含用户输入的SQL查询,必须使用预处理语句来防止SQL注入,这是最重要的安全实践。
  4. 多线程MYSQLMYSQL_STMT 结构体不是线程安全的,每个线程都应该拥有自己独立的连接句柄。
  5. 连接管理:在高性能应用中,频繁地建立和关闭连接开销很大,应该使用 连接池 技术来复用数据库连接。
  6. 字符集:为了避免乱码,在连接后立即设置正确的字符集。
    mysql_set_character_set(con, "utf8mb4");
-- 展开阅读全文 --
头像
dede站点默认关键字
« 上一篇 03-05
74HC595 C语言程序如何实现数据移位输出?
下一篇 » 03-05

相关文章

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

目录[+]