资讯动态

C语言连接MySQL实现图书管理系统:从环境配置到增删改查实战

发布时间:2026/10/24 7:08:11 来源:尧图企业网站定制
简介一份面向初学者的C语言与MySQL数据库结合的综合实践项目文档以控制台程序实现图书管理系统的完整功能覆盖从数据库连接到业务逻辑处理的主要环节。资源围绕C语言基础、数据库连接和SQL查询展开涵盖图书借阅、归还、查询以及管理员端的添加、修改、删除等核心操作便于读者理解软件开发中前后端交互的基本流程。整个资源包为单份doc文档大小仅137KB集中呈现项目代码与关键逻辑说明轻量易用。已有1574人学习下载适合正在学习C语言或数据库课程的初学者作为课程设计参考。文档结合具体代码片段详细演示了使用mysql.h提供的API完成数据表状态更新、条件查询与结果集读取的方法并提示了mysql_error排错和mysql_free_result内存释放等细节能帮助读者快速上手MySQL C API的基本用法。1. 图书管理系统到底是什么一个控制台程序怎么管起一堆书很多初学者学完C语言的基础语法之后都会有一个共同的困惑学了指针、学了结构体、学了循环可这些到底能做什么整天在控制台里打印三角形、算水仙花数总觉得离真正的软件还很远。图书管理系统就是帮你迈过这道坎的一个经典小项目用C语言写一个黑底白字的命令行程序让你能添加图书、借书、还书、搜书而所有书的数据都存在MySQL数据库里。说人话就是控制台是门面数据库是仓库。你在命令行里输入数字选择功能程序就去操作数据库里的数据再把结果打印给你看。这个项目特别适合刚学完C语言基础、想看看数据库和编程怎么结合的零基础新手因为它把登录验证、增删改查、模糊搜索这些真实项目里天天用的东西全串在了一起而且代码量不大拆开看每一段都能看懂。2. 先搞懂一个概念C语言是怎么和MySQL数据库说上话的2.1 别把数据库当黑匣子先看看MySQL那边存了什么你要操作数据库第一步是得知道数据库里长什么样。这个项目的数据库名叫bookinfo里面有一张book表专门存图书信息。表的结构大概是这样的字段名含义示例bid图书编号数字1bname书名字符串C语言程序设计bauthor作者字符串某导师bstate借阅状态字符串未借出 / 已借出还有一张userinfo表存放账号密码字段有uid账号和upwd密码。登录的时候就拿你输入的数字去这张表里比对。你可以先在MySQL里手动建好这两张表也可以等程序跑通之后再回来补。初学者建议先手动建表因为这样你能亲眼看到数据是怎么一条一条存进去的。用Navicat或者命令行都行建表语句后面会给你。数据库这边准备好了剩下的问题就是C语言这个外村人怎么进出 MySQL 这个大仓库去搬东西2.2 C语言连接MySQL的四个标准动作不管是查一本书还是改一本书C语言和MySQL打交道都逃不开这四个步骤第一步初始化。调用mysql_init(0)创建一个连接对象你可以把它理解成拿一张空白的出入证。第二步连接。调用mysql_real_connect填上数据库地址、账号、密码、库名这张出入证就盖章生效了。第三步执行SQL。调用mysql_query把一条SQL语句发给MySQL比如select * from book。第四步取结果。如果是查询操作要用mysql_store_result或mysql_use_result把返回的数据接住再用mysql_fetch_row一行一行读出来。如果是增删改操作只要判断mysql_query返回的是不是0就行。这四个动作在项目里会重复出现很多次。你现在不用记函数名先有个印象连接是一次性的SQL是反复发的结果是要记得释放的。后面每一段代码都会再次遇到它们。2.3 第一个最小连接代码能连上就是胜利我建议你第一次跑的时候先别管那些借书还书的功能就写一个最简单的小程序专门测试能不能连上数据库。这就像学做饭先学会开火火都点不着后面炒菜免谈。#include stdio.h #include windows.h #include mysql.h int main() { // 1. 初始化连接对象 MYSQL *pconn mysql_init(0); if (pconn NULL) { printf(初始化失败\n); return 1; } // 2. 连接数据库地址localhost账号root密码root库名bookinfo if (!mysql_real_connect(pconn, localhost, root, root, bookinfo, 0, 0, 0)) { printf(连接失败错误为%s\n, mysql_error(pconn)); return 1; } // 3. 设置编码防止中文乱码 if (mysql_query(pconn, set names gbk)) { printf(设置编码失败%s\n, mysql_error(pconn)); } printf(数据库连接成功\n); // 4. 用完关闭连接 mysql_close(pconn); return 0; }这段代码里mysql_init(0)括号里的0表示不额外指定参数用默认配置。mysql_real_connect的第三个参数root是MySQL的用户名第四个参数root是密码第五个bookinfo是数据库名——这些都得和你自己电脑上MySQL的设置一致。如果你安装MySQL时设置的密码不是root这里一定要改否则会一直报连接失败。还有一个新手很容易踩的坑在Visual Studio或者Dev-C里编译器可能找不到mysql.h这个头文件。这不是你的代码有问题而是你没告诉编译器MySQL的安装目录在哪里。需要在项目属性里把MySQL的include目录加进附加包含目录把lib目录加进附加库目录并且在链接器里加上libmysql.lib。这一步每个环境设置方法不一样卡住了就搜一下你的编译器名字 配置mysql.h基本都能解决。2.4 为什么选择MySQL而不是文件存储有同学可能会问我用C语言自己写个文件把书的信息存进去不就行了为什么非要用数据库这个问题问得很好。自己写文件存取一开始确实简单但你会很快遇到三个问题第一查询麻烦文件里存了一千本书想找一本叫《C语言》的书你得把整个文件读一遍一行一行比对第二多人同时操作会乱一个人借书的时候另一个人也在借很容易写坏数据第三没有现成的撤回机制手滑删错了就没了。MySQL这些事都已经帮你做好了有索引让查询变快有行锁让并发安全有事务让你可以后悔。你用C语言只需要拼SQL语句发过去剩下的事数据库替你扛。这也是为什么真实项目里几乎没有人用普通文件存核心业务数据。3. 跑通第一个例子连上数据库把书单打印出来3.1 准备工作先建好book表在写C语言代码之前先在MySQL里把表建好。打开你的MySQL命令行或者图形工具执行下面这段SQLcreate database if not exists bookinfo; use bookinfo; create table if not exists book ( bid int primary key, bname varchar(50), bauthor varchar(50), bstate varchar(10) ); -- 插入几本测试数据 insert into book values (1, C语言程序设计, 某导师, 未借出); insert into book values (2, 数据结构, A同学, 已借出); insert into book values (3, 数据库原理, 某导师, 未借出);varchar(50)表示最多存50个字符的字符串书名和作者一般够用。primary key的意思是bid是主键不能重复。测试数据随便写重要的是让你查询的时候能看到东西。你要注意项目里有一张userinfo表是存账号密码的登录时用的就是它。建表语句大概是create table userinfo (uid int, upwd varchar(20))然后在里面插入一条管理员记录比如insert into userinfo values (1001, 123456)不然登录那一步永远会提示失败。3.2 查询全部图书的完整代码建好表之后我们来写一个真正能干事儿的程序连接数据库查询所有图书一行一行打印出来。#include stdio.h #include mysql.h int main() { MYSQL *pconn mysql_init(0); if (!mysql_real_connect(pconn, localhost, root, root, bookinfo, 0, 0, 0)) { printf(连接失败%s\n, mysql_error(pconn)); return 1; } mysql_query(pconn, set names gbk); // 构造SQL语句 char query[100]; sprintf(query, select * from book); // 执行查询 if (mysql_query(pconn, query)) { printf(查询失败%s\n, mysql_error(pconn)); return 1; } // 获取结果集 MYSQL_RES *res mysql_store_result(pconn); if (res NULL) { printf(获取结果失败%s\n, mysql_error(pconn)); return 1; } // 一行一行读取并打印 MYSQL_ROW row; while ((row mysql_fetch_row(res)) ! NULL) { int num_fields mysql_num_fields(res); for (int i 0; i num_fields; i) { printf(%8s , row[i]); } printf(\n); } // 释放结果集 mysql_free_result(res); mysql_close(pconn); return 0; }这段代码的流程是先连接再sprintf把SQL语句拼进字符数组然后mysql_query发出去。如果查询成功MySQL会把结果放在一个结果集里mysql_store_result把这个结果集从数据库那边搬到程序这边。mysql_fetch_row每次调用取出一行返回的是一个字符串数组rowrow[0]就是第一个字段bidrow[1]是bname以此类推。你可能会奇怪为什么bid是数字取出来却变成了字符串因为mysql_fetch_row返回的数据全部以字符串形式存在哪怕你在SQL里写的是int它也会转成字符。打印的时候直接%s输出就没问题。3.3 这段代码里最容易改错的地方第一个容易改错的是sprintf的缓冲区大小。代码里char query[100]如果SQL语句特别长比如搜索条件很多可能装不下。保险一点可以改成char query[256]反正内存不差这点。第二个容易改错的是mysql_store_result和mysql_use_result的区别。mysql_store_result会把所有结果一次加载到内存里适合数据量小的情况因为后续可以随机跳转、也可以反复读取mysql_use_result是一行一行从数据库取省内存但取的时候不能同时执行其他查询。这个项目里数据量很小用store_result更顺手。第三个是释放。每次mysql_store_result之后用完了一定要mysql_free_result(res)释放掉。否则程序反复查询几十次内存就会被这些不用的结果集占满表现出来就是程序越跑越慢最后卡死。这个习惯要从一开始就养成。3.4 怎么验证你跑通了程序运行后如果屏幕上出现这样类似的内容就说明你已经成功迈出了第一步1 C语言程序设计 某导师 未借出 2 数据结构 A同学 已借出 3 数据库原理 某导师 未借出能看到这个表格意味着你的C语言环境、MySQL环境、数据库连接、SQL查询这四个环节全部打通了。后面的借书、还书、搜索本质上都是在这段代码的基础上改SQL语句而已。4. 动手实现借书、还书和搜索功能4.1 借书一条update语句的事借书的本质是什么就是把这本书的bstate从未借出改成已借出。在C语言里你只需要让用户输入一个图书编号然后拼一条SQL语句发出去。int bid1; printf(请输入图书编号); scanf(%d, bid1); // 构造SQL把这本书的状态改成已借出 char jieyue[200]; sprintf(jieyue, update book set bstate已借出 where bid %d, bid1); if (mysql_query(pconn, jieyue)) { printf(借阅失败错误为%s\n, mysql_error(pconn)); } else { printf(借阅成功\n); }update语句的格式是update 表名 set 字段名新值 where 条件。这里的where bid %d表示只改编号等于用户输入值的那一行。注意%d会被bid1的值替换所以你输入的是几就改哪本书。有人可能会问如果用户输入的编号根本不存在怎么办那就什么都改不到但mysql_query依然返回0程序会提示借阅成功。这是个逻辑漏洞。更严谨的做法是先用select查一下这本书存不存在再决定要不要执行update。初学者可以先不管这一步但要心里有数。4.2 还书和借书几乎一模一样还书的逻辑就是把状态改回来。借书是改成已借出还书是改成未借出其他完全一样。int bid2; printf(请输入图书编号); scanf(%d, bid2); char guihuan[200]; sprintf(guihuan, update book set bstate未借出 where bid %d, bid2); if (mysql_query(pconn, guihuan)) { printf(归还失败错误为%s\n, mysql_error(pconn)); } else { printf(归还成功\n); }你发现没有借书和还书的区别只是SQL语句里那个字符串值不同。这提示你一个优化方向完全可以把这两段合成一个函数传入一个状态参数代码量直接减半。不过新手阶段先把重复的代码写一遍找找感觉不用急着优化。4.3 借书前应该检查状态原项目里有一处逻辑是想做检查图书是否已借出但写法上有问题。正确的思路是这样// 先查这本书当前的状态 char sql[200]; sprintf(sql, select bstate from book where bid %d, bid1); mysql_query(pconn, sql); MYSQL_RES *res mysql_store_result(pconn); MYSQL_ROW row mysql_fetch_row(res); if (row ! NULL strcmp(row[0], 已借出) 0) { printf(此书已借出\n); mysql_free_result(res); // 回到主菜单不执行借书操作 } else { // 执行update借书 mysql_free_result(res); }这里strcmp是比较两个字符串如果相等返回0。row[0]就是bstate字段的值。把先查询、再判断、最后决定是否更新这个过程走完才算一个完整的借书逻辑。很多新手会跳过查询直接update结果就是同一本书被借走两次数据库里出现两个人都借了同一本书的荒唐局面。4.4 搜索图书模糊查询like的用法查询功能分成两块查全部和按关键词搜索。查全部你已经会了按关键词搜索用的是SQL的like语句。char temp[20]; char query[200]; printf(请输入搜索图书信息); scanf(%s, temp); // 构造模糊查询书名里包含关键字就匹配 sprintf(query, select * from book where bname like %%%s%%, temp); if (mysql_query(pconn, query)) { printf(查询失败%s\n, mysql_error(pconn)); } else { MYSQL_RES *res mysql_store_result(pconn); MYSQL_ROW row; while ((row mysql_fetch_row(res)) ! NULL) { for (int i 0; i mysql_num_fields(res); i) { printf(%8s , row[i]); } printf(\n); } mysql_free_result(res); }这里的%%%s%%看起来有点吓人其实拆开就明白了SQL语句里的%是通配符表示任意长度的字符。like %关键字%意思是包含关键字就行。但在C语言的sprintf里%是特殊字符想输出一个真正的%必须写成%%所以%%%s%%翻译过来就是% 用户输入的内容 %。比如用户输入程序实际发给MySQL的语句就是select * from book where bname like %程序%于是《C语言程序设计》就会被查出来。4.5 把功能串起来菜单循环项目里用goto实现操作完回到菜单继续选的功能。虽然老师常说goto要少用但在这个小项目里它确实简单直接每次执行完一个casegoto跳回菜单标签处重新打印提示等待用户选择。ll: printf(请选择功能1-借阅图书 2-归还图书 3-查询图书 4-继续操作 5-退出系统); int x; scanf(%d, x); switch (x) { case 1: // 借书逻辑 goto ll; case 2: // 还书逻辑 goto ll; case 5: printf(退出成功\n); return; }注意case 4在项目里写的是继续操作也就是什么事都不干直接跳回菜单本质上是给用户一个刷新界面的机会。这个设计谈不上优雅但作为新手练习理解它的意图就够了。等你熟练之后可以考虑用while(1)循环加布尔变量替代goto代码会更易读。5. 新手最容易翻车的几个坑5.1 连接数据库一直失败现象程序一运行就打印连接失败错误信息可能显示Access denied for user rootlocalhost或者Cant connect to MySQL server。原因最常见的有三种。第一MySQL服务没启动Windows下可以在服务管理里查看MySQL服务状态第二账号密码不对你安装MySQL时设置的是别的密码代码里却写了root/root第三数据库名写错了比如代码里写bookinfo但你的库叫book_info。解决先用MySQL命令行工具手动试试mysql -u root -p输入密码能进去说明账号密码没问题。再执行show databases;看看bookinfo是否存在。逐步排查哪一步过不去就修哪一步。这招屡试不爽。5.2 查询出来的中文全是乱码现象书名叫《C语言程序设计》打印出来却是一堆???或者完全看不懂的符号。原因MySQL服务器端、客户端、C程序这三者的字符编码不一致。你的控制台默认可能是GBK数据库表默认可能是utf8两边的编码对不上中文就变成乱码。解决在连接成功之后、执行任何SQL之前先执行mysql_query(pconn, set names gbk)。这句SQL会告诉MySQL接下来的会话请用GBK编码。如果你发现GBK还乱可以把gbk改成utf8同时把控制台的代码页也改成支持UTF-8的。这个先设置编码再干活的顺序很重要千万别省。5.3 借书不看状态同一本书被借两次现象一本书已经被A借走了B输入同样的编号还能借成功。原因代码只执行了update把状态改成已借出却没有在更新之前select一下这本书当前是什么状态。如果它已经是已借出应该直接拒绝。解决严格按照先查询、再判断、后更新的顺序写。查到row[0]的内容等于已借出就打印提示并且不执行update。这个逻辑虽然多写几行代码但它让系统变得真实可用。5.4 忘记释放结果集程序越跑越卡现象功能都能用但连续操作几十次之后程序反应明显变慢甚至直接卡死。原因每次查询后mysql_store_result拿到的结果集都占内存你只用了mysql_fetch_row读数据用完没有调用mysql_free_result释放。循环往复内存被吃光。解决养成一个条件反射只要代码里出现mysql_store_result或mysql_use_result就必须在读取完数据后、进入下一次操作之前写一行mysql_free_result(res)。哪怕查询结果为空也要释放。这是C语言程序员的基本功——自己申请的资源自己负责释放。5.5 登录验证的SQL拼接踩坑现象输入正确的账号密码登录失败或者输入1 or 11这种内容竟然能登录成功。原因登录功能用的是sprintf(sql_login, select * from userinfo where uid%d and upwd%d, account, pwd)把用户输入直接拼进SQL语句。如果输入的内容里含有SQL特殊字符就可能改变整条语句的含义这就是常说的SQL注入。虽然是在自己机器上练习但这个坏习惯一旦带到工作里会很危险。解决新手阶段至少要明白两点一是账号密码不要直接用%d去拼因为账号可能包含字母二是如果用%s拼接字符串用户输入里如果有引号SQL就会出错。最稳的办法是先用select查出来再比对或者学习使用MySQL的预处理语句。这个项目你先把功能跑通再慢慢研究怎么防注入。5.6 用goto导致代码跳来跳去看不懂现象程序功能正常但你想在某个case后面加一段新功能改来改去发现逻辑越来越乱一不小心就跳过了某一步。原因goto可以跳转到函数内任意标签处这给了你极大的自由但自由过头就是混乱。比如从一个case直接跳回菜单中间有些代码的收尾工作就被跳过了。解决小项目里用goto确实省事但建议你每写一个goto就想想如果改用while循环是不是也能达到同样效果当你觉得goto开始让你头疼时说明你的代码复杂度已经上来了是时候重构了。6. 学完这个系统下一步学什么这个图书管理系统你已经能跑通了接下来别急着删代码根据你现在的能力选一条路继续走。第一把登录部分改成真正能防注入的写法。现在用的是拼接SQL字符串你可以改成先用select * from userinfo where uid ?搭配预处理语句。这个知识点在真实项目里是必考的。第二把goto结构重构掉改成函数化设计。比如让userfunction和adminfunction各自返回一个状态码主函数根据状态码决定继续还是退出代码会清爽很多。第三学一学如何把数据从数据库里读进结构体数组然后用链表管理。这个系统目前是查一条打一条数据没有在内存里留下来。如果要做先读到内存、再统一处理你就需要结合C语言的结构体和链表知识。这是C语言进阶的一个关键台阶。第四有余力的同学可以试试给系统加上借书期限字段或者加上逾期未还的提示。这一步要用到MySQL的日期函数会让你的系统更像真实产品。我给一个具体建议把用户表userinfo里的密码从明文改成MD5加密后的值然后在C语言里调用MySQL的md5()函数做比对。这条路径能让你真实体验密码不能直接存明文这个业界铁律也能让你对数据库函数有个初步认识。从一个只会打印星星的初学者到能写一个连数据库的小系统这中间的跨度你已经走完了。想起我第一次写这个系统的时候卡在mysql.h找不到这个坑上整整两天后来才发现是编译器没配好库目录。从那以后我每次用第三方库之前都会先把头文件和库目录的配置确认一遍再开始写代码再也不让环境问题白耗时间了。希望这一点经验也能帮到你踩坑不可怕重要的是踩过一次就别再踩第二次。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑