C语言项目实战教程:11.3 项目实现

在本节中,我们将深入探讨如何在C语言中实现一个小型项目。我们将通过一个简单的命令行图书管理系统来展示项目的结构、设计和实现过程。该项目将涵盖文件操作、数据结构、动态内存管理等多个方面,适合希望提升C语言技能的开发者。

项目概述

我们的图书管理系统将具备以下功能:

  1. 添加新书籍
  2. 显示所有书籍
  3. 查找书籍
  4. 删除书籍
  5. 保存和加载书籍数据

项目结构

在开始编码之前,我们需要设计项目的结构。以下是推荐的文件结构:

book_manager/
├── main.c
├── book.h
├── book.c
└── Makefile
  • main.c:主程序文件,负责用户交互。
  • book.h:书籍数据结构和函数声明。
  • book.c:书籍相关的函数实现。
  • Makefile:构建项目的文件。

数据结构设计

book.h中,我们定义书籍的数据结构和相关函数的声明。

// book.h
#ifndef BOOK_H
#define BOOK_H

#define MAX_TITLE_LENGTH 100
#define MAX_AUTHOR_LENGTH 100
#define MAX_BOOKS 100

typedef struct {
    char title[MAX_TITLE_LENGTH];
    char author[MAX_AUTHOR_LENGTH];
    int year;
} Book;

void addBook(Book books[], int *count);
void displayBooks(const Book books[], int count);
int findBook(const Book books[], int count, const char *title);
void deleteBook(Book books[], int *count, const char *title);
void saveBooks(const Book books[], int count);
void loadBooks(Book books[], int *count);

#endif // BOOK_H

函数实现

接下来,在book.c中实现这些函数。

// book.c
#include <stdio.h>
#include <string.h>
#include "book.h"

void addBook(Book books[], int *count) {
    if (*count >= MAX_BOOKS) {
        printf("书籍数量已达上限。\n");
        return;
    }
    printf("输入书名: ");
    scanf("%s", books[*count].title);
    printf("输入作者: ");
    scanf("%s", books[*count].author);
    printf("输入出版年份: ");
    scanf("%d", &books[*count].year);
    (*count)++;
    printf("书籍添加成功!\n");
}

void displayBooks(const Book books[], int count) {
    if (count == 0) {
        printf("没有书籍可显示。\n");
        return;
    }
    for (int i = 0; i < count; i++) {
        printf("书名: %s, 作者: %s, 年份: %d\n", books[i].title, books[i].author, books[i].year);
    }
}

int findBook(const Book books[], int count, const char *title) {
    for (int i = 0; i < count; i++) {
        if (strcmp(books[i].title, title) == 0) {
            return i;
        }
    }
    return -1; // 未找到
}

void deleteBook(Book books[], int *count, const char *title) {
    int index = findBook(books, *count, title);
    if (index == -1) {
        printf("未找到书籍。\n");
        return;
    }
    for (int i = index; i < *count - 1; i++) {
        books[i] = books[i + 1];
    }
    (*count)--;
    printf("书籍删除成功!\n");
}

void saveBooks(const Book books[], int count) {
    FILE *file = fopen("books.dat", "wb");
    if (!file) {
        printf("无法打开文件进行写入。\n");
        return;
    }
    fwrite(books, sizeof(Book), count, file);
    fclose(file);
    printf("书籍数据已保存。\n");
}

void loadBooks(Book books[], int *count) {
    FILE *file = fopen("books.dat", "rb");
    if (!file) {
        printf("无法打开文件进行读取。\n");
        return;
    }
    *count = fread(books, sizeof(Book), MAX_BOOKS, file);
    fclose(file);
    printf("书籍数据已加载。\n");
}

主程序

main.c中实现用户交互逻辑。

// main.c
#include <stdio.h>
#include "book.h"

int main() {
    Book books[MAX_BOOKS];
    int count = 0;
    int choice;

    loadBooks(books, &count);

    do {
        printf("\n图书管理系统\n");
        printf("1. 添加书籍\n");
        printf("2. 显示所有书籍\n");
        printf("3. 查找书籍\n");
        printf("4. 删除书籍\n");
        printf("5. 保存书籍\n");
        printf("6. 退出\n");
        printf("选择操作: ");
        scanf("%d", &choice);

        switch (choice) {
            case 1:
                addBook(books, &count);
                break;
            case 2:
                displayBooks(books, count);
                break;
            case 3: {
                char title[MAX_TITLE_LENGTH];
                printf("输入书名: ");
                scanf("%s", title);
                int index = findBook(books, count, title);
                if (index != -1) {
                    printf("找到书籍: %s, 作者: %s, 年份: %d\n", books[index].title, books[index].author, books[index].year);
                } else {
                    printf("未找到书籍。\n");
                }
                break;
            }
            case 4: {
                char title[MAX_TITLE_LENGTH];
                printf("输入书名: ");
                scanf("%s", title);
                deleteBook(books, &count, title);
                break;
            }
            case 5:
                saveBooks(books, count);
                break;
            case 6:
                printf("退出系统。\n");
                break;
            default:
                printf("无效选择,请重试。\n");
        }
    } while (choice != 6);

    return 0;
}

Makefile

为了方便编译,我们可以使用Makefile。

# Makefile
CC = gcc
CFLAGS = -Wall -g
OBJ = main.o book.o
TARGET = book_manager

all: $(TARGET)

$(TARGET): $(OBJ)
	$(CC) $(CFLAGS) -o $(TARGET) $(OBJ)

%.o: %.c
	$(CC) $(CFLAGS) -c $<

clean:
	rm -f $(OBJ) $(TARGET)

优点与缺点

优点

  1. 模块化设计:将书籍相关的功能分离到book.cbook.h中,增强了代码的可读性和可维护性。
  2. 数据持久化:通过文件操作实现数据的保存和加载,确保数据不会丢失。
  3. 用户友好:提供简单的命令行界面,用户可以方便地进行操作。

缺点

  1. 内存限制:使用静态数组存储书籍,限制了书籍数量,无法动态扩展。
  2. 错误处理不足:在文件操作和用户输入方面的错误处理较为简单,可能导致程序崩溃或数据损坏。
  3. 缺乏多线程支持:该系统是单线程的,无法处理并发请求。

注意事项

  1. 输入验证:在实际应用中,用户输入需要进行严格验证,以防止缓冲区溢出等安全问题。
  2. 内存管理:在使用动态内存分配时,务必确保在不再使用时释放内存,以防内存泄漏。
  3. 文件操作:在进行文件读写时,始终检查文件是否成功打开,并处理可能的错误。

总结

通过本节的学习,我们实现了一个简单的图书管理系统,涵盖了C语言中的多个重要概念。希望通过这个项目,读者能够更深入地理解C语言的应用,并在此基础上进行更复杂的项目开发。