在C语言中,向数组中增加数据库相关的数据通常涉及将数据库查询结果存储到数组中,或者动态扩展数组以容纳新数据,以下是详细的实现步骤和代码示例,涵盖静态数组、动态数组以及与常见数据库(如SQLite)的交互方法。
静态数组存储数据库数据
静态数组的长度在编译时确定,适用于已知数据量的场景,以SQLite为例,假设有一个students
表,包含id
和name
字段,需将查询结果存入结构体数组。
定义结构体和数组
#include <stdio.h> #include <sqlite3.h> #define MAX_STUDENTS 100 typedef struct { int id; char name[50]; } Student; Student students[MAX_STUDENTS]; // 静态数组 int student_count = 0; // 当前存储的学生数量
查询数据库并填充数组
int callback(void *data, int argc, char **argv, char **azColName) { if (student_count < MAX_STUDENTS) { students[student_count].id = atoi(argv[0]); strcpy(students[student_count].name, argv[1]); student_count++; } return 0; } int main() { sqlite3 *db; char *errMsg = 0; int rc = sqlite3_open("test.db", &db); if (rc != SQLITE_OK) { fprintf(stderr, "无法打开数据库: %sn", sqlite3_errmsg(db)); return 1; } // 执行查询 rc = sqlite3_exec(db, "SELECT id, name FROM students", callback, 0, &errMsg); if (rc != SQLITE_OK) { fprintf(stderr, "SQL错误: %sn", errMsg); sqlite3_free(errMsg); } // 输出数组内容 for (int i = 0; i < student_count; i++) { printf("ID: %d, Name: %sn", students[i].id, students[i].name); } sqlite3_close(db); return 0; }
注意事项:
- 静态数组需预先定义最大容量,超出部分会导致数据丢失。
- 适用于数据量固定且较小的场景。
动态数组存储数据库数据
动态数组通过内存分配(如malloc
、realloc
)实现长度扩展,适合数据量不确定的情况。
动态分配数组
#include <stdio.h> #include <stdlib.h> #include <sqlite3.h> typedef struct { int id; char name[50]; } Student; Student *students = NULL; // 动态数组指针 int capacity = 0; // 当前数组容量 int size = 0; // 当前数据量 void add_student(int id, const char *name) { if (size >= capacity) { capacity = (capacity == 0) ? 1 : capacity * 2; // 容量不足时扩容 students = realloc(students, capacity * sizeof(Student)); if (!students) { fprintf(stderr, "内存分配失败n"); exit(1); } } students[size].id = id; strcpy(students[size].name, name); size++; }
查询数据库并动态填充
int callback(void *data, int argc, char **argv, char **azColName) { add_student(atoi(argv[0]), argv[1]); return 0; } int main() { sqlite3 *db; char *errMsg = 0; int rc = sqlite3_open("test.db", &db); if (rc != SQLITE_OK) { fprintf(stderr, "无法打开数据库: %sn", sqlite3_errmsg(db)); return 1; } rc = sqlite3_exec(db, "SELECT id, name FROM students", callback, 0, &errMsg); if (rc != SQLITE_OK) { fprintf(stderr, "SQL错误: %sn", errMsg); sqlite3_free(errMsg); } // 输出数组内容 for (int i = 0; i < size; i++) { printf("ID: %d, Name: %sn", students[i].id, students[i].name); } free(students); // 释放内存 sqlite3_close(db); return 0; }
注意事项:
- 动态数组需手动管理内存,避免内存泄漏。
- 扩容策略通常采用倍增(如
capacity *= 2
)以减少频繁realloc
调用。
数据库与数组的交互优化
使用批量插入减少查询次数
// 假设从CSV文件读取数据并批量插入数据库 void batch_insert(sqlite3 *db, const char *filename) { FILE *file = fopen(filename, "r"); if (!file) return; char line[100]; char sql[200]; sqlite3_exec(db, "BEGIN TRANSACTION;", 0, 0, 0); // 开启事务 while (fgets(line, sizeof(line), file)) { int id; char name[50]; sscanf(line, "%d,%49s", &id, name); sprintf(sql, "INSERT INTO students (id, name) VALUES (%d, '%s');", id, name); sqlite3_exec(db, sql, 0, 0, 0); } sqlite3_exec(db, "COMMIT;", 0, 0, 0); // 提交事务 fclose(file); }
使用预处理语句防止SQL注入
void safe_insert(sqlite3 *db, int id, const char *name) { sqlite3_stmt *stmt; const char *sql = "INSERT INTO students (id, name) VALUES (?, ?);"; if (sqlite3_prepare_v2(db, sql, -1, &stmt, 0) != SQLITE_OK) { fprintf(stderr, "预处理失败: %sn", sqlite3_errmsg(db)); return; } sqlite3_bind_int(stmt, 1, id); sqlite3_bind_text(stmt, 2, name, -1, SQLITE_STATIC); if (sqlite3_step(stmt) != SQLITE_DONE) { fprintf(stderr, "执行失败: %sn", sqlite3_errmsg(db)); } sqlite3_finalize(stmt); }
性能对比与选择建议
方法 | 优点 | 缺点 | 适用场景 |
---|---|---|---|
静态数组 | 内存连续,访问速度快 | 容量固定,无法动态扩展 | 数据量小且固定 |
动态数组 | 容量可扩展,灵活 | 需手动管理内存,可能频繁扩容 | 数据量不确定或动态增长 |
批量插入+事务 | 减少I/O操作,提升性能 | 需事务支持,代码复杂度增加 | 大数据量导入 |
相关问答FAQs
Q1: 如何处理数据库查询结果为空的情况?
A1: 在回调函数或查询逻辑中检查返回的行数,SQLite的sqlite3_exec
回调中可通过argc
判断每列数据是否存在,若argc == 0
则表示无数据,可在查询后检查student_count
或size
是否为0,若为空则提示用户或执行默认逻辑。
Q2: 动态数组扩容时如何避免内存碎片?
A2: 可以采用指数扩容策略(如每次容量翻倍)减少realloc
调用次数,或使用内存池技术预分配大块内存,在程序结束时务必调用free
释放动态数组内存,避免内存泄漏,对于高频操作场景,可考虑使用第三方库(如GLib的GArray
)优化内存管理。
【版权声明】:本站所有内容均来自网络,若无意侵犯到您的权利,请及时与我们联系将尽快删除相关内容!
发表回复