零基础学C语言,12年老工程师写的通俗电子书
各位好我乃良许是一位老工程师在嵌入式这个领域深入钻研长达 12 年之前曾是跻身世界 500 强企业的高级工程师。我耗费了 3 个月时长撰写了一本 C 语言电子书运用极为通俗易懂的言语向大家讲解 C 语言将复杂的技术阐释得连小学生都能够听得明白绝非是 AI 生成的那种晦涩且难懂的电子垃圾。点击此处免费领取 C 语言电子书C 语言电子书目录如下1.2.1 编程语言是什么语言的本质沟通的桥梁在平常我们的生活当中语言是人与人相互之间沟通时所使用的工具 中文、英文、法文等自然语言使得我们能够去表达思想 能够传递信息 能够交流感情。同样的情况 编程语言是人与计算机相互之间沟通时所使用的工具。就如同我们运用中文告知朋友帮自个儿买一杯咖啡这般 我们运用编程语言告知计算机帮自个儿计算1加1等于几。然而计算机跟人存在差异人类的大脑具备高度智能。即便我们说话精准度欠佳或者表达存在歧义朋友依旧能够领会我们的意图。举例阐释倘若你言说“买个东西”朋友会依据上下文以及你的表情推断出你打算购买何物。但计算机却是固定思维它仅能依从极其精确、明确定晰的指令予以运行工作。你必须详细告知它每一个步骤的具体做法丝毫均不能有模糊不明之处。编程语言的发展层次如果我们把编程语言按照抽象程度来分类可以分为三个层次按照执行方式分类编译型语言与解释型语言编程语言能够依照执行方式来划分成两大类情形如同看书存在着两种方式那般这两种方式分别是一种方式是另一种方式是。好处在于编译型语言运行速度极快原因是计算机直接执行机器语言免去了中间的翻译流程然而其缺点是每当修改程序后就得重新进行编译并且经过编译的程序仅仅能在特定的操作系统上运行若移植到别的系统仍是需要重新进行编译。编写方便调试便利修改程序后即可马上运行且能于任何安装了解释器的系统上运行这是解释型语言的好处。然而其存在运行速度相对较慢的缺点因需边翻译边执行并且运行时必须安装相应的解释器。按照编程方式分类面向过程与面向对象编程语言还可以按照编程思想分为不同类型较为直观的是面向过程的思维方式它适合于解决流程相对明确的问题就像计算器程序那样输入数据接着进行运算随后输出结果这是一种清晰的流程。对于我们学习嵌入式开发而言面向过程的思维方式更贴近硬件工作方式并且也更易于理解程序执行过程。采用面向对象的思维方式对于构建复杂的大型软件系统而言更具适配性这是由于它可以更为出色地对代码予以组织以及管理进而使得程序在维护以及扩展方面变得更加容易。1.2.2 什么是程序程序的本质指令的序列程序简而言之是一连串指令的呈有序排列的合体告知计算机需为之何事以及如何为之。此情形恰似一本菜谱之于厨师细致地述明诸般步骤。它这样讲先将菜洗净尔后把菜切削成块接着把锅烧热在之后倒入油最终炒制菜肴。程序亦是这般情况。步骤是如下这般先把数据读取再予以计算然后对结果加以判定最后输出答案。我们借助一个生活当中的实例去领会程序假定你打算教导一位压根不会做饭的女朋友烹制蛋炒饭你得给出极为详尽的步骤将冰箱门开启从中拿出2个鸡蛋去找一个碗来在碗里打破鸡蛋使其散开把锅烧热往锅中倒入适量的油接着把蛋液倒下锅中迅速搅拌鸡蛋当鸡蛋处于半熟状态的时候把米饭倒入锅中进行翻炒3分钟加入适量的盐以及酱油继续翻炒1分钟后关火将其盛到盘子里。做饭的这个过程它是一种“程序”每一个步骤都是一条“指令”。程序需要足够详尽且精准其中不能存在遗漏或者模糊之处不然的话执行者不管是女朋友或者计算机就会不知如何进行。从程序到进程程序的运行状态好多同学极易将“程序”与“进程”这俩概念弄混用以解释的是我会用一个简易比喻 标点符号。同样的情况是当我们对一个程序图标进行双击操作时操作系统会开展创建一个进程的行为以此来执行这个程序。进程所涵盖的内容有程序的代码程序运行时所需要的内存空间CPU的执行状态等等。任务与多任务于现代计算机里头我们时常听闻“任务”此词汇。任务也就是进程的别样表述尤其是在嵌入式系统当中我们更惯于运用“任务”这个词。实际上计算机的CPU于任何时刻仅能执行一条指令然而其执行速度极快能够在相异任务间迅速切换。举个例子它或许会耗费0.01秒来处理音乐播放器接着耗时0.01秒去处理浏览器事项随后再花0.01秒处理文字处理软件。鉴于切换速度相当快致使用户感觉仿佛是多个程序在同步运行。程序的不同类型根据功能和用途的不同程序可以分为很多类型1.2.3 程序与算法的关系经典公式程序 数据结构 算法在计算机科学范畴之内存在着一个极其有名的公式程序等于数据结构加上算法这一公式是由瑞士的计算机科学家尼古拉斯·沃思提出的它精准地归纳了程序的内在实质。以一个生活当中的实例来对这个公式予以理解。设想一下你打算去组织一场同学之间的聚会要是没有通讯录这种数据结构那你就不清楚该去联系哪个人要是没有组织方法这种算法那你就不晓得该如何去操办聚会唯有将这两者相互结合起来才能够成功地组织一回聚会也就是去完成程序那相应的功能。什么是数据结构数据结构有着这样的定义那就是数据元素之间存在的关系以及针对于这些数据实施操作的方法扼要来讲便是数据以何种方式存放以怎样的模式组织的方面的问题。让我们用几个生活中的例子来理解不同的数据结构比如说要是你打算去存储一个班级内所有学生的成绩那么能够采用数组像是成绩等于85成绩等于92成绩等于78等等。数组所具备的特点是查找处于某个位置的数据速度非常快也就是能够直接依据编号找到相应柜子然而要是想要在中间部分插入数据或者删除数据那就会显得比较麻烦这是因为需要移动后面的所有数据。链表具备这样的特性插入数据会显得便利不已删除数据同样如此省事仅仅只需变动指针所指的方向就行然而查找某一个特定的数据项这般的时候就得从起始部位开始逐个进行查找这情形就如同要寻觅糖葫芦处于中间位置的某一颗糖必然得从第一颗开始逐一计数方可实现。栈于程序里存在诸多用途像用于保存函数调用的信息。程序调用某函数之际会将当下的状态“压入”栈中函数执行完毕之时又会从栈里“弹出”先前的状态。队列常常被用于处理那些需要排队去等待的任务像是打印机所进行的打印任务还有操作系统的任务调度之类的情况。树结构特别适宜用来展现带有层次关联的数据像是文件系统其中文件夹又涵盖子文件夹还有文件以及组织架构图等等。什么是算法把特定问题予以解决的是一系列清晰且有限情况的步骤这被称作算法的定义它所回应的是“怎么做”的问题。我们所讲述的算法更侧重于“逻辑算法”然而并非是“数学型算法”像PID算法、滤波算法这类数学型算法一般而言对于学历要求是硕士、博士以上学历算法工程师才具备。让我们通过几个具体的例子来理解算法查找算法于电话簿里去寻人假定你打算在一本依照姓名排序的电话簿之中寻得“张三”的电话号码你或许会运用以下几种办法顺序查找起始于第一页逐页进行翻动直至寻觅到张三。此方式简便然而或许颇为迟缓。二分查找并由于电话簿系依字母顺序予以排列的缘故你能够翻至中间一页查看其处于“张”之前抑或之后随后持续在相应的那一半范围内展开查找。如此一来每次均可排除一半的页面查找速度明显变得快许多。数据结构与算法如何结合成程序在对数据结构以及算法的概念有了理解之后我们接着去瞧瞧它们究竟是以怎样的方式结合成为一个完整的程序的。以学生成绩管理系统为例第一步明确数据结构首先那我们得去判定怎样去存储学生的相关信息每一个学生都存在着姓名、学号以及各科成绩这类信息我们能够设计如此的数据结构struct Student { char name[50]; // 姓名 int id; // 学号 float scores[5]; // 五科成绩 float average; // 平均分 };然后我们需要存储所有学生的信息可以用数组struct Student students[100]; // 最多100个学生 int student_count 0; // 当前学生数量第二步设计算法 接下来我们需要设计各种操作的算法添加学生算法查找学生算法计算平均分算法第三步需将数据结构与算法进行组合进而形成完整的程序最终达成把数据结构和算法组合形成完整程序并实现的整体结果。#include // 数据结构定义 struct Student { char name[50]; int id; float scores[5]; float average; }; struct Student students[100]; int student_count 0; // 算法实现 float calculate_average(float scores[]) { float sum 0; for(int i 0; i 5; i) { sum scores[i]; } return sum / 5; } void add_student() { if(student_count 100) { printf(学生数量已满\n); return; } // 输入学生信息 printf(请输入学生姓名); scanf(%s, students[student_count].name); printf(请输入学号); scanf(%d, students[student_count].id); printf(请输入5科成绩); for(int i 0; i 5; i) { scanf(%f, students[student_count].scores[i]); } // 计算平均分 students[student_count].average calculate_average(students[student_count].scores); student_count; printf(学生信息添加成功\n); } // 主程序 int main() { int choice; while(1) { printf(1. 添加学生\n2. 查找学生\n3. 退出\n); printf(请选择); scanf(%d, choice); switch(choice) { case 1: add_student(); break; case 2: // 查找学生的代码... break; case 3: return 0; } } }通过这个例子我们可以清楚地看到1.2.4 如何从零生产一个程序程序诞生的完整过程不少刚开始接触的人觉得编程便是坐在电脑跟前敲代码然而事实是从无到有去制作一个程序犹如建造一幢房子那般要历经设计、施工、装修、验收等好些阶段。编程仅仅是其中的一个环节那就让我们来详尽知晓程序诞生的整个历程。1. 起初的一段时期进行编程这项活动就是运用代码去把解决方案描绘出来。啥是编程编程乃是运用计算机能够理解的语言去描绘解决问题的办法这恰似用中文写作文那般你心里存有想法然而得借助文字将想法给表达出来编程亦是如此你晓得如何解决问题可是得运用编程语言把解决办法“写”出来。编程的具体过程先来理解编程过程我们采用一个简便示例。倘若我们打算编写个程序这个程序用于计算圆的面积。步骤1分析问题步骤2设计解决方案步骤3编写代码#include int main() { float radius, area; const float PI 3.14159; // 提示用户输入 printf(请输入圆的半径); // 读取用户输入 scanf(%f, radius); // 计算面积 area PI * radius * radius; // 输出结果 printf(圆的面积是%.2f\n, area); return 0; }2. 第二个阶段是编译也就是Compilation这一过程要将其转化为计算机所使用的语言。需不需要编译呢我们所撰写的C语言代码如同以中文所书写的有关说明的文书然而计算机仅仅能够理解机器语言也就是由0和1所构成的一组代码。编译实际上就是将以中文书写出来的说明文书转变成计算机能够领会的类似“外星语”那般的代码的进程。编译的详细过程先有初稿之后校对最后润色这几步必不可少编译过程恰似如此就如同翻译一本书籍时要经这般多环节。这是编译的第一步预处理器会处理所有以#开头的指令。比如就像写作文前先准备好所有需要的资料和素材。编程语言编译器会将经过预处理操作得出的C语言的代码转化成汇编语言汇编语言相较于机器语言而言理解起来会略微容易那么一些然而它依旧是非常接近硬件的这情形就如同先把中文翻译成为英文以此为进一步的翻译工作做好方面的准备。汇编器会将汇编语言予以翻译成机器语言进而生成目标文件也就是.obj或.o文件。这恰似将英文转化成计算机能够理解的“外星语”。一个完整的可执行文件是由链接器将多个目标文件以及系统库文件组合而成的这情形如同把那些翻译好了后的各个章节装订成为一本完整的书啰。编译工具的使用于实际的开发里面我们一般会运用集成开发环境也就是IDE去把编译的过程予以简化。采用命令行进行编译要是你所运用的是GCC编译器那么编译经过或许是这样子的。gcc -o circle_area circle_area.c这条命令告知GCC编译器要将circle_area.c编译为名为circle_area的可执行文件是这样做的。若是你运用像Dev-C、Code::Blocks这类开发环境一般来讲仅需按下F9键或者点击“编译并运行”按钮此时IDE就会自行完成全部的编译过程。编译过程中可能遇到的问题这好似在写作文之际的错别字或者语法错误举例而言像是忘掉写分号括号出现不匹配等情况编译器会将错误的位置告知你你得进行修改之后再度编译。这一般来讲缘由在于寻觅不到某一个函数的定义或者归因为欠缺必要的库文件。恰似撰写书籍之际引用了某一份资料然而于参考文献里找寻不到该资料。编译不会被警告所阻止然而代码里或许存在的问题会被提醒。这如同老师批改作文给出的建议虽不算错误可还是以改作为佳。3. 第三阶段执行Execution- 程序开始工作什么是程序执行呢编译完成之后我们得到了一个可执行文件然而它仍然只是安静地放置在硬盘之上至于程序执行便是要让这个处于“沉睡”状态的程序“苏醒过来”进而开始展开工作。执行过程的详细步骤当你对可执行文件实施双击操作时操作系统会执行将程序从硬盘加载至内存中的行为。这一行为与把一本书从书架之上取下并打开以作阅读准备的情况相类似。操作系统会为程序分配内存空间包括代码段其发挥着存储程序的指令数据之作用数据段它承担着存储全局变量以及静态变量的活儿堆是专门用以进行动态分配内存之所在栈乃是用来存储局部变量以及函数调用信息之处。操作系统会针对程序去创建出一个进程给该进程分配一个进程ID也就是PID并且会于进程表当中记录下相关的信息。这情形如同是给每一位正在做菜的厨师去分配一个工作台以及工具。CPU开始执行程序的指令。对于我们的圆面积计算程序首先执行这样一行代码即printf(请输入圆的半径); 它会在屏幕之上显示提示信息之后执行scanf(%f, radius); 以此来等待用户输入当用户输入完数据之后执行area PI * radius * radius; 展开进行计算最后执行printf(圆的面积是%.2f\n, area); 用以显示结果。4. 调试Debugging- 发现和修复错误程序极少能够一下子就毫无瑕疵地运行起来一般而言都得历经调试这样一个过程以此去发觉并修正其中的错误这些操作都是必不可少的lo.jcipa.cnlp.jcipa.cnlq.jcipa.cnlr.jcipa.cnls.jcipa.cnlt.jcipa.cnlu.jcipa.cnlv.jcipa.cnlw.jcipa.cnlx.jcipa.cnly.jcipa.cnlz.jcipa.cnma.jcipa.cnmb.jcipa.cnmc.jcipa.cnmd.jcipa.cnme.jcipa.cnmf.jcipa.cnmg.jcipa.cnmh.jcipa.cnmi.jcipa.cnmj.jcipa.cnmk.jcipa.cnml.jcipa.cnmm.jcipa.cnmn.jcipa.cnmo.jcipa.cnmp.jcipa.cnmq.jcipa.cnmr.jcipa.cnms.jcipa.cnmt.jcipa.cnmu.jcipa.cnmv.jcipa.cnmw.jcipa.cnmx.jcipa.cnmy.jcipa.cnmz.jcipa.cnna.jcipa.cnnb.jcipa.cnnc.jcipa.cnnd.jcipa.cnne.jcipa.cnnf.jcipa.cnng.jcipa.cnnh.jcipa.cnni.jcipa.cnnj.jcipa.cnnk.jcipa.cnnl.jcipa.cnnm.jcipa.cnnn.jcipa.cnno.jcipa.cnnp.jcipa.cnnq.jcipa.cnnr.jcipa.cnns.jcipa.cnnt.jcipa.cnnu.jcipa.cnnv.jcipa.cnnw.jcipa.cnnx.jcipa.cnny.jcipa.cnnz.jcipa.cnoa.jcipa.cnob.jcipa.cnoc.jcipa.cnod.jcipa.cnoe.jcipa.cnof.jcipa.cnog.jcipa.cnoh.jcipa.cnoi.jcipa.cnoj.jcipa.cnok.jcipa.cnol.jcipa.cnom.jcipa.cnon.jcipa.cnoo.jcipa.cnop.jcipa.cnoq.jcipa.cnor.jcipa.cnos.jcipa.cnot.jcipa.cnou.jcipa.cnov.jcipa.cnow.jcipa.cnox.jcipa.cnoy.jcipa.cnoz.jcipa.cnpa.jcipa.cnpb.jcipa.cnpc.jcipa.cnpd.jcipa.cnpe.jcipa.cnpf.jcipa.cnpg.jcipa.cnph.jcipa.cnpi.jcipa.cnpj.jcipa.cnpk.jcipa.cnpl.jcipa.cnpm.jcipa.cnpn.jcipa.cnpo.jcipa.cnpp.jcipa.cnpq.jcipa.cnpr.jcipa.cnps.jcipa.cnpt.jcipa.cnpu.jcipa.cnpv.jcipa.cnpw.jcipa.cnpx.jcipa.cnpy.jcipa.cnpz.jcipa.cnqa.jcipa.cnqb.jcipa.cnqc.jcipa.cnqd.jcipa.cnqe.jcipa.cnqf.jcipa.cnqg.jcipa.cnqh.jcipa.cnqi.jcipa.cnqj.jcipa.cnqk.jcipa.cnql.jcipa.cnqm.jcipa.cnqn.jcipa.cnqo.jcipa.cnqp.jcipa.cnqq.jcipa.cnqr.jcipa.cnqs.jcipa.cnqt.jcipa.cnqu.jcipa.cnqv.jcipa.cnqw.jcipa.cnqx.jcipa.cnqy.jcipa.cnqz.jcipa.cnra.jcipa.cnrb.jcipa.cnrc.jcipa.cnrd.jcipa.cnre.jcipa.cnrf.jcipa.cnrg.jcipa.cnrh.jcipa.cnri.jcipa.cnrj.jcipa.cnrk.jcipa.cnrl.jcipa.cnrm.jcipa.cnrn.jcipa.cnro.jcipa.cnrp.jcipa.cnrq.jcipa.cnrr.jcipa.cnrs.jcipa.cnrt.jcipa.cnru.jcipa.cnrv.jcipa.cnrw.jcipa.cnrx.jcipa.cnry.jcipa.cnrz.jcipa.cnsa.jcipa.cnsb.jcipa.cnsc.jcipa.cnsd.jcipa.cnse.jcipa.cnsf.jcipa.cnsg.jcipa.cnsh.jcipa.cnsi.jcipa.cnsj.jcipa.cnsk.jcipa.cnsl.jcipa.cnsm.jcipa.cnsn.jcipa.cnso.jcipa.cnsp.jcipa.cnsq.jcipa.cnsr.jcipa.cnss.jcipa.cnst.jcipa.cnsu.jcipa.cnsv.jcipa.cnsw.jcipa.cnsx.jcipa.cnsy.jcipa.cnsz.jcipa.cnta.jcipa.cntb.jcipa.cntc.jcipa.cntd.jcipa.cnte.jcipa.cntf.jcipa.cntg.jcipa.cnth.jcipa.cnti.jcipa.cntj.jcipa.cntk.jcipa.cntl.jcipa.cntm.jcipa.cntn.jcipa.cnto.jcipa.cntp.jcipa.cntq.jcipa.cntr.jcipa.cnts.jcipa.cntt.jcipa.cntu.jcipa.cntv.jcipa.cntw.jcipa.cntx.jcipa.cnty.jcipa.cntz.jcipa.cnua.jcipa.cnub.jcipa.cnuc.jcipa.cnud.jcipa.cnue.jcipa.cnuf.jcipa.cnug.jcipa.cnuh.jcipa.cnui.jcipa.cnuj.jcipa.cnuk.jcipa.cnul.jcipa.cnum.jcipa.cnun.jcipa.cnuo.jcipa.cnup.jcipa.cnuq.jcipa.cnur.jcipa.cnus.jcipa.cnut.jcipa.cnuu.jcipa.cnuv.jcipa.cnuw.jcipa.cnux.jcipa.cnuy.jcipa.cnuz.jcipa.cnva.jcipa.cnvb.jcipa.cnvc.jcipa.cnvd.jcipa.cnve.jcipa.cnvf.jcipa.cnvg.jcipa.cnvh.jcipa.cnvi.jcipa.cnvj.jcipa.cnvk.jcipa.cnvl.jcipa.cnvm.jcipa.cnvn.jcipa.cnvo.jcipa.cnvp.jcipa.cnvq.jcipa.cnvr.jcipa.cnvs.jcipa.cnvt.jcipa.cnvu.jcipa.cnvv.jcipa.cnvw.jcipa.cnvx.jcipa.cnvy.jcipa.cnvz.jcipa.cnwa.jcipa.cnwb.jcipa.cnwc.jcipa.cnwd.jcipa.cnwe.jcipa.cnwf.jcipa.cnwg.jcipa.cnwh.jcipa.cnwi.jcipa.cnwj.jcipa.cnwk.jcipa.cnwl.jcipa.cnwm.jcipa.cnwn.jcipa.cnwo.jcipa.cnwp.jcipa.cnwq.jcipa.cnwr.jcipa.cnws.jcipa.cnwt.jcipa.cnwu.jcipa.cnwv.jcipa.cnww.jcipa.cnwx.jcipa.cnwy.jcipa.cnwz.jcipa.cnxa.jcipa.cnxb.jcipa.cn