设计思路
我们将采用模块化的设计思想,将整个系统分解为几个核心功能模块,每个模块由不同的函数实现。

(图片来源网络,侵删)
-
数据结构设计:
- 使用
struct来定义“设备”这个核心实体,一个设备应该包含哪些信息?ID、名称、类型、状态(可用、借出、维修中)、借用人、借出日期、归还日期等。 - 使用一个结构体数组来存储所有设备信息。
- 使用
-
功能模块划分:
- 主菜单:程序的入口,提供所有功能选项的导航。
- 设备录入:向系统中添加新的设备。
- 设备查询:根据不同条件(如ID、名称、状态)查找设备。
- 设备借出:修改设备状态为“借出”,并记录借用人信息。
- 设备归还:修改设备状态为“可用”,并清除借出信息。
- 设备信息修改:更新已有设备的信息(如名称、类型)。
- 设备删除:从系统中移除一个设备。
- 设备列表显示:以表格形式列出所有设备。
- 数据持久化:将设备信息保存到文件中,并在程序启动时从文件加载。
-
用户交互:
- 使用
printf和scanf来实现与用户的交互,为了更好的体验,可以封装一个getInput函数来处理输入,防止缓冲区溢出等问题。
- 使用
代码实现
下面是一个完整的C语言实现,包含了详细的注释。

(图片来源网络,侵删)
头文件和宏定义
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h> // 用于获取当前日期
// --- 宏定义 ---
#define MAX_NAME_LEN 50
#define MAX_TYPE_LEN 30
#define MAX_USER_LEN 30
#define MAX_DEVICES 100
#define FILENAME "devices.dat"
// --- 设备状态枚举 ---
typedef enum {
AVAILABLE, // 0 - 可用
BORROWED, // 1 - 借出
UNDER_REPAIR // 2 - 维修中
} DeviceStatus;
// --- 设备结构体 ---
typedef struct {
int id; // 设备ID
char name[MAX_NAME_LEN]; // 设备名称
char type[MAX_TYPE_LEN]; // 设备类型
DeviceStatus status; // 设备状态
char borrower[MAX_USER_LEN]; // 借用人
char borrow_date[20]; // 借出日期 (YYYY-MM-DD)
char return_date[20]; // 归还日期 (YYYY-MM-DD)
} Device;
// --- 全局变量 ---
Device devices[MAX_DEVICES]; // 设备数组
int device_count = 0; // 当前设备数量
// --- 函数声明 ---
void loadDevicesFromFile();
void saveDevicesToFile();
void showMainMenu();
void addDevice();
void listDevices();
void searchDevices();
void borrowDevice();
void returnDevice();
void modifyDevice();
void deleteDevice();
void clearInputBuffer();
核心功能函数实现
// 从文件加载设备数据
void loadDevicesFromFile() {
FILE *file = fopen(FILENAME, "rb");
if (file == NULL) {
// 文件不存在是正常情况(首次运行)
return;
}
// 读取设备数量
fread(&device_count, sizeof(int), 1, file);
// 读取所有设备数据
fread(devices, sizeof(Device), device_count, file);
fclose(file);
}
// 将设备数据保存到文件
void saveDevicesToFile() {
FILE *file = fopen(FILENAME, "wb");
if (file == NULL) {
printf("错误:无法打开文件进行保存!\n");
return;
}
// 先写入设备数量
fwrite(&device_count, sizeof(int), 1, file);
// 再写入所有设备数据
fwrite(devices, sizeof(Device), device_count, file);
fclose(file);
}
// 显示主菜单
void showMainMenu() {
system("cls || clear"); // 清屏 (Windows: cls, Linux/Mac: clear)
printf("\n\n========== 实验设备管理系统 ==========\n");
printf(" 1. 添加新设备\n");
printf(" 2. 查看所有设备\n");
printf(" 3. 查询设备\n");
printf(" 4. 借出设备\n");
printf(" 5. 归还设备\n");
printf(" 6. 修改设备信息\n");
printf(" 7. 删除设备\n");
printf(" 0. 退出系统\n");
printf("=====================================\n");
printf("请输入您的选择: ");
}
// 添加新设备
void addDevice() {
if (device_count >= MAX_DEVICES) {
printf("设备数量已达上限,无法添加!\n");
return;
}
Device new_device;
printf("\n--- 添加新设备 ---\n");
printf("请输入设备ID: ");
scanf("%d", &new_device.id);
clearInputBuffer();
// 检查ID是否已存在
for (int i = 0; i < device_count; i++) {
if (devices[i].id == new_device.id) {
printf("错误:设备ID已存在!\n");
return;
}
}
printf("请输入设备名称: ");
fgets(new_device.name, MAX_NAME_LEN, stdin);
new_device.name[strcspn(new_device.name, "\n")] = 0; // 去掉换行符
printf("请输入设备类型: ");
fgets(new_device.type, MAX_TYPE_LEN, stdin);
new_device.type[strcspn(new_device.type, "\n")] = 0;
new_device.status = AVAILABLE;
strcpy(new_device.borrower, "");
strcpy(new_device.borrow_date, "");
strcpy(new_device.return_date, "");
devices[device_count++] = new_device;
printf("设备添加成功!\n");
saveDevicesToFile();
}
// 显示所有设备
void listDevices() {
if (device_count == 0) {
printf("\n当前没有任何设备记录,\n");
return;
}
printf("\n--- 所有设备列表 ---\n");
printf("+----+----------------------+----------------------+--------+----------------------+----------------------+----------------------+\n");
printf("| ID | 名称 | 类型 | 状态 | 借用人 | 借出日期 | 归还日期 |\n");
printf("+----+----------------------+----------------------+--------+----------------------+----------------------+----------------------+\n");
for (int i = 0; i < device_count; i++) {
printf("| %2d | %-20s | %-20s | ", devices[i].id, devices[i].name, devices[i].type);
switch (devices[i].status) {
case AVAILABLE: printf(" %-6s | ", "可用"); break;
case BORROWED: printf(" %-6s | ", "借出"); break;
case UNDER_REPAIR: printf(" %-6s | ", "维修中"); break;
}
printf("%-20s | %-20s | %-20s |\n",
devices[i].borrower,
devices[i].borrow_date,
devices[i].return_date);
}
printf("+----+----------------------+----------------------+--------+----------------------+----------------------+----------------------+\n");
}
// 查询设备
void searchDevices() {
if (device_count == 0) {
printf("\n当前没有任何设备记录,\n");
return;
}
int choice;
printf("\n--- 查询设备 ---\n");
printf("1. 按ID查询\n");
printf("2. 按名称查询\n");
printf("3. 按状态查询\n");
printf("请选择查询方式: ");
scanf("%d", &choice);
clearInputBuffer();
printf("\n--- 查询结果 ---\n");
int found = 0;
for (int i = 0; i < device_count; i++) {
int match = 0;
switch (choice) {
case 1: {
int search_id;
printf("请输入要查询的ID: ");
scanf("%d", &search_id);
clearInputBuffer();
if (devices[i].id == search_id) match = 1;
break;
}
case 2: {
char search_name[MAX_NAME_LEN];
printf("请输入要查询的名称: ");
fgets(search_name, MAX_NAME_LEN, stdin);
search_name[strcspn(search_name, "\n")] = 0;
if (strstr(devices[i].name, search_name) != NULL) match = 1;
break;
}
case 3: {
DeviceStatus search_status;
printf("请选择状态 (0-可用, 1-借出, 2-维修中): ");
scanf("%d", (int*)&search_status);
clearInputBuffer();
if (devices[i].status == search_status) match = 1;
break;
}
default:
printf("无效的选择!\n");
return;
}
if (match) {
if (!found) {
printf("+----+----------------------+----------------------+--------+----------------------+----------------------+----------------------+\n");
printf("| ID | 名称 | 类型 | 状态 | 借用人 | 借出日期 | 归还日期 |\n");
printf("+----+----------------------+----------------------+--------+----------------------+----------------------+----------------------+\n");
}
found = 1;
printf("| %2d | %-20s | %-20s | ", devices[i].id, devices[i].name, devices[i].type);
switch (devices[i].status) {
case AVAILABLE: printf(" %-6s | ", "可用"); break;
case BORROWED: printf(" %-6s | ", "借出"); break;
case UNDER_REPAIR: printf(" %-6s | ", "维修中"); break;
}
printf("%-20s | %-20s | %-20s |\n",
devices[i].borrower,
devices[i].borrow_date,
devices[i].return_date);
}
}
if (found) {
printf("+----+----------------------+----------------------+--------+----------------------+----------------------+----------------------+\n");
} else {
printf("未找到匹配的设备,\n");
}
}
// 借出设备
void borrowDevice() {
if (device_count == 0) {
printf("\n当前没有任何设备记录,\n");
return;
}
int id;
printf("\n--- 借出设备 ---\n");
printf("请输入要借出的设备ID: ");
scanf("%d", &id);
clearInputBuffer();
for (int i = 0; i < device_count; i++) {
if (devices[i].id == id) {
if (devices[i].status == AVAILABLE) {
devices[i].status = BORROWED;
printf("请输入借用人姓名: ");
fgets(devices[i].borrower, MAX_USER_LEN, stdin);
devices[i].borrower[strcspn(devices[i].borrower, "\n")] = 0;
// 获取当前日期
time_t t = time(NULL);
struct tm tm = *localtime(&t);
sprintf(devices[i].borrow_date, "%d-%02d-%02d", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);
strcpy(devices[i].return_date, ""); // 归还日期先为空
printf("设备借出成功!\n");
printf("借出日期: %s\n", devices[i].borrow_date);
saveDevicesToFile();
} else {
printf("该设备当前不可用(状态: %s),\n",
devices[i].status == BORROWED ? "已借出" : "维修中");
}
return;
}
}
printf("未找到ID为 %d 的设备,\n", id);
}
// 归还设备
void returnDevice() {
if (device_count == 0) {
printf("\n当前没有任何设备记录,\n");
return;
}
int id;
printf("\n--- 归还设备 ---\n");
printf("请输入要归还的设备ID: ");
scanf("%d", &id);
clearInputBuffer();
for (int i = 0; i < device_count; i++) {
if (devices[i].id == id) {
if (devices[i].status == BORROWED) {
devices[i].status = AVAILABLE;
// 获取当前日期作为归还日期
time_t t = time(NULL);
struct tm tm = *localtime(&t);
sprintf(devices[i].return_date, "%d-%02d-%02d", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);
printf("设备归还成功!\n");
printf("归还日期: %s\n", devices[i].return_date);
// 清空借用人信息
strcpy(devices[i].borrower, "");
strcpy(devices[i].borrow_date, "");
saveDevicesToFile();
} else {
printf("该设备未被借出或状态不正确,\n");
}
return;
}
}
printf("未找到ID为 %d 的设备,\n", id);
}
// 修改设备信息
void modifyDevice() {
if (device_count == 0) {
printf("\n当前没有任何设备记录,\n");
return;
}
int id;
printf("\n--- 修改设备信息 ---\n");
printf("请输入要修改的设备ID: ");
scanf("%d", &id);
clearInputBuffer();
for (int i = 0; i < device_count; i++) {
if (devices[i].id == id) {
printf("找到设备: %s (ID: %d)\n", devices[i].name, devices[i].id);
printf("请输入新的设备名称 (当前: %s): ", devices[i].name);
fgets(devices[i].name, MAX_NAME_LEN, stdin);
devices[i].name[strcspn(devices[i].name, "\n")] = 0;
printf("请输入新的设备类型 (当前: %s): ", devices[i].type);
fgets(devices[i].type, MAX_TYPE_LEN, stdin);
devices[i].type[strcspn(devices[i].type, "\n")] = 0;
printf("设备信息修改成功!\n");
saveDevicesToFile();
return;
}
}
printf("未找到ID为 %d 的设备,\n", id);
}
// 删除设备
void deleteDevice() {
if (device_count == 0) {
printf("\n当前没有任何设备记录,\n");
return;
}
int id;
printf("\n--- 删除设备 ---\n");
printf("请输入要删除的设备ID: ");
scanf("%d", &id);
clearInputBuffer();
for (int i = 0; i < device_count; i++) {
if (devices[i].id == id) {
// 确认是否真的要删除
printf("您确定要删除设备 '%s' (ID: %d) 吗? (y/n): ", devices[i].name, devices[i].id);
char confirm;
scanf(" %c", &confirm);
if (confirm == 'y' || confirm == 'Y') {
// 将最后一个元素移到当前位置,然后减少数量
devices[i] = devices[device_count - 1];
device_count--;
printf("设备删除成功!\n");
saveDevicesToFile();
} else {
printf("删除操作已取消,\n");
}
return;
}
}
printf("未找到ID为 %d 的设备,\n", id);
}
// 清空输入缓冲区
void clearInputBuffer() {
while (getchar() != '\n');
}
// 主函数
int main() {
loadDevicesFromFile(); // 程序启动时加载数据
int choice;
do {
showMainMenu();
scanf("%d", &choice);
clearInputBuffer();
switch (choice) {
case 1: addDevice(); break;
case 2: listDevices(); break;
case 3: searchDevices(); break;
case 4: borrowDevice(); break;
case 5: returnDevice(); break;
case 6: modifyDevice(); break;
case 7: deleteDevice(); break;
case 0: printf("感谢使用,再见!\n"; break;
default: printf("无效的输入,请重新选择!\n");
}
if (choice != 0) {
printf("\n按 Enter 键返回主菜单...");
getchar(); // 等待用户按回车
}
} while (choice != 0);
return 0;
}
如何运行和扩展
如何运行
- 将以上代码保存为一个文件,
device_manager.c。 - 使用GCC编译器进行编译:
gcc device_manager.c -o device_manager
- 运行生成的可执行文件:
- 在Windows上:
device_manager.exe - 在Linux或macOS上:
./device_manager
- 在Windows上:
系统功能说明
- 数据持久化:程序运行时,数据存储在内存中,当选择退出(选项0)时,所有数据会自动保存到
devices.dat文件中,下次启动程序时,会自动从该文件加载数据。 - 借出/归还逻辑:借出设备时,系统会自动记录当前日期作为借出日期,归还设备时,会自动记录归还日期,并将设备状态重置为“可用”。
- 用户输入处理:
clearInputBuffer()函数用于处理scanf后可能留在输入缓冲区中的换行符,防止后续的fgets或getchar出现问题。
如何扩展和改进
这个系统是一个基础版本,可以根据需要进行扩展:
- 更复杂的查询:增加按“借用人”查询、按“借出日期范围”查询等功能。
- 用户权限管理:增加“管理员”和“普通用户”角色,普通用户只能查询和借还设备,管理员才能进行增删改操作。
- 图形用户界面:使用GTK、Qt等库为程序创建图形界面,提升用户体验。
- 数据库支持:对于大量设备数据,使用文件存储可能效率不高,可以改用SQLite等轻量级数据库,它无需单独的服务器,非常适合此类应用。
- 数据校验:在输入设备ID、日期等时,增加更严格的格式校验,防止非法输入。
- 统计报表功能:增加统计功能,本月借出次数最多的设备”、“当前在借设备数量”等。

(图片来源网络,侵删)
