用c语言编写的代码程序写下这个编程~谢谢~

c语言编写的代码程序编程问题謝谢能改一下我写的代码吗Description编写程序,输入一批学生的成绩遇0或负数则输入结束,要求统计并输出优秀(大于85)、通过(60~84)和不及格(小于60)的学生人... c语言编写的代码程序编程问题谢谢能改一下我写的代码吗Description

编写程序,输入一批学生的成绩遇0或负数则输入结束,要求统计并输出优秀(大于85)、通过(60~84)和不及格(小于60)的学生人数

专业C/C++软件开发


这个没有说明输入2113规模,也5261就是总个4102而是要求遇到0或者负数结束1653

所以用数组不是好方法

 

下载百度知道APP,抢鲜体验

使用百度知道APP立即抢鲜体验。你的手机镜头里或许有别人想知道的答案

以下是一个c语言编写的代码程序函数有三行代码,实现将数字转为对应字符的功能当然,真正的函数实现应该增加判断a的取值是否在0到9之间这里简单的逻辑实现仅僅是为了阐述嵌入式的知识点。我们的故事就从我们写下这行代码开始止于这行代码变成指令在CPU中运行。写这篇文章是为了讲清楚作为┅名嵌入式软件开发工程师应该要具备的技能即其应该要掌握的知识点。

我们写代码、编译、链接、调试等都是在集成开发环境中进行作为一个嵌入式工程师应该非常精通一个集成开发环境,就像至少要精通一种芯片的体系结构一样如Keil之于51。其相关点包括:

  1. 工程的属性设置这非常重要,包括库路径、头文件路径、代码优化级别、调试选项、宏定义等等不仅仅是读懂,而是应该去改动这些设置看看有什么变化和影响,真正地掌握

  2. 每个文件的属性设置也要充分了解,如产生汇编中间文件等;

  3. 中间文件和结果文件的路径以及文件的格式作用;

  4. 设置字体和code style(颜色和高亮显示)是让自己的眼睛更舒服

  5. 知晓集成开发环境和工具链的对应关系。集成开发环境的编辑链接调试按鈕对应什么应该要知道吧有些芯片平台可能并没有很好的集成开环境(如MIPS),那只能熟悉命令行操作了

  6. 一般的基础开发环境都支持插件开發,理解一下就好除非自己的工作就是做这个的。

当然看代码一般不在集成环境里面进行,我喜欢source insight高亮显示和智能关联,极大地提高效率在开发环境和source insight里面写代码都较常见。

引言的函数是高级语言而CPU能理解的是机器语言,也就是0101这些二进制数据所以要想让CPU执行這个函数,得依靠工具链的支持一般我们是在PC上进行开发,即所谓的宿主但执行这段代码的是嵌入式处理器,也就是所谓的目标系统所以这种工具链也成为交叉工具链,对应的开发环境称为交叉编译环境我个人认为很多事物和思想都是相通的,举一反三我们需要莋的就是精通,至少很熟悉一种工具链如gcc工具链。能够运用工具链并不代表你很熟悉真正的熟悉应该是理解工具链的作用以及其在实現这个作用的过程中的大致做法,也就是实现的原理我们需要弄懂的包括:

  1. 理解编译原理。编译是将每个C文件转换为接近机器语言而又能够被开发工程师理解的汇编语言而且其将每个文件的代码和变量数据进行重新组织,分成几个部分如这个文件里面的函数都会集中放到一个代码段.CODE(除非你自己把某个函数规定要某个指定的段),常量数据段.CONST(类似代码段只读)静态变量和全局变量所在的.DATA(有些平台两个段的洺字也不一样,按作用实际一样的)未初始化的全局变量.BSS;另外一个很重要的任务是记录可重定位的符号(函数、变量)信息,如引言函数调鼡printf这个函数而printf函数的地址暂时是未知的,那文件中需要为这个调用做好将来重定位的准备这时看到的代码段的起始地址往往是0。

  2. 理解編译选项其实集成开发环境的工程属性和文件的属性设置对应的就是这个编译选项。

汇编器是将汇编语言转为机器语言输出的也是可偅定位文件。一般编译器都包含了汇编这个步骤直接输出可重定位.O文件。而汇编器一般是处理以汇编语言编写的文件

链接就是将所有鈳重定位文件的代码段、数据段等进行统一组织和空间分配,其依据就是工程的链接文件另外最重要的是实现重定位的过程。链接文件昰有其固有的语法格式的要熟悉其语法,会自己写链接文件不能仅仅是拷贝和局部改动,而是真正能利用链接来实现自己普通的和特囿的想法最后链接器会生成一定格式的可执行文件,如exe和elf格式等另外,掌握芯片平台的存储资源范围也是写好链接文件的前提条件

makefile昰gcc工具链非常重要的部分,其作用就用命令行的方式来替代集成开发环境实现编译和链接的自动化执行过程。makefile是脚本执行同样有固定嘚语法,需要很好地领会

即二进制工具集,掌握和利用好这些工具能够提高开发的效率例如在GCC工具链中我们想快速定位到某个变量的哋址,并不需要去查看一些结果文件而是敲命令mn就可以了。

至此我们假定已经将引言的C函数编译成.O文件了,加入到工程并调用写好makefile囷link,就可以输出为可执行文件了

三、理解各种类型的文件格式

嵌入式开发需要和硬件打交道,硬件资源有限交叉调试环境往往没有PC软件那么友好。因此要懂得利用一切游泳资源来提高开发调试效率我现在的机子没有交叉编译环境,我叫一个嵌入式开发工程师帮我取得引言中代码的反汇编代码他竟然回答说要调试的时候用汇编模式才能看到反汇编代码。这明显就是一般PC工程师的认识实际我们可以在佷多地方看到反汇编的信息的。

  1. 可执行文件格式如elf。应该很好地理解这个文件格式我们常听到一句话是“存在即是道理”,里面所有嘚数据结构都有其对应的作用我们可能会看到在链接文件里面会有一个ENTRY的标识,而在elf的文件头格式中有一个entry的域其是一一对应的,它玳表着这个可执行文件的第一条执行指令的地址有很多人可能会认为这个地址应该是main的地址吧,其实并不是而是运行时库的入口。当嘫我们并不需要对着elf的格式说明来查看这个二进制文件。不是有binutils吗它就是用来查看我们需要的信息的。

  2. map文件map文件是以文本文件的形式来记录elf格式文件中的各种代码和数据段的地址信息和长度,以及各个函数和变量对应的地址等等结合link文件来理解map文件会很有趣。map文件反映的函数和变量的地址信息对于调试会非常有用尤其是一个团队开发一个项目时往往有多个工程,多个模块而工程A在调试时,集成開发环境的watch是监看不到工程B的某个变量的这时就需要直接输入该变量地址来查看内容值。

  3. lst文件其是反汇编文件,掌握汇编语言是嵌入式工程师的必备技能平时可能较少用汇编开发程序,但调试的时候需要经常看反汇编是很正常的

  4. 理解代码和数据的组织方式。.O和.ELF都有描述我们在学习c语言编写的代码程序的时候,老师往往不会讲这些内容这里指的是我们要知道编译器如何按它的方式去理解我们的想法(函数和变量),并以其自己的方式去管理这些函数和变量也就是一个C文件通过编译输出.O文件时,其产生的.CODE、.DATA、.BSS、.SYM等等段

嵌入式软件工程师和PC工程师最大的区别是嵌入式软件工程师需要对自己所写的每一行代码负责,负责的意义代表着这行代码的作用、它在可执行文件中嘚位置、它真正要加载到哪里(即虚拟运行地址)、它什么时候会被加载、它什么时候会被执行等等

加载一般指的是将目标代码复制到内存對应位置的过程(记得,RAM是掉电不保存内容的)一般有以下几种方式:

  1. 对于存储资源丰富型系统,一般是使用高端嵌入式处理器的系统在系统启动时ELF文件一般是放在外存储设备中。这种系统都部署了嵌入式操作系统能够解释ELF格式文件,将ELF里面真正的代码和数据段加载到内存对应的位置其第一个加载的自然是ENTRY对应的起始地址所在的代码段。

  2. 对应存储资源紧缺型系统一般是使用低端嵌入式控制器的系统,其部署的操作系统往往是精简高效型系统它并不直接解释ELF格式,而是解释ELF再处理后的格式文件这种系统往往是定制型的操作系统,而ELF洅处理也跟操作系统相关但再处理后的格式依然要保持必要的信息,如entry.CODE,.DATA等等在系统启动阶段,再处理后大幅减少容量的文件一般吔是放在外存储设备中在必要时被操作系统读取并加载到内存中。

  3. 有时一些代码会被固化到ROM中如系统启动执行的代码都是固化到ROM中,洳PC上电执行的第一条指令就是固化的而且固化的地址就是运行地址,无需加载对于嵌入式控制器,这个固化也可以理解为将代码烧写箌flash rom中这应该是常见的利用开发板进行开发所进行的步骤。

  4. 有时一个系统里面又有RAM又有ROM那有部分代码会固化到ROM中,但其运行地址并不是凅化的地址这时也需要将代码加载到RAM中对应的地址才能运行。

上节所说的加载一定是将代码加载到实际的物理内存但我们的可执行文件中的运行地址是虚拟运行地址,两者之间是什么映射的呢这就是内存管理单元(MMU)的作用域。高端处理器一般都有MMU但低端控制器一般是沒有MMU的,但低端控制器也会按一定的方式去做好映射最简单的就是虚拟和物理一一对应嘛。有MMU管理的系统也不是硬件就能解决所有问题其也需要借助于页表来记录映射关系,MMU里面有一个叫做TLB的东西是页表的CACHE,在每次访问内存时会被将虚拟地址的前N位转为对应的物理内存块的前N位依据就是页表(可以理解为转换表)。

执行肯定指的是CPU的取指执行了我们需要懂得什么?我们要理解的是芯片的体系结构理解其精简指令集。

  1. 寄存器包括通用寄存器和专用寄存器。通用寄存器一般用来暂存数据和做计算用专用寄存器是实现专有的功能,最偅要的是PC和SPPC即程序寄存器,记录的是当前指令执行的地址SP是栈指针,记录的是当前的栈顶地址栈的使用绝对是一个非常绝妙的发明,栈的递归恰好对应的是函数的调用和返回函数的调用就是将必要的信息入栈,这里最重要的是包括返回地址;而程序返回就是出栈峩们可以看到像RET这样的指令对应的伪指令都是将当前SP的值赋给PC,然后SP增加或者减少(根据体系结构而定)

  2. 理解流水线相关的问题、cache的问题。調试的时候特别要注意指令预取所带来的问题

我们刚开始学c语言编写的代码程序的时候应该都有一种迷惑,我们在main中调用printf这个函数就能咑印了但我们又没看到它具体的实现是在哪里。另外我们大部分人往往都有一个误解,以为main入口是程序执行的第一个指令实际是不對的。

  1. pirntf的功能是打印信息到屏幕上我们可以设想在main执行之前一定要初始化屏幕的输出驱动吧。

  2. 操作系统支持多线程而执行的程序是一個进程,main就是一个主线程在我们的程序里面没看到什么进程线程之类的东西,就是在main之前做好的

  3. 我们一般在C++中看到某个类有构造函数囷虚构函数,它们分别是在main之前和退出之后要做的动作怎么支持这个功能呢?

这些大致的功能就是运行时库所要支持的功能不同的操莋系统有不同的要求,但从现在开始我们不能再认为main就是程序第一个要执行的指令。

printf这个函数实际上是标准C库的一个函数每种语言都會有很多的支持库,所以我们要懂得如何去利用现有的库来实现我们的功能而不是什么都要自力更生。当然从节约存储资源和提高执行效率的角度可以参考C库某函数的实现,然后进行改进C库包括常见的字符串转换(如引言的str2num)、浮点计算等模块。

引言函数调用printf这个函数洏这个函数一般是操作系统提供的函数,其不会把代码实现拿到引言函数所在的应用工程去build拿到的仅仅是API库。也就是说应用先通过API层进叺到操作系统层再调用实际的打印驱动代码API怎么实现?一般使用陷入指令或者软件中断当然API的设计也是一门艺术,在资源紧缺型的系統中更是如此因为陷入的时候我们除了要提供打印的参数,还要提供打印函数对应的索引这个索引实现的方式并不单一,这就是具体設计时要考虑的

ABI即二进制程序接口,主要涉及的是栈帧的实现和函数调用时的传参约定这是在c语言编写的代码程序和汇编语言交叉编程时特别要注意的知识点。

  1. 栈帧记录的是非常重要的上下文信息在多任务操作系统移植时,它记录的信息一定是要进行重点关注的往往是汇编语言进行编写。因此理解栈帧是移植操作系统的必要步骤当然理解好栈帧能够很好地帮助调试。

  2. 传参约定即在传递参数时用寄存器存还是栈来存,或者两者都有(如MIPS工具链)存放的顺序,在参数类型不同或者个数不同时又是怎么约定的

  3. 寄存器的使用约定,通用寄存器的执行速度肯定优于内存所以寄存器的使用非常频繁。在函数调用的过程中自然会出现某个函数执行到一半的时候就发生调用叻,这时某些寄存器就要先保存起来以免被下一个要调用的程序破坏;而某些寄存器在使用之前按规定是先要保存再利用,返回前将其恢复就可以了

将数字转为字符串就是加上0X30,这是跟进ASCII码表来实现的做嵌入式开发很多时候需要理解很多编码格式,如UNICODE字库等等码表。

嵌入式开发工程师不应仅仅停留在某个应用或者某个模块上而是应尽量全面地学会或者把控整个系统,虽然不能每个模块的代码都去悝解一遍但基本的流程要懂。基本的软件层次从低到高有启动、驱动、操作系统、API、中间件、UI、应用等等

现在的芯片都可以成为SOC,不僅CPU很多模块都已经集成到芯片上,如ADC、SRAM等等要懂得各个模块的使用接口,还要理解CPU和各种接口对应的总线的访问方式他们是怎么竞爭访问的。

要真正地学好一门语言不是指懂得它的语法,而是要有解决问题的思想对于嵌入式开发,应该学会面向对象的编程思想其实C++的类CLASS和c语言编写的代码程序的数据结构STRUCT不是一回事吗?

三行代码书写着程序员的情书

我要回帖

更多关于 C语言编写的代码程序 的文章

 

随机推荐