简介《使用C语言控制台结合MySQL数据库实现简单的图书管理系统》面向C语言与数据库入门学习者聚焦控制台交互、MySQL C API连接及基础SQL增删改查练习。文档围绕会员借阅、归还、查询与管理员添加、修改、删除图书等流程展开涉及mysql_query、mysql_use_result、mysql_fetch_row等接口调用并呈现状态判断、结果集释放和循环菜单等实现细节适合作为课程设计或自学练手参考。压缩包共1个doc文件约137KB内容以项目源码与实现说明为主便于直接查看和对照理解。已有1574人学习能帮助读者梳理C语言与MySQL结合开发的基本思路掌握图书状态更新、模糊搜索及错误信息处理等常见做法。1. 先搞懂一件事C 语言 MySQL 图书管理系统到底是什么很多人学完 C 语言的循环和数组以为自己已经入门了结果课程设计题目一发下来就懵做一个 C 语言 MySQL 图书管理系统。每个语法都认识凑在一起却不知道从哪下手。其实它没那么吓人就是一个跑在黑色控制台窗口里的记账本C 语言负责跟人对话问你“要借哪本书”MySQL 负责把书名、作者、借没借出去存好下次打开还在。整套系统只有两个身份、五六个功能、几百行代码一个初学者几个晚上就能跑起来。这份资源适合刚学完 C 语言基础、想做第一个完整项目、又不想一上来就啃框架的初学者。后面按“它是什么 → 为什么学 → 环境怎么搭 → 第一个例子怎么跑”的顺序走。2. 为什么零基础值得学它一个作业练透三件基本功2.1 它把“会写语法”变成“会做一个东西”学 C 语言最难受的阶段是语法都会、题目也能做但给你一个空白文件你不知道第一行该写什么。这不是笨是缺一次“从零到能跑”的完整经历。这个项目正好补上这一段。它有一个清晰的骨架程序启动 → 连数据库 → 让人登录 → 根据身份进入不同菜单 → 循环处理操作 → 退出。你要做的不是发明架构而是把这个骨架一行行填满。填的过程里你会反复用到if、switch、数组、字符串、函数但这次它们不是练习题里的孤立语法而是真有用途的零件。我第一次做类似的东西时最大的收获不是学会了某个函数而是第一次体会到“程序是一层层搭起来的”。先让它能编译再让它能连上再让它能查出一行数据最后才加借书还书。每一步都很小每一步都能看到结果。这种“小步可验证”的习惯比多背十个函数名有用得多。对初学者来说最怕的是一上来就面对几千行的陌生代码。这个项目的规模刚好相反——你可以把整个文件从头读到尾能看懂主流程在干什么。这种“我居然能读完一个完整项目”的确定感是继续往下学的重要燃料。2.2 一个作业同时练三样以后天天要用的技能表面上看这是个 C 语言作业。实际上它把三样东西拧在了一起第一样是 C 语言本身。输入输出、字符串拼接、条件分支、循环、函数拆分全都在用。尤其是字符串拼接你会反复写sprintf把用户输入的数字和一段 SQL 文本拼成一句话这个过程会让你真正理解“程序里的字符串就是一段字符序列”。第二样是 SQL。你不用学完整个数据库课程只要会四条语句就能把系统跑起来select查、insert加、update改、delete删。这四条刚好对应图书管理里最核心的四个动作——查询、上架、借还、下架。学 SQL 最怕的是脱离场景背语法而这里每条语句都有明确的业务理由为什么这里用update不用insert因为书已经存在只是状态变了。第三样是“程序怎么跟数据库说话”也就是数据库接口。这部分是最容易被忽略、也最容易卡住的。你得知道先初始化、再连接、再发语句、再取结果、最后释放结果集。顺序错了就跑不通。而这套顺序换成别的语言、别的数据库思路几乎一样。三样东西分开学是三门课拧在一个能跑的小系统里就变成了一次完整的开发体验。对零基础的人来说这种“知识立刻有用”的反馈特别重要它能把“我学这个干嘛”的疑问直接消掉。2.3 代码规模刚好小到能一眼看完很多教程项目的问题不是太难而是太“全”。一上来就是分层架构、配置文件、依赖管理新手光是弄清楚文件之间的关系就要花掉一周。这个项目不是。它就是一个.c文件几个函数一个main。规模小带来三个好处。一是你能读完。从头到尾读一遍最多半小时读完之后你脑子里有一张完整的地图哪里是登录哪里是会员菜单哪里是管理员菜单。有了地图改起来才有方向。二是出错好定位。程序跑飞了你能很快判断是连不上数据库、还是 SQL 写错了、还是打印的时候格式串不对。大项目里一个错误可能藏在五层调用之下这里不会。三是改造成本低。想加一个“按作者搜索”你只需要照着现有的搜索功能复制一份把bname改成bauthor。这种“照着改就能跑”的正反馈是新手建立信心最快的方式。当然小也有小的代价。比如原代码里用了goto做循环用了固定长度的字符数组拼 SQL账号密码是明文整数比对。这些写法在真实项目里都不合适。但对入门阶段来说先跑通比先优雅重要。等你把功能跑通了再回头把这些地方一个个换成更好的写法那才是真正有效的学习——因为你已经知道它为什么要改了。2.4 你会第一次面对“数据不归程序管”这是我觉得这个项目最值钱的一点也是最容易被新手忽略的一点。以前写程序数据都在内存里。程序一关数据就没了。你写个学生成绩统计跑完就结束没什么感觉。但这个项目不一样图书信息存在数据库里程序关掉再打开书还在。这个变化看起来小实际上改变了你思考问题的方式。你会开始想这条数据是谁改的两个人同时借同一本书会怎样如果程序在“改成已借出”之后崩了数据会不会不一致原代码里其实已经碰到了这个问题。借书的时候它先查这本书的状态如果已经是“已借出”就提示不能再借否则才执行更新。这个“先查再改”的动作就是在处理数据的一致性。虽然它的实现有 bug第 5 章会讲但思路是对的。理解“程序只是数据的操作者数据本身独立存在”是从写练习题跨到写应用的关键一步。跨过去之后你再看到任何系统都会自然地问一句它的数据存在哪谁在改3. 动手前的准备数据库和表先搭起来3.1 装三样东西环境就算齐了在写第一行代码之前机器上得有三样东西备齐。缺一样后面都会卡住。第一样是 C 语言编译器。Windows 上最常见的是 MinGW 那一套装完之后在命令行里敲gcc -v能看到版本号就说明通了。如果你用的是某个集成开发环境那一般自带编译器确认一下能编译一个空的main函数就行。第二样是 MySQL 数据库服务。装的时候会让你设一个 root 账号的密码这个密码记住后面连数据库要用。装完之后确认服务是启动状态——最直接的验证方式是打开命令行客户端输入密码能进去看到mysql提示符。第三样是 MySQL 的 C 语言开发文件。这部分最容易被漏掉很多人装了数据库却发现编译器说找不到mysql.h。原因是数据库服务端和开发用的头文件、库文件是分开的。头文件里声明了mysql_init、mysql_query这些函数的模样库文件里才是它们的实际代码。你编译的时候要告诉编译器这两样东西在哪。提示头文件目录一般叫include库文件目录一般叫lib。装完之后去安装目录下看一眼能找到mysql.h和libmysql.dll/libmysql.lib这类文件就说明开发文件齐了。装好之后先别急着写代码。花两分钟做一件事把这三样东西的路径都记在一个文本文件里。后面配置编译参数的时候你会反复用到现场去找很浪费时间。3.2 建库建表两张表、几个字段就够这个系统的数据结构简单到可以用一句话说清一张表存书一张表存人。书需要知道编号、书名、作者、借没借出去。人需要知道账号和密码。没了。不需要分类、不需要出版社、不需要借阅记录入门阶段先把主干跑通。打开命令行客户端登进去之后依次执行-- 建一个库名字叫 bookinfo和后面 C 代码里的库名保持一致 create database bookinfo default charset utf8mb4; use bookinfo; -- 图书表bid 编号、bname 书名、bauthor 作者、bstate 借阅状态 create table book ( bid int primary key, bname varchar(50), bauthor varchar(50), bstate varchar(20) default 未借出 ); -- 用户表uid 账号、upwd 密码 create table userinfo ( uid int primary key, upwd int ); -- 塞几条测试数据让程序一跑就有东西可查 insert into book values (1, C语言基础, 某作者, 未借出); insert into book values (2, 数据结构基础, 某作者, 未借出); insert into userinfo values (1001, 123456); insert into userinfo values (1002, 111111);几处需要解释一下。bid int primary key里的primary key是主键意思是这一列的值不能重复也不能为空。图书编号本来就该唯一加了这个约束数据库会帮你挡住重复插入。varchar(50)表示最多存 50 个字符的变长字符串。书名用 50 够用了别一上来就开varchar(255)那是浪费。default 未借出是一个默认值。新加一本书如果没指定状态就自动是“未借出”省得每次插入都写一遍。用户表里uid 1001那条在这个系统里被当作管理员的约定账号。原代码里就是硬编码判断account 1001来决定进管理员菜单还是会员菜单。这不是好设计但入门阶段先接受它第 6 章会讲怎么改。3.3 先在命令行把 SQL 跑通再回 C 里写这一步很多人会跳过结果在后面浪费大把时间。血泪经验SQL 本身写错了C 代码里报的错会含混得多你会以为是连接问题、编码问题、库没装对其实是where后面的字段名拼错了。所以养成一个习惯任何要写进 C 代码的 SQL先在命令行里跑一遍确认结果对再复制进去。试这几条-- 1. 查全部应该能看到刚才插的两本书 select * from book; -- 2. 按编号查状态借书前的状态检查就是这么来的 select bstate from book where bid 1; -- 3. 改状态借书动作的本质 update book set bstate 已借出 where bid 1; -- 4. 再查一次确认真的改掉了 select * from book where bid 1; -- 5. 模糊搜索按书名里的关键字找 select * from book where bname like %语言%;第 5 条里的%是通配符代表任意多个字符。%语言%的意思是“名字里任何位置出现‘语言’两个字都算”。这个写法在 C 代码里会被拼成like %%%s%%看起来奇怪是因为sprintf里的%%要转义成一个真正的%剩下的%s才是书名占位。第一次看到这个写法基本都会愣一下记住这个规律就行。注意跑完第 3 条之后第 1 号书的状态变成“已借出”了。等下测试程序的时候记得把它改回“未借出”不然一测借书就提示“已借出”会以为是代码有问题。3.4 把 mysql.h 和库文件连进编译器这是新手最容易卡住的一步也是报错最吓人的一步。典型症状是编译时报mysql.h: No such file or directory或者编译过了但链接时报一堆undefined reference to mysql_init。两个报错原因不同解决方式也不同。找不到头文件是因为编译器不知道去哪找。用-I参数告诉它头文件目录。链接时报未定义是因为编译器找到了函数声明但没找到函数实现用-L指定库目录、用-l指定库名。命令行编译大概长这样# -I 后面跟头文件所在目录-L 后面跟库文件所在目录-l 后面跟库名 gcc demo.c -o demo \ -I 你的MySQL安装目录/include \ -L 你的MySQL安装目录/lib \ -llibmysql三个参数逐个说清。-I大写 i是 include 路径告诉编译器“如果代码里写了#include mysql.h去这个目录找”。路径里有空格的话记得加引号。-L是 library 路径告诉链接器去哪找库文件。这一步在编译阶段不报错只在链接阶段起作用所以很多人以为配置成功了一编译才发现问题。-l小写 L是库名。注意写法是-llibmysql而不是-l libmysql.lib-l后面直接跟库名编译器会自动补上lib前缀和扩展名。这个细节坑过不少人。如果你用的是集成开发环境思路一样找到“包含目录”和“库目录”两个设置项分别填进去再在“链接库”里加上库名。配置完之后把生成的可执行文件需要的动态库文件.dll复制到可执行文件旁边或者在系统路径里加一下不然运行时可能提示找不到动态库。还有一个容易忽略的点原代码开头有#includewinsock.h而且写在mysql.h前面。这不是随便写的在 Windows 下某些 MySQL 客户端库依赖网络相关的类型定义顺序反了会编译不过。但这个图书管理系统本身并不做网络通信所以这个头文件属于历史遗留先照着保留就行别去改它。4. 第一个能跑的例子从连上数据库到查出一行书4.1 十行代码验证能不能连上别急着把整套系统抄进去。第一步只做一件事连上数据库打印一句“连接成功”。这一步跑通了后面所有功能才有意义。新建一个文件写下面这段#include stdio.h #include mysql.h int main() { // 1. 初始化一个连接句柄相当于先把电话机拿在手里 MYSQL *conn mysql_init(0); // 2. 真正拨号地址、账号、密码、库名后面四个参数先不管 if (!mysql_real_connect(conn, localhost, root, 你的密码, bookinfo, 0, 0, 0)) { printf(连接失败%s\n, mysql_error(conn)); return 1; } printf(连接成功\n); // 3. 告诉数据库这次通讯用哪套字符集不然后面中文可能变问号 if (mysql_query(conn, set names gbk)) { printf(设置编码失败%s\n, mysql_error(conn)); } // 4. 用完关掉别让连接一直挂着 mysql_close(conn); return 0; }逐句解释。mysql_init(0)返回一个MYSQL *指针你可以把它理解成这次连接的身份证。后面所有的查询、取结果、关闭都要拿这个指针当第一个参数。参数传 0 表示用默认配置入门阶段不用关心别的选项。mysql_real_connect有八个参数前五个是关键连接句柄、主机地址、账号、密码、数据库名。后面三个是端口、套接字和客户端标志传 0 就是默认值。返回值如果是 0也就是 NULL说明连接失败。所以判断条件写成!mysql_real_connect(...)——返回空就进入失败分支。失败的时候一定要打印mysql_error(conn)。这个函数会返回一句人能看懂的错误说明比如密码错、库不存在、服务没启动。新手最常见的错误是只打印“连接失败”四个字然后自己猜半天。set names gbk这条语句是设置客户端和服务端之间的字符集。原代码用的是 gbk因为在 Windows 控制台里gbk 和默认代码页对得上中文能正常显示。如果你的环境是 utf8就改成set names utf8。这一步放在连接成功之后、任何业务查询之前顺序不能反。mysql_close是释放连接。程序结束前调用一下是个好习惯。编译运行的命令在 3.4 节已经给过了。跑起来看到“连接成功”就可以往下走了。提示如果这里就失败了先别改代码。回到命令行客户端用同样的账号密码登一次。命令行能登进去而程序登不进八成是密码里含特殊字符没处理或者库名拼错了。4.2 逐句拆mysql_init 与 mysql_real_connect 到底做了什么上一节的代码能跑但我想把这两个函数再拆细一点因为后面所有功能都建立在它们之上理解透了能省很多事。mysql_init干的事情很朴素在内存里准备好一个结构体用来存放连接状态、字符集、错误信息这些东西。它不联网也不验证账号。所以哪怕你把mysql_init写在main第一行程序也不会卡住。真正耗时、真正可能失败的是下一步。mysql_real_connect才是“拨号”。它会做几件事建立到数据库服务的连接、验证账号密码、切换到指定数据库、协商协议版本和字符集。任何一步失败它都返回空指针并把原因写进句柄里。这就是为什么失败之后要用mysql_error(conn)去取——错误信息存在那个句柄里不是全局变量。这里有个新手常犯的误区以为连接成功就万事大吉后面不用管了。实际上连接是会断的。数据库服务重启、网络抖动、连接空闲太久被服务端回收都会让你手里的句柄失效。这时候再去查询返回的是“MySQL server has gone away”之类的错误。入门阶段不用做复杂的重连逻辑但要养成一个意识每次执行查询之后都要检查返回值。原代码里到处都在写if (mysql_query(...)) { printf(失败:%s\n, mysql_error(pconn)); }看着啰嗦其实是必要的防御。少了这一层出问题的时候你只知道“没成功”不知道“为什么没成功”。还有一个细节mysql_init(0)里的 0 是“用默认参数”的意思。如果你以后要做多线程或者需要指定字符集可以传一个初始化过的MYSQL结构体进去。入门阶段保持 0 就够。4.3 从表里读出一行书连上之后第一个真正的功能是查。查询是这套系统里用得最多的操作查书的全部列表、按关键字搜、借书前查状态、登录时查账号。把这一个动作吃透剩下的都是换 SQL 语句。#include stdio.h #include mysql.h int main() { MYSQL *conn mysql_init(0); if (!mysql_real_connect(conn, localhost, root, 你的密码, bookinfo, 0, 0, 0)) { printf(连接失败%s\n, mysql_error(conn)); return 1; } mysql_query(conn, set names gbk); // 1. 发一条查询语句出去 if (mysql_query(conn, select * from book)) { printf(查询出错%s\n, mysql_error(conn)); return 1; } // 2. 把结果从服务端搬到本地 MYSQL_RES *res mysql_store_result(conn); if (res NULL) { printf(取结果失败%s\n, mysql_error(conn)); return 1; } // 3. 一行一行取取到 NULL 说明取完了 MYSQL_ROW row; while ((row mysql_fetch_row(res)) ! NULL) { // mysql_num_fields 告诉我们这行有几列不用写死数字 for (int i 0; i mysql_num_fields(res); i) { printf(%10s, row[i] NULL ? NULL : row[i]); } printf(\n); } // 4. 结果集要还回去否则内存会一直涨 mysql_free_result(res); mysql_close(conn); return 0; }四步走发语句、取结果集、逐行读、释放。顺序不能乱。mysql_query负责把 SQL 字符串发给数据库。成功返回 0失败返回非 0。注意它只是“发出去”不负责把结果拿回来。结果还在服务端等着你去取。mysql_store_result把结果一次性全搬到本地内存。返回值是MYSQL_RES *可以理解成一张临时表格。如果 SQL 是insert、update这种没有返回行的语句这个函数会返回 NULL这属于正常情况不是错误。mysql_fetch_row每次取一行返回一个字符指针数组每个元素对应一列的值。取完了返回 NULL所以用while循环包起来这就是遍历的终止条件。注意它返回的都是字符串哪怕数据库里是int类型取出来也是1这样的字符串。要用数字的话得自己atoi转换——原代码里登录那里就是先mysql_fetch_row再用atoi(row[1])取用户编号。mysql_num_fields(res)返回这一行有几列。用它循环比写死for (i 0; i 4; i)好因为以后表加了字段代码不用改。mysql_free_result是必须的。结果集占用的是客户端内存不释放会一直累积。少数几次查询看不出来但系统里有个循环用户可以反复查询跑久了内存就一直涨。这类问题很难排查因为程序不报错只是越来越慢。还有一个和mysql_store_result对应的是mysql_use_result原代码在打印的时候用的就是后者。区别在于store一次性搬到本地之后可以随便遍历也能立刻发下一条查询use是边取边读内存占用小但在结果读完之前不能发新的查询。新手建议统一用mysql_store_result省心。4.4 把“登录”接上去第一个完整功能就通了查询跑通之后登录就很简单了——它本质上就是一次带条件的查询账号密码对得上就查出至少一行对不上就一行都查不出。int account, pwd; printf(请输入账号和密码); scanf(%d %d, account, pwd); // 拼一条带条件的查询账号和密码都对才算查到 char sql[200]; sprintf(sql, select uid from userinfo where uid %d and upwd %d, account, pwd); if (mysql_query(conn, sql)) { printf(登录查询失败%s\n, mysql_error(conn)); return 1; } MYSQL_RES *res mysql_store_result(conn); if (res NULL) { printf(取结果失败%s\n, mysql_error(conn)); return 1; } // 结果有几行大于 0 说明账号密码匹配上了 if (mysql_num_rows(res) 0) { printf(登录成功\n); // 这里接后续菜单原代码用 account 1001 判断是不是管理员 } else { printf(账号或密码不对\n); } mysql_free_result(res);scanf(%d %d, account, pwd)一次读两个整数中间用空格或回车隔开都行。这里必须传地址加因为scanf要把读到的值写回变量。sprintf(sql, ..., account, pwd)是字符串拼接。它把格式串里的%d替换成变量的值结果写进sql这个字符数组。拼完之后sql里就是一条完整的 SQL 语句。这是原代码里到处都在用的手法借书拼update、加书拼insert、搜索拼like。mysql_num_rows(res)返回结果集有几行。登录判断就是看它是否大于 0大于 0 说明where条件匹配到了记录账号密码对等于 0 说明没匹配上。登录成功之后原代码用if (account 1001)分流进管理员菜单或者会员菜单。这个写法很硬但作为第一版够用了。两个菜单各自的循环就是一层while或者goto标签加switch会员能借书、还书、查书管理员能加书、改书、删书、查书。写到这里整套系统的骨架就齐了。剩下的所有功能都是同一个套路的变体读输入 → 拼 SQL → 执行 → 检查返回值 → 提示结果 → 回到菜单。注意sprintf拼 SQL 的时候如果拼进去的内容里有单引号SQL 会被截断执行就报错。书名里带单引号的情况少见但不是没有。这是这个入门写法的已知短板第 5 章会讲怎么先绕过去。5. 避坑与排查新手最容易翻车的五个地方5.1 连上了但查出来的中文全是问号现象程序提示连接成功查询也能执行但打印出来的书名是一串问号或者乱码。原因字符集没对齐。MySQL 有服务端字符集、数据库字符集、表字符集、连接字符集好几层任何一层对不上中文就会在传输过程中被破坏。原代码在连接之后立刻执行set names gbk就是在设连接这一层。解决确认三处一致。命令行里执行show variables like character%;看一下当前字符集C 代码里连接成功后第一件事就执行set names gbk或者utf8取决于你的环境和控制台编码建表时用default charset utf8mb4。三处对齐之后中文就正常了。如果还不行检查源文件保存的编码是不是和控制台一致这一层最容易被忽略。5.2 输入一次之后后面的输入全乱了现象程序让你输账号你输完按回车下一个提示还没看清就自己跳过去了或者读到一堆奇怪的数字。原因scanf读整数时会把回车留在输入缓冲区里下一次读字符或者读字符串的时候先读到那个回车于是“什么都没输就过去了”。另一个常见原因是scanf(%s, book_name)给字符串数组多写了虽然很多时候碰巧能跑但类型不对行为不可靠。解决读字符串时去掉写成scanf(%s, book_name)因为数组名本身就是地址。如果混着读数字和字符可以在读字符前加一句getchar();把残留的换行吃掉。更稳的做法是统一用fgets读整行再自己解析但入门阶段先用前者够用。5.3 查询正常但程序越跑越卡最后直接崩现象反复查询几十次之后程序变慢或者某次查询突然失败提示连接不可用。原因结果集没释放或者mysql_store_result和mysql_use_result混用。前者会一直占用客户端内存后者在结果没读完之前就发新查询会导致协议错乱。原代码里就有混用的情况有的地方用store有的地方用use。解决定一条规矩——每次mysql_store_result或mysql_use_result之后必须跟一句mysql_free_result放在同一个函数的正常路径和失败路径上。新手建议统一只用mysql_store_result不用use能避开绝大多数坑。5.4 编译报找不到 mysql.h或者链接时报一堆 undefined reference现象一个说mysql.h: No such file or directory另一个是编译通过但链接阶段冒出十几行undefined reference to mysql_init。原因前者是头文件路径没配编译器不知道去哪找声明后者是库路径或库名没配对编译器找到了声明但找不到实现。这两个报错发生在不同阶段很容易混为一谈。解决分两步查。先解决头文件确认-I后面的路径下真的有mysql.h路径里有空格要加引号。再解决链接确认-L后面是lib目录-l后面是库名且不带lib前缀和扩展名。如果编译过了但运行时提示找不到动态库就把对应的.dll复制到可执行文件同目录下。5.5 书名里带单引号整个操作就报错现象加一本名字里带英文单引号的书程序提示“添加失败”错误信息里能看到 SQL 语法错误。原因sprintf拼出来的 SQL 里那个单引号把语句“截断”了。数据库看到的是一个没闭合的字符串自然报语法错。这就是常说的 SQL 注入问题的雏形。解决入门阶段先做一个简单过滤遇到单引号就提示不合法或者把它替换成两个单引号。等把功能跑通、想更进一步时再换成预处理语句——把 SQL 模板和数据分开传数据里的任何字符都不会被当成语法。这是第 6 章要讲的第一件事。6. 下一步学什么从“能跑”到“能改”的三步走程序能跑起来之后最危险的状态是“不敢动”。一改就崩崩了不知道怎么查于是原样交上去学到的也就停在原地。接下来这三步是我建议的改造顺序每步只改一个地方改完都能跑通再往下。阶段目标具体做的事大概花多久第一步把goto换成正常循环把ll:和kk:标签改成while (1)加break菜单逻辑不变一个晚上第二步把拼 SQL 换成预处理语句用mysql_stmt_init系列函数把%d、%s换成参数占位两三个晚上第三步把功能拆成函数借书、还书、查询各写成一个函数main只留调度一个周末第一步先做因为它是纯体力活不涉及新知识但能立刻降低阅读难度。改的时候注意switch里每个case结束要加break否则会“贯穿”到下一个分支。原代码里就有case后面漏了break的情况改的时候顺手补上。第二步是真正长本事的地方。预处理语句的思路是先准备一个 SQL 模板里面用?占位然后把变量的值单独绑上去。这样数据永远只是数据不会被当成语法。学这一步不用一口气全改先挑“添加图书”这一个功能改改通了再推广到别的。我当初就是先改了一个跑通之后剩下几个半小时就改完了。第三步是把一坨代码变成能维护的结构。具体做法是每个功能一个函数函数只负责自己那一段公共的东西比如执行查询并打印结果抽出来复用。改完之后你会发现main变得很短一眼能看清整个程序的流程。这一步对以后学任何语言都有用。顺便说两个可以自己加的小功能用来检验是不是真懂了给书加一个“按作者搜索”照着现有的书名搜索复制一份把字段名换掉就行再给借书加一个“借出时间”字段涉及改表结构和改 SQL能练到手。这两个都是照着现有代码改难度不大但能验证你对整条链路的理解。还有一句血泪教训要说每次只改一个地方改完立刻编译运行。我见过太多人一口气改五个功能然后对着一个错误猜半天。从那以后我每次接一个小项目都会先把“连接—查询—释放”这三步单独跑通再往里加功能从来不敢跳过。希望帮到你。本文还有配套的精品资源点击获取
企业数字化 ERP 产品动态
相关推荐
基于WMSST和MCNN-GRU的网络故障诊断方法 1. 项目背景与核心价值网络故障诊断一直是运维领域的痛点问题。传统基于阈值告警或简单统计分析的方法,在面对复杂网络环境时往往存在误报率高、定位精度低的问题。这个项目提出了一种融合WMSST(加权多尺度样本熵)和MCNN-GRU(多尺… · 2026/11/3 8:08:04
Agentic AI Infra:智能体工程化落地的关键基础设施 云栖2026那几天,我翻了翻各个分论坛的议题列表,发现一件很有意思的事:纯粹讲模型参数、讲训练技巧的场次明显少了,几乎每个技术专场都在反复出现同一个词——Agentic AI Infra。不是说模型不重要了,而是大家默认了一个… · 2026/11/3 8:08:04
二维均匀分布与随机变量独立性判定全解析 1. 二维均匀分布的核心概念解析二维均匀分布是概率论中最基础的连续型联合分布之一,也是理解多元随机变量关系的绝佳切入点。在实际教学中,我发现许多同学对"均匀"二字的理解仅停留在表面,这直接影响了后续独立性判断的准确性。二维… · 2026/11/3 8:08:04
项目申请书配图指南:从规划到避坑,提升评审通过率 每年一到申报季,后台总有人问我同一个问题:本子写得挺顺,评审专家翻到第三页就皱眉,问题到底出在哪。我通常会先要一份他们的申请书PDF,翻一遍配图,答案基本就浮出水面了。文字部分再扎实,图一旦… · 2026/11/3 8:40:29
高校实验室设备报修系统开发实践 1. 项目背景与核心需求 人工智能实验室设备管理一直是高校信息化建设中的痛点。传统报修方式存在响应慢、流程不透明、数据难追溯等问题。我们实验室去年就遇到过GPU服务器故障导致科研项目延期的情况——从打电话报修到工程师上门整整花了3天时间,期间完全不知道维… · 2026/11/3 8:40:29
智能体技术在能源效率优化中的应用与实践 1. 能源效率优化与智能体的天然契合点 去年冬天参与某工业园区能效项目时,我亲眼目睹了传统SCADA系统在应对复杂能源调度时的无力——当光伏发电骤降、生产线负载突变同时发生时,操作员面对十几块闪烁的屏幕根本来不及做出最优决策。这正是智能体技术大显… · 2026/11/3 8:40:29
自动驾驶平行泊车优化:MPC与纯跟踪算法融合实践 1. 项目背景与核心价值 平行泊车是自动驾驶领域最具挑战性的场景之一。在仅比车身长约1.2-1.5倍的狭窄空间内完成精准泊入,需要解决路径规划、运动控制和实时纠错三大核心问题。传统方法如几何轨迹规划配合PID控制,在复杂场景下容易产生较大跟踪误差。这… · 2026/11/3 8:40:29
企业级Data Agent技术选型与架构设计:从NL2SQL到Agent编排的落地实践 1. 市场主流 Data Agent 的技术路线全景 Data Agent 这个词在 2025 年到 2026 年之间被反复提及,但真正落到技术选型和架构设计层面,能讲清楚的人并不多。我过去大半年时间一直在做企业级数据智能产品的架构评审和选型咨询,前前后后深度拆解过… · 2026/11/3 8:40:29
Python安全工具AI化:SSH日志分析与Linux审计技能封装 1. 项目背景与核心目标最近在整理服务器安全审计工具时,发现之前用Python写的SSH日志分析脚本分散在各个服务器上,每次使用都需要手动复制粘贴。正好接触到QClaw这个AI安全分析平台,它支持通过自定义skill扩展功能。于是萌生想法:… · 2026/11/3 8:39:29
双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/2 13:10:28
STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 6:04:24
DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 5:52:46
STM32F411RE与MAX14431实现SPI隔离:工业现场抗共模干扰实战 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 1:01:59
ESP32+大模型AI硬件开发:从Demo到产品的8个工程问题 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 1:01:59
基于MAX14431BAEE+与PIC18F85J10的SPI隔离方案设计与实践 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 1:01:59
双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/2 13:10:28
STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 6:04:24
DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 5:52:46