C语言学习

2024-06-20

C语言学习(通用12篇)

C语言学习 篇1

摘要:随着我国计算机技术不断发展, C语言的一些独特优势不断被开发出来, C语言已经成为主流编程语言之一。但是在C语言的教学过程中, 往往会出现诸多问题, 致使学习效率不高, 无法很好地完成教学目标, 使学生难以通过C语言做出编程。主要以C语言学习困难的原因作为出发点, 探究C语言编程技巧在C语言学习中的应用。

关键词:C语言,编程技巧,学习,应用

随着计算机技术不断发展, 计算机编程显得愈加重要。C语言作为计算机编程中使用最为广泛的语言之一, 同样也是语言编程领域中的重要代表。其主要特点分为两点:一是高级语言;二是低级语言, 即能够通过系统软件进行编写, 同时也能够通过应用软件进行编写, 可以说C语言的应用范围极为广泛, 是计算机编程中的主流语言之一。但C语言在学习过程中存在一些难度, 需要学生对C语言有一定理解, 这样才能够更好地学习C语言、掌握C语言。

一、C语言学习困难的主要因素

由于C语言的优势非常明显, 在计算机长期发展中, 对C语言编程的重视程度越来越高, 并且职业学校与高校都开设了C语言编程专业, 学习C语言的学生也不断增加。但是, 在C语言的实践学习中, 很多学生认为C语言学习非常枯燥、乏味, 使学习效率低下, 对C语言的基本内涵与技术没有一个明确的认识, 即使学习一段时间后也很难独立进行简单的编程。究其根本原因, 是由于C语言是一种高级语言, 并且与其他语言有着较大的差异性, 在日常学习中无法直观地感受到相应的知识画面, 只有通过C语言编程之后, 再进行一定的调试, 才能够直观看到编程效果。如果学生在编程过程中稍有一丝纰漏, 就会致使编程错误, C语言编程可以说是“牵一发而动全身”。

与此同时, C语言中存在诸多数据结构与算法结构, 这些知识点只能通过机械式记忆, 至今也无法找出很好的记忆方法, 使学生在背诵繁杂的算法过程中会容易遇到障碍或产生混淆。正因如此, 才会让学生感到C语言非常枯燥、乏味, 极大地降低学生学习兴趣, 使学生在学习过程中对C语言的学习逐渐丧失主观愿望, C语言学习质量与效率止步不前, 无法很好地完成教学目标。

二、C语言编程技巧在C语言学习中的应用

(一) 指针掌握

在C语言编程学习过程中, 学生要充分掌握指针知识, 这也是实现C语言编程的重要环节。正确应用指针能够帮助学生有效地解决一些相对比较复杂的C语言编程问题。在C语言学习中, 指针是一种特殊变量, 其主要包含三大要素, 即指针名称、指针类型、指针数值。在通常情况下, 指针名称与变量名称是相同的, 两者的主要区别在于数值与指针类别层面上。除此之外, 指针不仅能够表示数组, 同时也能够作为函数的残数返回值。

(二) C语言特有函数

C语言作为一种高级语言, 其内部构成也是非常复杂的, 需要学生能够正确掌握C语言特有函数, 也可以说C语言特有功能。可以说在一般情况下, 每一个C语言函数都能够代表、实现一定的C语言功能, 同时每个函数的名称也能够反映出其功能。在给函数进行定义的过程中, 应该包含函数的名字、参数、类型、返回值类型等。其中, 在编程过程中, 函数数据库会给予函数一个系统名称, 在正式应用过程中, 只需要将指令 (#include) 把相关的头文件包含在本文件中即可。

(三) 算法技巧

在C语言学习过程中, 学生必须要掌握一定的算法技巧, 同时算法也是C语言编程的重要组成部分。通常情况下, 算法可以通过自然语言的形态表示, 也可以采用流程图的形式来表示。在应用流程图的过程中, 要求学生能够灵活、熟练地掌握一些常用流程符号。学生想要更好地掌握算法技巧, 一些基础的数学知识是非常重要的, 同时也比数学基础较差的学生要掌握得更快。所以, 在算法教学过程中, 教师可以适当融入一些数学知识, 从而提高C语言编程效率与质量。

(四) 位运算与文件

位运算可以说是C语言编程中的一大特色, 位运算在其他语言编程中是不具备的。二进制是位运算的主要针对对象, 之后再通过输入数值进行一系列的运算。例如按位、位与、取反等算法, 都是位运算中的基本算法。除此之外, 文件在C语言编程时也是十分重要的, 同样也是必不可少的。文件主要包含数据文件以及程序文件, 通过文件能够让数据查询更为便捷, 能够有效提高查询效率。可以说C语言编程的效率性, 是学习计算机编程的重要目标之一。所以, 学生必须要掌握好位运算相关的技巧与方法, 以及对文件知识更加得心应手, 在实践操作中不断自我反省、自我发现, 不断挖掘行之有效的编程方法, 从而提高学生的C语言编程技能。

C语言作为当代主流编程的代表语言之一, 在C语言应用范围非常广泛的同时, 也带来较为困难的学习难度。但是在实际学习过程中, 仍然可以挖掘一些学习方法与技巧, 来降低C语言的学习难度, 从而提高学习效率, 为社会提供更多的C语言编程人才。

参考文献

[1]段煅.C语言编程技巧在C语言学习中的应用[J].电脑编程技巧与维护, 2012, 7 (20) :97-104.

[2]郭慧敏, 詹玲超.将C语言趣味化教学进行到底:提高C语言课程教学效果方法之我见[J].电脑知识与技术, 2010, 6 (28) :31-32.

C语言学习 篇2

课题:

教学目的: 教学重点: 教学难点: 第一章 C语言概述

1、了解C语言出现的背景

2、掌握C语言程序的构成、书写格式和上机步骤 C语言程序的构成 上机操作

步骤一 引入新课

从计算机应用基础中学过的计算机语言及语言处理系统引出C语言。步骤二 讲授新课

一、C语言出现的背景

1.ALGOL60 :1960年面向问题、结构化,可读性、可移植性好,不能对硬件操作; 2.CPL:63年接近硬件,规模较大、难以应用;

3.SIMULA:1966~1967 SIMULA 66是ALGOL 60的扩充;SIMULA 67引进了“对象”和“类”等概念而成为第一个面向对象的语言;

4.BCPL :67年由CPL改制,结构化、直接处理硬件;

5.B语言:1970年贝尔实验室的Ken Thompson以BCPL语言为基础设计的,开发了第一个UNIX操作系统;

6.C语言:1972-73年间,保持了BCPL和B语言的精炼与接近硬件的优点,克服了它们过于简单、数据无类型的缺点,重写了UNIX操作系统的90%;

7.标准 C:以1978年发表的UNIX第七版中的C编译程序为基础,Brain W.Kernighan&Dennis M.Ritchie合著的《The C Programming Language》问世; 8.ANSI C:1983年美国国家标准化协会(ANSI)制定的标准

9.C++:1986年 与C兼容,保持了C的所有优点并进行了增强:增强了面向对象的机制,成为典型的面向对象和面向过程的混合语言,适用于大型系统软件和应用软件的开发;

10.87 ANSI C:1987年制定的新标准;

11.ISO C:1990年ISO接受87 ANSI C为ISO C的标准,该标准是目前C编译器的标准。

二、C语言的特点

1.语言简洁、紧凑,使用方便、灵活; 2.运算符丰富

3.数据类型多(整型、实型、字符型、数组类型、指针类型、结构体类型、共用体类等)4.具有结构化的控制语句 5.语法不太严格,自由度大

6.既是高级语言,又具有低级语言的功能 7.生成目标代码质量高,程序执行效率 8.可移植性好

三、C语言程序的构成

1.C语言是由函数构成的,至少有一个main()函数;

2.每个函数由函数首部和函数体组成;函数体由说明语句、执行语句组成; 3.每个C程序从main()函数开始执行,并在main()中结束; 4.每个语句和数据定义的最后必须加分号;

5.C程序无输入、输出语句

输入功能由scanf()函数完成;输出功能由printf()函数完成; 6.可加注释/*……*/

借助程序实例加以说明,并提倡良好的程序设计书写风格: 1)每个语句占一行;

2)同一层次的语句从同一位置处开始书写;

3)同一层次中嵌套的结构,应从不同位置开始书写;

4)编译控制行、外部数据定义、函数定义之间空一行书写; 5)对于函数体的大括号的书写约定 6)学会使用注释。

四、上机步骤

1.进入环境c:tctc 或进入ms-dos,>cd,>cd tc,>tc 2.编辑源程序 3.保存源程序 4.编译源程序F9 5.执行程序Ctrl-F9,查看结果Alt-F5 6.退出C环境Alt-X

步骤三 课堂小结

1、C语言的构成要素,main函数在程序中的作用

2、上机操作的过程

步骤四 布置作业 上机练习:

1、进入C编辑环境,按良好的程序设计风格输入例题,按步骤编译、运行、查看结果

C语言学习 篇3

关键词:c/c++;程序设计;编程思想

1引言

“c语言程序设计”课程是高校计算机及相关专业的传统课程,近年来该课程却逐渐向“C/C++程序设计”过渡,更有甚者干脆摒弃C语言课程直接开设C++课程,这一现象值得我们反思,究竟如何在C与C++之间取舍,二者有何联系与区别?下面分别从三方面进行阐述。

2联系

C++是以G语言为基础增加新内容发展而来,是C语言的推广和延伸。绝大部分C语言中正确的语句在C十十中也可以使用,C语言是C++的子集。

2.1 main函数

c++与c语言中都有main函数,main函数在两种语言中都起着不可或缺的作用,担当重要作用即程序的入口。简单的不带类的c++程序中的主函数除了输入输出(Gout/cin)与G语言不同。别的二者非常类似。

2.2类与结构

类是面向对象程序设计中最基本的概念,是实现面向对象程序设计的基础与核心。类是将不同类型的数据和与数据相关的操作封装在一起的集合体,是对要处理问题的抽象描述。定义了抽象类之后可利用该类定义其实体即该类的对象。结构是c语言中很有用的一种数据类型,是以后学习c++中“类”的基础。结构体成员组合在一起形成一个整体,其成员可为不同的类型。定义结构体之后可用该结构体类型定义结构体变量。c中的结构可以说是c++中类的雏形,但其区别也是显而易见的,结构中只是有数据成员没有成员函数;结构中也没有public和private关键字,其中的数据成员默认都是公有的,一般函数都可以访问,而C++的类中的数据成员默认情况下都是私有的,只有类对象中的成员函数和友元函数能够访问,这样极大地提高了数据的安全性。

2.3模板与宏

模板是c++程序设计中的重要机制,可以节约程序代码,提高面向对象程序设计的可重用性和可维护性。模板把函数或类要处理的数据类型参数化,表现为参数的多态性,从而有效实现了程序设计中的代码重用。c语言中的宏定义是一种预处理方式。可改进程序设计环境,提高编程效率。其中带参数的宏定义方式不只是简单的字符串替换,还要进行参数替换,为日后学习面向对象程序设计中的函数模板奠定基础。

3区别

C是一种结构化语言,其重点在于算法和数据结构。C程序设计首要考虑如何通过一个过程,对输入或环境条件进行运算处理得到输出或实现过程控制,而c++首要考虑如何构造一个对象模型,让这个模型能契合与之对应的问题域,这样就可通过获取对象的状态信息得到输出或实现过程控制。

3.1编程思想

c++与C语言最大的区别在于编程思想的截然不同,前者是面向对象的编程语言,后者则是面向过程的结构化的编程语言。面向对象程序语言将程序设计领域与日常生活拉得更近,面向过程的C语言强调程序的功能,以函数为中心,c++在C语言这一强大巨人的肩膀上发展,通过C语言强大的软硬件控制功能,融入面向对象的编程思想。强调程序的分层。分类,以抽象的类为基础,进行对象的定义与展示,使应用程序具有封装性、继承性和多态性。

3.2关键字

c语言中的关键字几乎都可以在C++中使用,但c++中增加了一些C语言中所不支持的关键字,这些关键字能够作为函数和变量的标识符在c程序中使用,尽管C++包含了所有的c,很显然没有任何e++编译器能够编译这样的c程序。例如c++中的new和delete取代了C中的malloc和free,C++中还增加了public、private、try catch。throw等很多关键字。

3.3语法格式

c++和c语言在语法格式上也有很大的不同。c程序员可以省略函数的返回类型,C++却不可以,即使无返回也应加上void类型。注释的格式:c语言只支持**。C++还支持//在C语言中输入输出是使用scanf和printf函数来实现的,而e++是使用类来实现的,C++中用于控制1/O的iostream类库替代了C语言中的stdio函数库。C++中的try/oatch/thrOw异常处理机制取代了C中的setjmp和Iongjmp函数。C++中的引用简化了c语言中过于繁杂的指针。

4结语

C语言学习 篇4

关键词:高校,C语言编程,改善对策

1 C语言及其优势

C语言作为目前世界上使用最广泛、最流行的计算机编程语言,要想学习起来得心应手,对其的了解也是必不可少的,C语言起源于上世纪60年代,成型于70年代,在近50年的使用过程中表现出了以下特点:C语言简介紧凑、灵活方便,语法限制不严格,程序的书写比较自由,将高级编程语言的结构性和低级编程语言的实用性有效的结合起来,形成了新的功能强大的编程语言;应用广泛,可植性强,C语言适用于多种操作系统;C语言中各种运算符丰富,在C语言编程过程中灵活的使用各种运算符,可以实现其他很多高级语言无法实现的功能;数据结构丰富:多种的数据结构类型的使用使C语言除了可以完成各种复杂的数据运算外还具有强大的图形功能;结构式的语言特点使代码和数据分割,程序层次清晰,便于使用、维护、调试;用C语言可以直接对物理地址进行访问,由此可见C语言的强大功能,所以在信息技术的世界中C语言的地位是很高的。

2 学习C语言枯燥原因分析

C语言的强大功能决定了它的“江湖地位”,所以社会重视、企业重视、学校重视使得学习C语言、想掌握C语言技术的人也越来越多,但是大部分人在学习过程中却表示:C语言的学习是非常难,非常枯燥的,经常是一个学期下来感觉毫无所获,还是处于云里雾里,对学科没有整体清晰的概念。为什么会出现这样的状况呢,为什么越重视效果越不行?笔者认为C语言属于高级语言,是跟面向对象的语言有很大的区别的面向过程的编程语言,它不能直接看到很华丽的画面效果,编写过程稍有不慎就会出错,而且C语言中的很多算法和数据结构如果不加以理解而只是死记硬背的话是会非常枯燥且容易出错的。在运行过程中不能很直接的看到出错的地方,需要自己投入其中进行深入分析,还应该积极的研究算法,因为只有充分地了解算法才能够在C语言的学习上游刃有余,将C语言知识融会贯通,教师在教学过程中也不能一味的讲解,而不考虑学生的接收情况,想当然学生都应该懂。笔者认为C语言编程的学习中,C语言编程技巧可以帮助C语言的学习。

3 C语言编程技巧

现在的C语言学习过程中,学生普遍感觉枯燥无味,其实笔者认为初学C语言觉得比较复杂是人之常情,但是只要你具备了一定的编程经验,再掌握一些编程技巧,其实C语言也不是很难,而且可以更加有效地提高编程的效率和编程能力。在此介绍几种在实践中比较有用的编程技巧:

首先熟练掌握指针,指针的重要性是学习过C语言的人都知道的,指针为什么重要,因为其可以大大提高运算的速度,并且使用的语句简单,但是可以达到意想不到的效果,当然指针也有缺点,就是占用了大量的内存,但是获得了程序执行的高效率。如果系统的实时性要求很高,内存还有一些可以使用指针操作会达到良好的效果。其次运用数学方法解决问题,在C语言的学习过程中,一般有大量的算法和数据结构需要去了解,通常这个阶段是最难熬的阶段,因为对于初学者来说显得比较复杂和枯燥,IT界有一句比较实在的话“数学是计算机之母”,由此可见数学在计算机学习中的重要地位,同理,在C语言的学习过程中,数学思想的应用会让人有豁然开朗之感,一般而言程序的编写都是要解决问题,而简单的运算问题的解决其实就是在数学领域的,编写程序是为了让计算机可以代替人操作运算过程,从而减少人力。从这个角度上来说,在编程前应该用数学的思维寻找规律,用数学的威力来提高编程效率,拓展编程人员的思维。最后要用高效的C语言编程,可以使用位操作,在所有的计算机程序中,可以说位是最小的数据单位,一般而言所有的运算和操作都可以通过“位运算”来解决,足见“位”在编程语言中的重要性,在C语言编程过程中使用位操作可以使代码更简洁,效率更高,当然使用任何技巧都应该视情况而定,如位操作的使用有时可能导致程序的安全隐患。

上面介绍的几种提高编程效率的编程技巧总体来说对于学习C语言指明了方向,让人们在C语言的学习中有重点和方向可寻,必定能使得人们学习C语言的积极性提高,将C语言应用地更加广泛。

4 结语

C语言的强大功能和可植入性一直是其他编程语言望尘莫及的,所以C语言在IT界广受追捧,熟悉C语言的人都被人称为“电脑高手”,所以这些年来,C语言在高校中也有原来的计算机专业课程发展到现在的理工类公共课程,自然,这对于C语言的发展来说是一件好事。与面向对象的编程语言不同,C语言是一门面向过程的编程语言,这就决定了其对算法和数据结构的要求比较高,而算法和数据结构恰恰是编程语言中比较难的部分,正因如此,现在在C语言的学习过程中,大部分人都表示这是一门很重要却很难学的课程,要让C语言更好地服务,必须打破这种现状。笔者认为在C语言的编程教学中加入C语言编程技巧的教学可以很好地改善现状。因为C语言编程技巧可以让人们提高学习兴趣、学习起来也比较有目的性,可以很好地提高C语言的学习效果。

参考文献

[1]李利国,王磊.C语言编程风格之六大章法[J].成才之路,2007,(20).

[2]王展运.C语言编程中常见错误及解决办法[J].长江职工大学学报,2002,(03).

[3]李自清.浅谈C语言编程中的常见错误和解决方法[J].科技信息(科学教研),2007,(33).

[4]王彤,张小志,王大鹏.C语言编程的技巧探讨[J].科技信息(学术研究),2007,(09).

C语言学习心得 篇5

学习单片机也已经有几年了,藉此机会和大家聊一下我学习过程中的一些经历和想法吧。也感谢一线工人提供了这个机会。希望大家有什么好的想法和建议都直接跟帖说出来。毕竟只有交流才能够碰撞出火花来^_^。

。“卖弄”也好,“吹嘘”也罢,我只是想认真的写写我这一路走来历经的总总,把其中值得注意,以及经验的地方写出来,权当是我对自己的一个总结吧。而作为看官的你,如果看到了我的错误,还请一定指正,这样对我以及其它读者都有帮助,而至于你如果从中能够收获到些许,那便是我最大的欣慰了。姑妄言之,姑妄听之。如果有啥好的想法和建议一定要说出来。 几年前,和众多初学者一样,我接触到了单片机,立刻被其神奇的功能所吸引,从此不能自拔。很多个日夜就这样陪伴着它度过了。期间也遇到过非常多的问题,也一度被这些问题所困惑„„等到回过头来,看到自己曾经走过的路,唏嘘不已。经常混迹于论坛里,也看到了很多初学者发的求助帖子,看到他们走在自己曾走过的弯路上,忽然想到了自己的那段日子,心里竟然莫名的冲动,凡此总总,我总是尽自己所能去回帖。很多时候,都想写一点什么东西出来,希望对广大的初学者有一点点帮助。但总是不知从何处写起。今天借一线工人的台,唱一唱我的戏

一路学习过来的过程中,帮助最大之一无疑来自于网络了。很多时候,通过网络,我们都可以获取到所需要的学习资料。但是,随着我们学习的深入,我们会慢慢发现,网络提供的东西是有限度的,好像大部分的资料都差不多,或者说是适合大部分的初学者所需,而当我们想更进一步提高时,却发现能够获取到的资料越来越少,相信各位也会有同感,铺天盖地的单片机资料中大部分不是流水灯就是LED,液晶,而且也只是仅仅作功能性的演示。于是有些人选择了放弃,或者是转移到其他兴趣上面去了,而只有少部分人选择了继续摸索下去,结合市面上的书籍,然后在网络上锲而不舍的搜集资料,再从牛人的只言片语中去体会,不断动手实践,慢慢的,也摸索出来了自己的一条路子。当然这个过程必然是艰辛的,而他学会了之后也不会在网络上轻易分享自己的学习成果。如此恶性循环下去,也就不难理解为什么初级的学习资料满天飞,而深入一点的学习资料却很少的原因了。相较于其他领域,单片机技术的封锁更加容易。尽管已经问世了很多年了,有价值的资料还是相当的欠缺,大部分的资料都是止于入门阶段或者是简单的演示实验。但是在实际工程应用中却是另外一回事。有能力的高手无暇或者是不愿公开自己的学习经验。

很多时候,我也很困惑,看到国外爱好者毫不保留的在网络上发布自己的作品,我忽然感觉到一丝丝的悲哀。也许,我们真的该转变一下思路了,帮助别人,其实也是在帮助自己。啰啰嗦嗦的说了这么多,相信大家能够明白说的是什么意思。在接下来的一段日子里,我将会结合电子工程师之家举办的主题周活动写一点自己的想法。尽可能从实用的角度去讲述。希望能够帮助更多的初学者更上一层楼。而关于这个主题周的最大主题我想了这样的一个名字“从单片机初学者迈向单片机工程师”。名字挺大挺响亮,给我的压力也挺大的,但我会努力,争取使这样的一系列文章能够带给大家一点帮助,而不是看后大跌眼镜。这样的一系列文章主要的对象是初学者,以及想从初学者更进一步提高的读者。而至于老手,以及那些牛XX的人,希望能够给我们这些初学者更多的一些指点哈~@_@

我们首先来看第一章节

从这一章开始,我们开始迈入单片机的世界。在我们开始这一章具体的学习之前,有必要给大家先说明一下。在以后的系列文章中,我们将以51内核的单片机为载体,C语言为编程语言,开发环境为KEIL uv3。至于为什么选用C语言开发,好处不言而喻,开发速度快,效率高,代码可复用率高,结构清晰,尤其是在大型的程序中,而且随着编译器的不断升级,其编译后的代码大小与汇编语言的差距越来越小。而关于C语言和汇编之争,就像那个啥,每隔一段时间总会有人挑起这个话题,如果你感兴趣,可以到网上搜索相关的帖子自行阅读。不是说汇编不重要,在很多对时序要求非常高的场合,需要利用汇编语言和C语言混合编程才能够满足系统的需求。在我们学习掌握C语言的同时,也还需要利用闲余的时间去学习了解汇编语言。

1.从点亮LED(发光二极管)开始

在市面上众多的单片机学习资料中,最基础的实验无疑于点亮LED了,即控制单片机的I/O的电平的变化。

如同如下实例代码一般

void main(void){ LedInit();While(1){ LED = ON;DelayMs(500);LED = OFF;DelayMs(500);} }

程序很简单,从它的结构可以看出,LED先点亮500MS,然后熄灭500MS,如此循环下去,形成的效果就是LED以1HZ的频率进行闪烁。下面让我们分析上面的程序有没有什么问题。

看来看出,好像很正常的啊,能有什么问题呢?这个时候我们应该换一个思路去想了。试想,整个程序除了控制LED = ON ; LED = OFF; 这两条语句外,其余的时间,全消耗在了DelayMs(500)这两个函数上。而在实际应用系统中是没有哪个系统只闪烁一只LED就其它什么事情都不做了的。因此,在这里我们要想办法,把CPU解放出来,让它不要白白浪费500MS的延时等待时间。宁可让它一遍又一遍的扫描看有哪些任务需要执行,也不要让它停留在某个地方空转消耗CPU时间。

从上面我们可以总结出

(1)无论什么时候我们都要以实际应用的角度去考虑程序的编写。

(2)无论什么时候都不要让CPU白白浪费等待,尤其是延时(超过1MS)这样的地方。

下面让我们从另外一个角度来考虑如何点亮一颗LED。先看看我们的硬件结构是什么样子的。

我手上的单片机板子是电子工程师之家的开发的学习板。就以它的实际硬件连接图来分析吧。如下图所示

(原文件名:led.jpg)

引用图片

一般的LED的正常发光电流为10~20MA而低电流LED的工作电流在2mA以下(亮度与普通发光管相同)。在上图中我们可知,当Q1~Q8引脚上面的电平为低电平时,LED发光。通过LED的电流约为(VCC-Vd)/ RA2。其中Vd为LED导通后的压降,约为1.7V左右。这个导通压降根据LED颜色的不同,以及工作电流的大小的不同,会有一定的差别。下面一些参数是网上有人测出来的,供大家参考。红色的压降为1.82-1.88V,电流5-8mA,绿色的压降为1.75-1.82V,电流3-5mA,橙色的压降为1.7-1.8V,电流3-5mA 兰色的压降为3.1-3.3V,电流8-10mA,白色的压降为3-3.2V,电流10-15mA,(供电电压5V,LED直径为5mm)

74HC573真值表如下:

(原文件名:74hc573.jpg)

引用图片

通过这个真值表我们可以看出。当OutputEnable引脚接低电平的时候,并且LatchEnable引脚为高电平的时候,Q端电平与D端电平相同。结合我们的LED硬件连接图可以知道LED_CS端为高电平时候,P0口电平的变化即Q端的电平的变化,进而引起LED的亮灭变化。由于单片机的驱动能力有限,在此,74HC573的主要作用就是起一个输出驱动的作用。需要注意的是,通过74HC573的最大电流是有限制的,否则可能会烧坏74HC573这个芯片。

上面这个图是从74HC573的DATASHEET中截取出来的,从上可以看出,每个引脚允许通过的最大电流为35mA 整个芯片允许通过的最大电流为75mA。在我们设计相应的驱动电路时候,这些参数是相当重要的,而且是最容易被初学者所忽略的地方。同时在设计的时候,要留出一定量的余量出来,不能说单个引脚允许通过的电流为35mA,你就设计为35mA,这个时候你应该把设计的上限值定在20mA左右才能保证能够稳定的工作。

(设计相应驱动电路时候,应该仔细阅读芯片的数据手册,了解每个引脚的驱动能力,以及整个芯片的驱动能力)

了解了相应的硬件后,我们再来编写驱动程序。

首先定义LED的接口 #define LED P0 然后为亮灭常数定义一个宏,由硬件连接图可以,当P0输出为低电平时候LED亮,P0输出为高电平时,LED熄灭。

#define LED_ON()LED = 0x00 //所有LED亮 #define LED_OFF()LED = 0xff //所有LED熄灭

下面到了重点了,究竟该如何释放CPU,避免其做延时空等待这样的事情呢。很简单,我们为系统产生一个1MS的时标。假定LED需要亮500MS,熄灭500MS,那么我们可以对这个1MS的时标进行计数,当这个计数值达到500时候,清零该计数值,同时把LED的状态改变。unsigned int g_u16LedTimeCount = 0;//LED计数器

unsigned char g_u8LedState = 0;//LED状态标志, 0表示亮,1表示熄灭

void LedProcess(void){ if(0 == g_u8LedState)//如果LED的状态为亮,则点亮LED { LED_ON();} else //否则熄灭LED { LED_OFF();} }

void LedStateChange(void){ if(g_bSystemTime1Ms)//系统1MS时标到 { g_bSystemTime1Ms = 0;g_u16LedTimeCount++;//LED计数器加一

if(g_u16LedTimeCount >= 500)//计数达到500,即500MS到了,改变LED的状态。{ g_u16LedTimeCount = 0;g_u8LedState =!g_u8LedState;} } }

上面有一个变量没有提到,就是g_bSystemTime1Ms。这个变量可以定义为位变量或者是其它变量,在我们的定时器中断函数中对其置位,其它函数使用该变量后,应该对其复位(清0)。我们的主函数就可以写成如下形式(示意代码)void main(void){ while(1){ LedProcess();LedStateChange();} }

因为LED的亮或者灭依赖于LED状态变量(g_u8LedState)的改变,而状态变量的改变,又依赖于LED计数器的计数值(g_u16LedTimeCount,只有计数值达到一定后,状态变量才改变)所以,两个函数都没有堵塞CPU的地方。让我们来从头到尾分析一遍整个程序的流程。

程序首先执行LedProcess();函数

因为g_u8LedState 的初始值为0(见定义,对于全局变量,在定义的时候最好给其一个确定的值)所以LED被点亮,然后退出LedStateChange()函数,执行下一个函数LedStateChange()在函数LedStateChange()内部首先判断1MS的系统时标是否到了,如果没有到就直接退出函数,如果到了,就把时标清0以便下一个时标消息的到来,同时对LED计数器加一,然后再判断LED计数器是否到达我们预先想要的值500,如果没有,则退出函数,如果有,对计数器清0,以便下次重新计数,同时把LED状态变量取反,然后退出函数。

由上面整个流程可以知道,CPU所做的事情,就是对一些计数器加一,然后根据条件改变状态,再根据这个状态来决定是否点亮LED。这些函数执行所花的时间都是相当短的,如果主程序中还有其它函数,则CPU会顺次往下执行下去。对于其它的函数(如果有的话)也要采取同样的措施,保证其不堵塞CPU,如果全部基于这种方法设计,那么对于不是非常庞大的系统,我们的系统依旧可以保证多个任务(多个函数)同时执行。系统的实时性得到了一定的保证,从宏观上看来,就是多个任务并发执行。

好了,这一章就到此为止,让我们总结一下,究竟有哪些需要注意的吧。

(1)无论什么时候我们都要以实际应用的角度去考虑程序的编写。

(2)无论什么时候都不要让CPU白白浪费等待,尤其是延时(超过1MS)这样的地方。(3)设计相应驱动电路时候,应该仔细阅读芯片的数据手册,了解每个引脚的驱动能力,以及整个芯片的驱动能力

(4)最重要的是,如何去释放CPU(参考本章的例子),这是写出合格程序的基础。

附完整程序代码(基于电子工程师之家的单片机开发板)

#include

sbit LED_SEG = P1^4;//数码管段选 sbit LED_DIG = P1^5;//数码管位选 sbit LED_CS11 = P1^6;//led控制位 sbit ir=P1^7;#define LED P0 //定义LED接口

bit g_bSystemTime1Ms = 0;// 1MS系统时标 unsigned int g_u16LedTimeCount = 0;//LED计数器

unsigned char g_u8LedState = 0;//LED状态标志, 0表示亮,1表示熄灭

#define LED_ON()LED = 0x00;//所有LED亮 #define LED_OFF()LED = 0xff;//所有LED熄灭

void Timer0Init(void){ TMOD &= 0xf0;TMOD |= 0x01;//定时器0工作方式1 TH0 = 0xfc;//定时器初始值 TL0 = 0x66;TR0 = 1;ET0 = 1;} void LedProcess(void){ if(0 == g_u8LedState)//如果LED的状态为亮,则点亮LED { LED_ON();} else //否则熄灭LED { LED_OFF();} }

void LedStateChange(void){ if(g_bSystemTime1Ms)//系统1MS时标到 { g_bSystemTime1Ms = 0;g_u16LedTimeCount++;//LED计数器加一

if(g_u16LedTimeCount >= 500)//计数达到500,即500MS到了,改变LED的状态。{ g_u16LedTimeCount = 0;g_u8LedState =!g_u8LedState;} } }

void main(void){ Timer0Init();EA = 1;LED_CS11 = 1;//74HC595输出允许

LED_SEG = 0;//数码管段选和位选禁止(因为它们和LED共用P0口)LED_DIG = 0;while(1){ LedProcess();LedStateChange();} }

void Time0Isr(void)interrupt 1 { TH0 = 0xfc;//定时器重新赋初值 TL0 = 0x66;g_bSystemTime1Ms = 1;//1MS时标标志位置位 }

“从单片机初学者迈向单片机工程师”

第三章----模块化编程初识

好的开始是成功的一半

通过上一章的学习,我想你已经掌握了如何在程序中释放CPU了。希望能够继续坚持下去。一个良好的开始是成功的一半。我们今天所做的一切都是为了在单片机编程上做的更好。

在谈论今天的主题之前,先说下我以前的一些经历。在刚开始接触到C语言程序的时候,由于学习内容所限,写的程序都不是很大,一般也就几百行而矣。所以所有的程序都完成在一个源文件里面。记得那时候大一参加学校里的一个电子设计大赛,调试了一个多星期,所有程序加起来大概将近1000行,长长的一个文件,从上浏览下来都要好半天。出了错误简单的语法错误还好定位,其它一些错误,往往找半天才找的到。那个时候开始知道了模块化编程这个东西,也尝试着开始把程序分模块编写。最开始是把相同功能的一些函数(譬如1602液晶的驱动)全部写在一个头文件(.h)文件里面,然后需要调用的地方包含进去,但是很快发现这种方法有其局限性,很容易犯重复包含的错误。

而且调用起来也很不方便。很快暑假的电子设计大赛来临了,学校对我们的单片机软件编程进行了一些培训。由于学校历年来参加国赛和省赛,因此积累了一定数量的驱动模块,那些日子,老师每天都会布置一定量的任务,让我们用这些模块组合起来,完成一定功能。而正是那些日子模块化编程的培训,使我对于模块化编程有了更进一步的认识。并且程序规范也开始慢慢注意起来。此后的日子,无论程序的大小,均采用模块化编程的方式去编写。很长一段时间以来,一直有单片机爱好者在QQ上和我一起交流。有时候,他们会发过来一些有问题的程序源文件,让我帮忙修改一下。同样是长长的一个文件,而且命名极不规范,从头看下来,着实是痛苦,说实话,还真不如我重新给他们写一个更快一些,此话到不假,因为手头积累了一定量的模块,在完成一个新的系统时候,只需要根据上层功能需求,在底层模块的支持下,可以很快方便的完成。而不需要从头到尾再一砖一瓦的重新编写。藉此,也可以看出模块化编程的一个好处,就是可重复利用率高。下面让我们揭开模块化神秘面纱,一窥其真面目。C语言源文件 *.c 提到C语言源文件,大家都不会陌生。因为我们平常写的程序代码几乎都在这个XX.C文件里面。编译器也是以此文件来进行编译并生成相应的目标文件。作为模块化编程的组成基础,我们所要实现的所有功能的源代码均在这个文件里。理想的模块化应该可以看成是一个黑盒子。即我们只关心模块提供的功能,而不管模块内部的实现细节。好比我们买了一部手机,我们只需要会用手机提供的功能即可,不需要知晓它是如何把短信发出去的,如何响应我们按键的输入,这些过程对我们用户而言,就是是一个黑盒子。

在大规模程序开发中,一个程序由很多个模块组成,很可能,这些模块的编写任务被分配到不同的人。而你在编写这个模块的时候很可能就需要利用到别人写好的模块的借口,这个时候我们关心的是,它的模块实现了什么样的接口,我该如何去调用,至于模块内部是如何组织的,对于我而言,无需过多关注。而追求接口的单一性,把不需要的细节尽可能对外部屏蔽起来,正是我们所需要注意的地方。C语言头文件 *.h 谈及到模块化编程,必然会涉及到多文件编译,也就是工程编译。在这样的一个系统中,往往会有多个C文件,而且每个C文件的作用不尽相同。在我们的C文件中,由于需要对外提供接口,因此必须有一些函数或者是变量提供给外部其它文件进行调用。假设我们有一个LCD.C文件,其提供最基本的LCD的驱动函数 LcdPutChar(char cNewValue);//在当前位置输出一个字符 而在我们的另外一个文件中需要调用此函数,那么我们该如何做呢?

头文件的作用正是在此。可以称其为一份接口描述文件。其文件内部不应该包含任何实质性的函数代码。我们可以把这个头文件理解成为一份说明书,说明的内容就是我们的模块对外提供的接口函数或者是接口变量。同时该文件也包含了一些很重要的宏定义以及一些结构体的信息,离开了这些信息,很可能就无法正常使用接口函数或者是接口变量。但是总的原则是:不该让外界知道的信息就不应该出现在头文件里,而外界调用模块内接口函数或者是接口变量所必须的信息就一定要出现在头文件里,否则,外界就无法正确的调用我们提供的接口功能。因而为了让外部函数或者文件调用我们提供的接口功能,就必须包含我们提供的这个接口描述文件----即头文件。同时,我们自身模块也需要包含这份模块头文件(因为其包含了模块源文件中所需要的宏定义或者是结构体),好比我们平常所用的文件都是一式三份一样,模块本身也需要包含这个头文件。

下面我们来定义这个头文件,一般来说,头文件的名字应该与源文件的名字保持一致,这样我们便可以清晰的知道哪个头文件是哪个源文件的描述。

于是便得到了LCD.C的头文件LCD.h 其内容如下。#ifndef _LCD_H_ #define _LCD_H_ extern LcdPutChar(char cNewValue);#endif

这与我们在源文件中定义函数时有点类似。不同的是,在其前面添加了extern 修饰符表明其是一个外部函数,可以被外部其它模块进行调用。#ifndef _LCD_H_ #define _LCD_H_ #endif

这个几条条件编译和宏定义是为了防止重复包含。假如有两个不同源文件需要调用LcdPutChar(char cNewValue)这个函数,他们分别都通过#include “Lcd.h”把这个头文件包含了进去。在第一个源文件进行编译时候,由于没有定义过 _LCD_H_ 因此 #ifndef _LCD_H_ 条件成立,于是定义_LCD_H_ 并将下面的声明包含进去。在第二个文件编译时候,由于第一个文件包含时候,已经将_LCD_H_定义过了。因此#ifndef _LCD_H_ 不成立,整个头文件内容就没有被包含。假设没有这样的条件编译语句,那么两个文件都包含了extern LcdPutChar(char cNewValue);就会引起重复包含的错误。

不得不说的typedef 很多朋友似乎了习惯程序中利用如下语句来对数据类型进行定义 #define uint unsigned int #define uchar unsigned char 然后在定义变量的时候 直接这样使用 uint g_nTimeCounter = 0;不可否认,这样确实很方便,而且对于移植起来也有一定的方便性。但是考虑下面这种情况你还会 这么认为吗?

#define PINT unsigned int * //定义unsigned int 指针类型 PINT g_npTimeCounter, g_npTimeState;那么你到底是定义了两个unsigned int 型的指针变量,还是一个指针变量,一个整形变量呢?而你的初衷又是什么呢,想定义两个unsigned int 型的指针变量吗?如果是这样,那么估计过不久就会到处抓狂找错误了。

庆幸的是C语言已经为我们考虑到了这一点。typedef 正是为此而生。为了给变量起一个别名我们可以用如下的语句

typedef unsigned int uint16;//给指向无符号整形变量起一个别名 uint16 typedef unsigned int * puint16;//给指向无符号整形变量指针起一个别名 puint16 在我们定义变量时候便可以这样定义了:

uint16 g_nTimeCounter = 0;//定义一个无符号的整形变量 puint16 g_npTimeCounter;//定义一个无符号的整形变量的指针

在我们使用51单片机的C语言编程的时候,整形变量的范围是16位,而在基于32的微处理下的整形变量是32位。倘若我们在8位单片机下编写的一些代码想要移植到32位的处理器上,那么很可能我们就需要在源文件中到处修改变量的类型定义。这是一件庞大的工作,为了考虑程序的可移植性,在一开始,我们就应该养成良好的习惯,用变量的别名进行定义。如在8位单片机的平台下,有如下一个变量定义 uint16 g_nTimeCounter = 0;如果移植32单片机的平台下,想要其的范围依旧为16位。

可以直接修改uint16 的定义,即

typedef unsigned short int uint16;这样就可以了,而不需要到源文件处处寻找并修改。

将常用的数据类型全部采用此种方法定义,形成一个头文件,便于我们以后编程直接调用。文件名 MacroAndConst.h 其内容如下:

#ifndef _MACRO_AND_CONST_H_ #define _MACRO_AND_CONST_H_

typedef unsigned int uint16;typedef unsigned int UINT;typedef unsigned int uint;typedef unsigned int UINT16;typedef unsigned int WORD;typedef unsigned int word;typedef int int16;typedef int INT16;typedef unsigned long uint32;

typedef unsigned long UINT32;typedef unsigned long DWORD;typedef unsigned long dword;typedef long int32;typedef long INT32;typedef signed char int8;typedef signed char INT8;typedef unsigned char byte;typedef unsigned char BYTE;typedef unsigned char uchar;typedef unsigned char UINT8;typedef unsigned char uint8;typedef unsigned char BOOL;#endif

至此,似乎我们对于源文件和头文件的分工以及模块化编程有那么一点概念了。那么让我们趁热打铁,将上一章的我们编写的LED闪烁函数进行模块划分并重新组织进行编译。

在上一章中我们主要完成的功能是P0口所驱动的LED以1Hz的频率闪烁。其中用到了定时器,以及LED驱动模块。因而我们可以简单的将整个工程分成三个模块,定时器模块,LED模块,以及主函数 对应的文件关系如下

main.c Timer.hTimer.c--Led.hLed.c--在开始重新编写我们的程序之前,先给大家讲一下如何在KEIL中建立工程模板吧,这个模板是我一直沿用至今。希望能够给大家一点启发。

下面的内容就主要以图片为主了。同时辅以少量文字说明。我们以芯片AT89S52为例。

(原文件名:1.jpg)

引用图片

(原文件名:2.jpg)

引用图片

(原文件名:3.jpg)

引用图片

(原文件名:4.jpg)

引用图片

(原文件名:5.jpg)

引用图片

(原文件名:6.jpg)

引用图片

(原文件名:7.jpg)

引用图片

(原文件名:8.jpg)

引用图片

(原文件名:9.jpg)

引用图片

(原文件名:10.jpg)

引用图片

(原文件名:11.jpg)

引用图片

(原文件名:12.jpg)

引用图片

(原文件名:13.jpg)

引用图片

(原文件名:14.jpg)

引用图片

(原文件名:15.jpg)

引用图片

(原文件名:16.jpg)

引用图片

(原文件名:17.jpg)

引用图片

(原文件名:18.jpg)

引用图片

(原文件名:19.jpg)

引用图片

(原文件名:20.jpg)

引用图片

(原文件名:21.jpg)

引用图片

(原文件名:22.jpg)

引用图片

OK,到此一个简单的工程模板就建立起来了,以后我们再新建源文件和头文件的时候,就可以直接保存到src文件目录下面了。

下面我们开始编写各个模块文件。

首先编写Timer.c 这个文件主要内容就是定时器初始化,以及定时器中断服务函数。其内容如下。#include

bit g_bSystemTime1Ms = 0;// 1MS系统时标

void Timer0Init(void){ TMOD &= 0xf0;TMOD |= 0x01;//定时器0工作方式1 TH0 = 0xfc;//定时器初始值 TL0 = 0x66;TR0 = 1;ET0 = 1;}

void Time0Isr(void)interrupt 1 { TH0 = 0xfc;//定时器重新赋初值 TL0 = 0x66;g_bSystemTime1Ms = 1;//1MS时标标志位置位 }

由于在Led.c文件中需要调用我们的g_bSystemTime1Ms变量。同时主函数需要调用Timer0Init()初始化函数,所以应该对这个变量和函数在头文件里作外部声明。以方便其它函数调用。

Timer.h 内容如下。#ifndef _TIMER_H_ #define _TIMER_H_

extern void Timer0Init(void);extern bit g_bSystemTime1Ms;#endif

完成了定时器模块后,我们开始编写LED驱动模块。Led.c 内容如下:

#include #include “MacroAndConst.h” #include “Led.h” #include “Timer.h”

static uint16 g_u16LedTimeCount = 0;//LED计数器 static uint8 g_u8LedState = 0;//LED状态标志, 0表示亮,1表示熄灭

#define LED P0 //定义LED接口

#define LED_ON()LED = 0x00;//所有LED亮 #define LED_OFF()LED = 0xff;//所有LED熄灭

void LedProcess(void){ if(0 == g_u8LedState)//如果LED的状态为亮,则点亮LED { LED_ON();} else //否则熄灭LED { LED_OFF();} }

void LedStateChange(void){ if(g_bSystemTime1Ms)//系统1MS时标到 { g_bSystemTime1Ms = 0;g_u16LedTimeCount++;//LED计数器加一

if(g_u16LedTimeCount >= 500)//计数达到500,即500MS到了,改变LED的状态。{ g_u16LedTimeCount = 0;g_u8LedState =!g_u8LedState;} } }

这个模块对外的借口只有两个函数,因此在相应的Led.h 中需要作相应的声明。Led.h 内容: #ifndef _LED_H_ #define _LED_H_

extern void LedProcess(void);extern void LedStateChange(void);#endif

这两个模块完成后,我们将其C文件添加到工程中。然后开始编写主函数里的代码。如下所示:

#include #include “MacroAndConst.h” #include “Timer.h” #include “Led.h”

sbit LED_SEG = P1^4;//数码管段选 sbit LED_DIG = P1^5;//数码管位选 sbit LED_CS11 = P1^6;//led控制位

void main(void){ LED_CS11 = 1;//74HC595输出允许

LED_SEG = 0;//数码管段选和位选禁止(因为它们和LED共用P0口)LED_DIG = 0;Timer0Init();EA = 1;while(1){ LedProcess();LedStateChange();} }

整个工程截图如下:

至此,第三章到此结束。

一起来总结一下我们需要注意的地方吧

[color=#FF0000]1.C语言源文件(*.c)的作用是什么 2.C语言头文件(*.h)的作用是什么 3.typedef 的作用 4.工程模板如何组织

5.如何创建一个多模块(多文件)的工程

“从单片机初学者迈向单片机工程师”之KEY主题讨论

按键程序编写的基础

从这一章开始,我们步入按键程序设计的殿堂。在基于单片机为核心构成的应用系统中,用户输入是必不可少的一部分。输入可以分很多种情况,譬如有的系统支持PS2键盘的接口,有的系统输入是基于编码器,有的系统输入是基于串口或者USB或者其它输入通道等等。在各种输入途径中,更常见的是,基于单个按键或者由单个键盘按照一定排列构成的矩阵键盘(行列键盘)。我们这一篇章主要讨论的对象就是基于单个按键的程序设计,以及矩阵键盘的程序编写。◎按键检测的原理

常见的独立按键的外观如下,相信大家并不陌生,各种常见的开发板学习板上随处可以看到他们的身影。

(原文件名:1.jpg)

引用图片

总共有四个引脚,一般情况下,处于同一边的两个引脚内部是连接在一起的,如何分辨两个引脚是否处在同一边呢?可以将按键翻转过来,处于同一边的两个引脚,有一条突起的线将他们连接一起,以标示它们俩是相连的。如果无法观察得到,用数字万用表的二极管挡位检测一下即可。搞清楚这点非常重要,对于我们画PCB的时候的封装很有益。

它们和我们的单片机系统的I/O口连接一般如下:

(原文件名:2.jpg)

引用图片

对于单片机I/O内部有上拉电阻的微控制器而言,还可以省掉外部的那个上拉电阻。简单分析一下按键检测的原理。当按键没有按下的时候,单片机I/O通过上拉电阻R接到VCC,我们在程序中读取该I/O的电平的时候,其值为1(高电平);当按键S按下的时候,该I/O被短接到GND,在程序中读取该I/O的电平的时候,其值为0(低电平)。这样,按键的按下与否,就和与该按键相连的I/O的电平的变化相对应起来。结论:我们在程序中通过检测到该I/O口电平的变化与否,即可以知道按键是否被按下,从而做出相应的响应。一切看起来很美好,是这样的吗?

◎现实并非理想

在我们通过上面的按键检测原理得出上述的结论的时候,其实忽略了一个重要的问题,那就是现实中按键按下时候的电平变化状态。我们的结论是基于理想的情况得出来的,就如同下面这幅按键按下时候对应电平变化的波形图一样:

(原文件名:3.jpg)

引用图片

而实际中,由于按键的弹片接触的时候,并不是一接触就紧紧的闭合,它还存在一定的抖动,尽管这个时间非常的短暂,但是对于我们执行时间以us为计算单位的微控制器来说,它太漫长了。因而,实际的波形图应该如下面这幅示意图一样。

(原文件名:4.jpg)

引用图片

这样便存在这样一个问题。假设我们的系统有这样功能需求:在检测到按键按下的时候,将某个I/O的状态取反。由于这种抖动的存在,使得我们的微控制器误以为是多次按键的按下,从而将某个I/O的状态不断取反,这并不是我们想要的效果,假如该I/O控制着系统中某个重要的执行的部件,那结果更不是我们所期待的。于是乎有人便提出了软件消除抖动的思想,道理很简单:抖动的时间长度是一定的,只要我们避开这段抖动时期,检测稳定的时候的电平不久可以了吗?听起来确实不错,而且实际应用起来效果也还可以。于是,各种各样的书籍中,在提到按键检测的时候,总也不忘说道软件消抖。就像下面的伪代码所描述的一样。(假设按键按下时候,低电平有效)

If(0 == io_KeyEnter)//如果有键按下了 { Delayms(20);//先延时20ms避开抖动时期

If(0 == io_KeyEnter)//然后再检测,如果还是检测到有键按下 { return KeyValue;//是真的按下了,返回键值 } else { return KEY_NULL //是抖动,返回空的键值 } while(0 == io_KeyEnter);//等待按键释放 }

所以合理的分配好微控制的处理时间,是编写按键程序的基础。乍看上去,确实挺不错,实际中呢?在实际的系统中,一般是不允许这么样做的。为什么呢?首先,这里的Delayms(20), 让微控制器在这里白白等待了20 ms 的时间,啥也没干,考虑我在《学会释放CPU》一章中所提及的几点,这是不可取的。其次while(0 == io_KeyEnter);更是程序设计中的大忌(极少的特殊情况例外)。任何非极端情况下,都不要使用这样语句来堵塞微控制器的执行进程。原本是等待按键释放,结果CPU就一直死死的盯住该按键,其它事情都不管了,那其它事情不干了吗?你同意别人可不会同意

◎消除抖动有必要吗? 的确,软件上的消抖确实可以保证按键的有效检测。但是,这种消抖确实有必要吗?有人提出了这样的疑问。抖动是按键按下的过程中产生的,如果按键没有按下,抖动会产生吗?如果没有按键按下,抖动也会在I/O上出现,我会立刻把这个微控制器锤了,永远不用这样一款微控制器。所以抖动的出现即意味着按键已经按下,尽管这个电平还没有稳定。所以只要我们检测到按键按下,即可以返回键值,问题的关键是,在你执行完其它任务的时候,再次执行我们的按键任务的时候,抖动过程还没有结束,这样便有可能造成重复检测。所以,如何在返回键值后,避免重复检测,或者在按键一按下就执行功能函数,当功能函数的执行时间小于抖动时间时候,如何避免再次执行功能函数,就成为我们要考虑的问题了。这是一个仁者见仁,智者见智的问题,就留给大家去思考吧。所以消除抖动的目的是:防止按键一次按下,多次响应。

“从单片机初学者迈向单片机工程师”之KEY主题讨论

基于状态转移的独立按键程序设计)的那种,有一个小液晶屏,还有四个按键,功能是时钟,闹钟以及秒表。在调整时间的时候,短按+键每次调整值加一,长按的时候调整值连续增加。小的时候很好奇,这样的功能到底是如何实现的呢,今天就让我们来剖析它的原理吧。 本章所描述的按键程序要达到的目的:检测按键按下,短按,长按,释放。即通过按键的返回值我们可以获取到如下的信息:按键按下(短按),按键长按,按键连_发,按键释放。不知道大家还记得小时候玩过的电子钟没有,就是外形类似于CALL 机(CALL 机,好像是很古老的东西了 状态在生活中随处可见。譬如早上的时候,闹钟把你叫醒了,这个时候,你便处于清醒的状态,马上你就穿衣起床洗漱吃早餐,这一系列事情就是你在这个状态做的事情。做完这些后你会去等车或者开车去上班,这个时候你就处在上班途中的状态„..中午下班时间到了,你就处于中午下班的状态,诸如此类等等,在每一个状态我们都会做一些不同的事情,而总会有外界条件促使我们转换到另外一种状态,譬如闹钟叫醒我们了,下班时间到了等等。对于状态的定义出发点不同,考虑的方向不同,或者会有些许细节上面的差异,但是大的状态总是相同的。生活中的事物同样遵循同样的规律,譬如,用一个智能充电器给你的手机电池充电,刚开始,它是处于快速充电状态,随着电量的增加,电压的升高,当达到规定的电压时候,它会转换到恒压充电。总而言之,细心观察,你会发现生活中的总总都可以归结为一个个的状态,而状态的变换或者转移总是由某些条件引起同时伴随着一些动作的发生。我们的按键亦遵循同样的规律,下面让我们来简单的描绘一下它的状态流程转移图。

(原文件名:1.jpg)

引用图片

下面对上面的流程图进行简要的分析。首先按键程序进入初始状态S1,在这个状态下,检测按键是否按下,如果有按下,则进入按键消抖状态2,在下一次执行按键程序时候,直接由按键消抖状态进入按键按下状态3,在此状态下检测按键是否按下,如果没有按键按下,则返回初始状态S1,如果有则可以返回键值,同时进入长按状态S4,在长按状态下每次进入按键程序时候对按键时间计数,当计数值超过设定阈值时候,则表明长按事件发生,同时进入按键连_发状态S5。如果按键键值为空键,则返回按键释放状态S6,否则继续停留在本状态。在按键连_发状态下,如果按键键值为空键则返回按键释放状态S6,如果按键时间计数超过连_发阈值,则返回连_发按键值,清零时间计数后继续停留在本状态。

看了这么多,也许你已经有一个模糊的概念了,下面让我们趁热打铁,一起来动手编写按键驱动程序吧。

下面是我使用的硬件的连接图。

(原文件名:2.jpg)

引用图片

硬件连接很简单,四个独立按键分别接在P3^0------P3^3四个I/O上面。

因为51单片机I/O口内部结构的限制,在读取外部引脚状态的时候,需要向端口写1.在51单片机复位后,不需要进行此操作也可以进行读取外部引脚的操作。因此,在按键的端口没有复用的情况下,可以省略此步骤。而对于其它一些真正双向I/O口的单片机来说,将引脚设置成输入状态,是必不可少的一个步骤。下面的程序代码初始化引脚为输入。void KeyInit(void){ io_key_1 = 1;io_key_2 = 1;io_key_3 = 1;io_key_4 = 1;} 根据按键硬件连接定义按键键值

#define KEY_VALUE_1 0x0e #define KEY_VALUE_2 0x0d #define KEY_VALUE_3 0x0b #define KEY_VALUE_4 0x07 #define KEY_NULL 0x0f 下面我们来编写按键的硬件驱动程序。

根据第一章所描述的按键检测原理,我们可以很容易的得出如下的代码: static uint8 KeyScan(void){ if(io_key_1 == 0)return KEY_VALUE_1;if(io_key_2 == 0)return KEY_VALUE_2;if(io_key_3 == 0)return KEY_VALUE_3;if(io_key_4 == 0)return KEY_VALUE_4;return KEY_NULL;} 其中io_key_1等是我们按键端口的定义,如下所示: sbit io_key_1 = P3^0;sbit io_key_2 = P3^1;sbit io_key_3 = P3^2;sbit io_key_4 = P3^3;

KeyScan()作为底层按键的驱动程序,为上层按键扫描提供一个接口,这样我们编写的上层按键扫描函数可以几乎不用修改就可以拿到我们的其它程序中去使用,使得程序复用性大大提高。同时,通过有意识的将与底层硬件连接紧密的程序和与硬件无关的代码分开写,使得程序结构层次清晰,可移植性也更好。对于单片机类的程序而言,能够做到函数级别的代码重用已经足够了。在编写我们的上层按键扫描函数之前,需要先完成一些宏定义。//定义长按键的TICK数,以及连_发间隔的TICK数 #define KEY_LONG_PERIOD 100 #define KEY_CONTINUE_PERIOD 25

//定义按键返回值状态(按下,长按,连_发,释放)#define KEY_DOWN 0x80 #define KEY_LONG 0x40 #define KEY_CONTINUE 0x20 #define KEY_UP 0x10

//定义按键状态

#define KEY_STATE_INIT 0 #define KEY_STATE_WOBBLE 1 #define KEY_STATE_PRESS 2 #define KEY_STATE_LONG 3 #define KEY_STATE_CONTINUE 4 #define KEY_STATE_RELEASE 5

接着我们开始编写完整的上层按键扫描函数,按键的短按,长按,连按,释放等等状态的判断均是在此函数中完成。对照状态流程转移图,然后再看下面的函数代码,可以更容易的去理解函数的执行流程。完整的函数代码如下: void GetKey(uint8 *pKeyValue){ static uint8 s_u8KeyState = KEY_STATE_INIT;static uint8 s_u8KeyTimeCount = 0;static uint8 s_u8LastKey = KEY_NULL;//保存按键释放时候的键值 uint8 KeyTemp = KEY_NULL;

KeyTemp = KeyScan();//获取键值

switch(s_u8KeyState){ case KEY_STATE_INIT : { if(KEY_NULL!=(KeyTemp)){ s_u8KeyState = KEY_STATE_WOBBLE;} } break;

case KEY_STATE_WOBBLE : //消抖 { s_u8KeyState = KEY_STATE_PRESS;} break;

case KEY_STATE_PRESS : { if(KEY_NULL!=(KeyTemp)){ s_u8LastKey = KeyTemp;//保存键值,以便在释放按键状态返回键值 KeyTemp |= KEY_DOWN;//按键按下 s_u8KeyState = KEY_STATE_LONG;} else { s_u8KeyState = KEY_STATE_INIT;} } break;

case KEY_STATE_LONG : { if(KEY_NULL!=(KeyTemp)){ if(++s_u8KeyTimeCount > KEY_LONG_PERIOD){ s_u8KeyTimeCount = 0;KeyTemp |= KEY_LONG;//长按键事件发生 s_u8KeyState = KEY_STATE_CONTINUE;} } else { s_u8KeyState = KEY_STATE_RELEASE;} } break;

case KEY_STATE_CONTINUE : { if(KEY_NULL!=(KeyTemp)){ if(++s_u8KeyTimeCount > KEY_CONTINUE_PERIOD){ s_u8KeyTimeCount = 0;KeyTemp |= KEY_CONTINUE;} } else { s_u8KeyState = KEY_STATE_RELEASE;} } break;

case KEY_STATE_RELEASE : { s_u8LastKey |= KEY_UP;KeyTemp = s_u8LastKey;s_u8KeyState = KEY_STATE_INIT;} break;

default : break;} *pKeyValue = KeyTemp;//返回键值 } 关于这个函数内部的细节我并不打算花过多笔墨去讲解。对照着按键状态流程转移图,然后去看程序代码,你会发现其实思路非常清晰。最能让人理解透彻的,莫非就是将整个程序自己看懂,然后想象为什么这个地方要这样写,抱着思考的态度去阅读程序,你会发现自己的程序水平会慢慢的提高。所以我更希望的是你能够认认真真的看完,然后思考。也许你会收获更多。

不管怎么样,这样的一个程序已经完成了本章开始时候要求的功能:按下,长按,连按,释放。事实上,如果掌握了这种基于状态转移的思想,你会发现要求实现其它按键功能,譬如,多键按下,功能键等等,亦相当简单,在下一章,我们就去实现它。

在主程序中我编写了这样的一段代码,来演示我实现的按键功能。void main(void){ uint8 KeyValue = KEY_NULL;uint8 temp = 0;LED_CS11 = 1;//流水灯输出允许 LED_SEG = 0;LED_DIG = 0;Timer0Init();KeyInit();EA = 1;while(1){ Timer0MainLoop();KeyMainLoop(&KeyValue);

C语言教学改革探索 篇6

关键词:c语言 程序设计能力 教学改革

C语言程序设计课程是白城师范学院计算机科学与技术、计算机应用和软件工程等专业的一门专业基础课,是数据结构、操作系统等专业课程的先导课程,因此占有举足轻重的地位。C语言程序设计课程的教学目标是以C语言为平台,使学生全面了解程序设计的总体思路、程序设计的基本算法、并能使用C语言进行构化程序设计,为今后使用C语言解决实际问题打下良好的基础。由于C语言比较灵活,而且是学生学习的第一门程序设计课程,所以如何让学生在较短的时间内迅速掌握新知识,真正有效培养学生的实际编程能力、分析和解决问题的能力并能够加以应用,是当前迫切需要解决的问题。

1 C语言教学中存在的问题分析

目前,传统的C语言教学普遍存在着以老师理论讲授为主,先介绍语法规则,再举例验证和说明,仅从语言的使用进行教学,忽视了知识的综合运用,缺乏对学生学习兴趣的培养,学生容易感到枯燥乏味,不能充分调动学习的积极性和动力,很多学生上课能够听得懂,自己编程时却无从下手,造成学生由于不能独立完成教师布置的任务,而使学生产生恐惧和厌学心理。C语言教学中还普遍存在重理论,对课程的实践性重视不够的现象。以往学生的实验内容大部分是验证性的,较死板和枯燥,实际知识运用的训练较少,学生兴趣不大,实践项目的设计没有充分考虑能力目标的培养而过多关注程序语言本身。针对C语言教学中存在的问题和现象,作者积极进行教学改革和探索,以求提高学生的编程能力和分析、解决问题的能力。

2 C语言教学改革措施探讨

2.1 C语言理论教学改革措施

由于传统的教学内容和方法枯燥,学生学习没有兴趣,缺乏主动性,如何激发学生的学习热情呢?

兴趣是最好的老师,创造一个学生乐学的环境。首先要建立良好的师生关系,学生喜欢老师,做学生的知心朋友,与学生建立起信任、友爱关系,学生会从心理上愿意学习老师讲授的课程。其次,教师应精心设计教学内容,改变过去单一的“满堂灌”的注入式教学。在课堂中引入大量经典的、趣味性浓的、联系生活实际的案例,在教学过程中,注意设置疑难问题,通过耐心的引导使学生渐入佳境,设置问题让学生自主探讨解体之道,让他们的思想活跃起来,并提高其学习编程的兴趣,从而更深入地展开学习。同时,各章节教学案例的设计兼顾知识结构的连贯性,由前一个实训项目为基础开展下一个实训项目的研究。各项目的实训内容接续紧密,循序渐进,知识结构螺旋式上升,教师主导课堂进度,真正突出学生的主体作用,使学生在轻松活跃的教学氛围中拓展思维,自主探索。这套教学相长的教学模式突出了学生的主体地位,极大的激发了学生的主观能动性,教学氛围非常活跃。在教学过程中不断对学生强化学习算法是为了解决实际问题,并引导学生进行算法分析和设计实践,真正做到学以致用。第三,将多媒体教学与传统教学有机结合,能给传统教学方式注入新的活力。现代化多媒体教学手段是基于传统讲学模式发展而来,多媒体在课堂教学中的应用,使刻板的讲学变得生动形象。并不是所有教学内容都适合采用多媒体,教师可以有选择地进行融合和重组,进行多媒体课程设计时,注意将多媒体与板书两种教学手段相互融合,使教学节奏更加紧凑。通过图文、声像与板书的融合运用,使讲学内容更加形象、具体,以活跃学生的思维,激发其求知欲望,从而形成学习动机。

2.2 C语言实验教学改革措施

实践课程是C语言程序设计教学中的重要组成部分。单向灌输式理论讲学只能丰富学生的理论知识,但无法培养其实践技能。这恰恰与C语言课堂的教学目标背道而驰。实践教学需要辅以有效的上机操作。在C语言实验教学中,普遍存在实验题目枯燥单一,且与实际脱节现象。造成实验教学课程机械,学生没有兴趣,学习主动性差,学生写程序能力不能得到实际提高。所以在实验教学过程中,我们应该多设计一些跟实际生活相关的实验。设置必做与选做题,以满足各个层次学生的需要。上机操作前,教师须明确教学内容和上机任务,课前要求学生编好要调试的程序,为上机做好准备。程序的调试是一个技巧性很强的工作,对于初学者来说,尽快掌握程序调试方法是非常重要的。上机内容除了调试程序外,还要要求学生分析程序算法结构。调试成功后,逐步引导学生基于理论知识进一步优化算法,锻炼其实际应用能力,课后要求学生完成实验报告。本课程结束后安排一次综合性实验,旨在要求学生综合利用所学的程序设计知识,编程解决实际问题,提高分析、解决问题的能力。

2.3 C语言课程组全体教师共同设计和开发了C语言网络课程平台,该平台为学生提供了课程简介、课程教案、教学大纲、考试大纲、实验教学大纲、教学日历、习题、开放实验项目、在线测试、在线交流等网络教学资源,为教师和学生开辟了一个开放式的辅助教学空间,能引导学生自主性学习,为开展教学和课程辅导提供了有利条件,弥补了课堂和实验教学的不足,大大活跃和丰富了教学活动。实践证明可以进一步调动学生的学习积极性,培养学生的自主学习能力和问题求解能力。

2.4 以ACM竞赛为依托,开发在线评测系统(简称OJ),培养学生实际动手能力与团结协作精神。ACM国际大学生程序设计竞赛已经成为最具影响力的大学生计算机竞赛。旨在使大学生运用计算机来充分展示自己分析问题和解决问题的能力。白城师范学院计算机科学学院学生每年都组建几支队伍参加省大学生程序设计竞赛的选拔。为此,我系教师共同研制开发了在线评测系统,学生可以在在线评测系统上挑选各种题目,挑战自我,提高自我,学习各种数据结构和算法;同时还可以模拟比赛的环境,培养学生团结合作能力,真正实现“以任务为驱动,教师为主导,学生为主体,强调自主学习,客观评价,培养协作能力,创新能力和探究能力”的实验教学新平台。

3 结束语

程序设计能力是理论和实践相结合的具体表现,作者根据多年的教学经验,对C语言理论和实验教学的改革措施进行了深入探讨,并通过开发网络课程平台和ACM在线评测系统,进一步提高了学生学习的积极性和主动性,增强了学生的实际编程和问题求解能力,真正实现了C语言程序设计课程的教学目标。

参考文献:

[1]李豫颖.C语言教学改革浅见[J].贵州教育学院学报,2008(09).

[2]张敬.关于C语言教学改革的若干思考[J].电脑知识与技术,2012(09).

[3]李玺,李亭升.大学C语言教学改革探索[J].中小企业管理与科技(下旬刊),2012(03).

C语言学习 篇7

关键词:汇编语言,C/C++语言

在计算机系统的应用程序中, 所有汇编任务都由汇编语言来完成, 这样虽然完成任务的效率很高, 但是工作量却非常大, 会影响到对于应用程序的维护。若是所有的程序都是由C/C++语言进行编程, 虽然执行过程比较的简单, 但是这种方式却导致了目标代码的执行任务效率低, 实时性很差。所以在嵌入式系统中, 主要采用的编程方式是汇编语言与C/C++语言混合编程形式, 在编程过程中, 初始化任务由汇编语言来完成, 主要的任务则是由C/C++语言来完成, 从而达到了最大的效果。

1 ARM处理器

ARM处理器主要是利用编译器将汇编语言以及C/C++语言进行相互的切换, 并且ARM制定了相关标准, 来保障切换过程中的顺利实施。

1.1 寄存器使用规则

ATPCS为ARM寄存器进行不同命名, 从而在编程的过程中来利用ATPCS寄存器进行编程。

1.2 堆栈的使用规则

在ATPCS中规定, 堆栈的主要类型是FD, 被称为满递减堆栈, 所以必须要利用STMFD/LDMFF进行堆栈的操作。

1.3 参数传递规则

参数传递的规则不一样, 利用参数的数量可以将子程序分为参数数量固定, 以及参数数量不固定的子程序, 对于参数变化的子程序, 若参数的数量少于四个, 那么要利用寄存器来进行参数的传递, 若参数的数量多余四个, 那么必须要利用堆栈来进行参数的传递, 所以参数之间传递的规则不同。

2 C语言

2.1 汇编语言调用C语言

在对计算机程序的开发中, 前期由汇编语言进行初始化的编程, 然后在进行C语言之间的切换, 对于C语言的切换, 主要是通过BL来实现的。以下是调用的字符:

汇编语言的集成环境如图1所示。

2.2 C语言调用汇编语言

在程序中, 使用“C++”程序来调用C程序, 以下是调用C程序的字符:

2.3 汇编程序调用C++程序

在汇编语言与C++程序的切换时, 必须要利用关键词进行声明, 在C++程序的结构中, 如果没有基类, 则要使相应的存储结构与ARMC相同。并且在汇编过程中将参数的数据放在数据栈中, 只有这样才能使被调用的C++程序访问到相应的参数。

3 混合编程

在进行混合编程的过程中, 若汇编代码较短, 那么可以利用内嵌汇编的方式进行混合编程, 不会直接指定寄存器, 而是直接利用编译器进行分配。主要的内嵌汇编语言如下:

以下是利用字符串复制的方式实现混合汇编:

4 结语

在计算机系统嵌入式应用程序的开发中, 利用ARM汇编语言与C/C++语言相互结合的混合编程方式, 可以在很大程度上提高编程的最佳效果。本文主要举出了ARM汇编语言以及C/C++语言汇编中的实例, 提出了设计的方法, 阐述了ARM汇编语言与C/C++语言相互结混合编程方式的实现。

参考文献

[1]翟乃强, 隋树林.汇编语言与C语言及Visual C++混合编程[J].青岛科技大学学报 (自然科学版) , 2003.

[2]王付山.汇编语言程序设计教学初探[J].福建电脑, 2005.

浅谈C语言的学习 篇8

1 C语言的背景及其优点

无论学习哪门语言首先应该了解一下所学语言的背景,也可以说它的发展史。C语言属于高级程序语言的一种,它的前身是“ALGOL 60”。其创始人是布朗?W?卡尼汉和丹尼斯·M·利奇。C语言问世时是带有很大的局限性,因为它只能用于UNIX系统上。然而随着科学技术的进步,计算机工业的发展,C语言逐渐脱离UNIX。1987年美国标准化协会制定了C语言的国际标准,简称“ANSI C”,从此以后它便成为一种广泛使用的程序语言。C语言的优点很多,主要的有如下四点:

(1)兼备高级语言与低级语言的优点,属于一种中间语言。

(2)它是一种结构化程序设计语言,非常适合结构化程序设计。

(3)有较丰富的数据类型、运算符以及函数供以选用。

(4)直接与内存打交道,使修改、编辑其他程序与文档变得轻松,简单。

2 两大语系两种不同的学习方法

高级程序语言分为两大语系。例如:C,C++(C语言的扩展),QBASIC,VB(BASIC的可视化),JAVAs cript,Js cript,VBs cript,JA-VA,ASP,FOXPRO,PERL等等。一路是以C为主的程序语言,例如:JAVAscript,JAVA等,这类语言在函数的调用,程序语句的书写,循环的控制都极为相似。另一路是以BASIC为首的程序语言,例如:FOXPRO,VBscript等,此类语言同样具有相似的函数调用,程序语句书写以及循环控制,但与C语系是不同的。因此若是您以前是从QBASIC起家的,那么在学习C语言前最好是先洗洗脑,千万不要把学习BASIC的方法以及思路用在C身上。

3 理解C语言的模块化程序结构

C语言的模块化程序结构用函数来实现,即将复杂的C程序分为若干模块,每个模块都编写成一个合法的C函数,然后用主函数调用函数及函数调用函数实现一大C程序:C程序=主函(main)+若干个函数。在程序设计中,常将一些常用的功能模块写成函数,也可以将大程序段分割成若干函数,前者目的在于减少重复编写程序段的工作量,后者目的在于缩短模块长度,以便程序阅读方便。一个源程序文件由一个或多个函数组成,它是一个编译单位,而一个C程序由一个或多个源程序文件组成。对较大的程序,往往分成多个文件,这样可以分别编写、分别编译,提高高调试效率,一个源程序文件可以被多个C程序公用。

4 重视C语言中的算法

写程序的最高境界就是掌握各种解决问题的手段和方法,写底层程序主要是掌握硬件的结构。要给一个芯片写驱动程序,无非就是掌握这块芯片的寄存器及其组合,然后读写值。这只是函数的简单应用。怎样才能精通程序设计呢?在程序设计中找到解决问题的最优方法和使用手段是考验一个程序员程序设计水平的重要标志。所以掌握好数据结构和一些常用算法,是精通C语言的必然之路。

5 具体学习C语言必须从以下四点入手

5.1 输入输出

C语言的输入输出是非常严格的,或许在其他程序语言中我们可以不关心这个问题,但在C语言中,我们必须要彻底了解它。由于篇幅有限,因此笔者不能详谈,有兴趣的朋友可以参考由著名程序语言教授谭浩强先生主编,由清华大学出版社出版的《C程序设计第二版》。不过这里笔者还是有几点要简单的谈一下。

5.1.1 二维浮点数数组的输入

二维浮点数数组的输入(即:通过键盘给二维浮点数数组赋值)在很多专业书中都没有详细讲过这个问题。在给二维浮点数数组赋值时一定要先声明一个变量,接着把数值赋予这个变量,最后把变量数值赋予二维浮点数数组赋值。

5.1.2 注意输出格式中“%”后的字符

C语言的输出说复杂不复杂,因为常用的都很简单。可说不复杂也未必,如果C语言要考得很难的话,根本不用考什么指针,只要专考输出格式,百分之九十九的学生都不会及格。可以看出C语言的输出格式之复杂程度。因此大家在学习它时千万要学会辨别输出格式中“%”后的字符,每个字符都有其意义,也都有其作用。

5.2 优先级

说到优先级,有很多人都不是很了解或说很模糊。什么叫优先级?比方说,我们在公交车终点站排座队时总会遇到70岁以上的老人不需要排队就能上车的情景,这就是优先级的涵义。C程序在运行时也象排队坐车一样,首先照顾那些优先级高的运算符,若是优先级相同,那么就像遇到两位(或两位以上)70岁以上的老人那样,让他们依次上车。但是C语言中的优先级的运算并不是千篇一律的。只能说是在大多数情况下,有些运算符的优先级有其自己的特点,因此这点大家要注意。例如条件表达式:条件?结果1:结果2,这种表达式很多人都知道,它的作用与IF ELSE条件判断语句很雷同,它运算时的优先级就不是按照C语言的规则来完成的。所以说对于优先级各位编程爱好者一定灵活掌握,不要死记硬背。

5.3 函数

虽说很多程序语言都有函数这一内容,但C语言的函数是最有魅力的。如果你能完全掌握C语言的函数,那么学习C++就不成问题了(C++是一门建立在C语言上,但又不同于C语言的高级程序语言,它增添了很多函数)。学习函数的方法是比较简单的,只有两个字“牢记”,即:牢记函数的功能,牢记函数的用途以及如何输入输出。有些朋友认为,程序语言中的函数没有多大用处。其实这并不正确,函数从本质上讲是一段通用程序,用它可以帮助我们节约很多编程的时间,一个聪明的编程者在编写程序前往往总是先找自己所编写的程序中有多少是可以用函数来代替的。比如要做一个比较字符串的实验,用C语言中的strcmp()函数只要一句话,而自己编写的话30句话都写不完,可想而知函数是多么实用呀!

5.4 指针

C语言中的指针是最有特色的,当然也是最难学的。指针说穿了,其实是变量的一种表现形式,只不过这种变量记载的不是数值而是地址。就象一个人可以用姓名来表示自己,也可以用身份证号码来表示自己一样。学习指针最好是先学些计算机硬件工作的原理,例如:直接寻址,间接寻址等,只有了解了这些内容以后,你再学指针就比较容易理会,毕竟C语言是一门介于机器语言与高级语言中间的语言,没有一些硬件工作知识是很难领悟它的真谛的。然而事事并非绝对,如果你没有这些知识也不要紧,只要清楚知道以下总结的二点再加上多练习便可:

(1)指针是地址变量。它的值有两种:其一是地址,其二是内容。不同的表达方式可以取不同的值,这有点像一个家庭地址在不同的场合标识的人物也不同。例如:父母亲在他们的单位所登记的家庭地址就代表他们自己,而你在学校中登记的同样的家庭地址就代表你自己。

(2)指针是可以运算的,它的运算法则与变量是一致的。

另外,在编写一个程序时,除非万不得已,一般不要使用指针变量。因为指针是比较复杂的,用不好就“当机”。但指针又是C语言的精华,可以使程序简洁、紧凑、高效,所以建议还是要深入的学习和掌握指针。

6 结束语

C语言功能非常强大、应用广泛,一旦掌握了,以后若是再自学其他语言就显得轻而易举了。其实C语言的学习并非是太难,只要你能理清思路,掌握它的精髓,那么自学C语言是一件非常容易且又其乐无穷的事。

参考文献

[1]谭浩强.C程序设计(第二版)[M].2版.北京摘要:清华大学出版社.1999.3.

[2]全国机等级考试二级教程C语言程序设计[M].北京摘要:高等教育出版杜.

C语言的指针学习法 篇9

关键词:指针,数组,地址,变量

凡是学习过c语言的都知道指针是c的灵魂, 它极大的丰富了c语言的功能。下面这部分内容就我个人认识做如下阐述。

1 指针的出现

我想有很多初学者学习到指针都感觉很难, 下面我就以自己的想法来解释下指针这个特殊的数据类型。基本类型变量大家可能并不难理解, 因为基本类型变量其内部存储了同类型的常量, 事实上指针也是变量, 不过呢, 这个变量和基本变量有点不一样, 不同点就在于基本类型变量内部存储了同类型的常量, 而指针变量内部存储的则是“同类型变量的首地址” (所指向的变量的地址) 。可以给你形象的来描述一下:

int a;/*声明一个基本整形的变量*/

此时, 编译器已经给a分配了连续4个字节的内存空间 (VC++6.0) , 结构如图1。

内存地址是线性编码的, 我们可以很容易的看出a的首地址就是他第一个单元的地址1001。

int*Pointer=&a;/*声明一个指向a的指针Pointer*/

编译器也同样给指针变量Pointer分配相应字节数的内存空间, 如图2

该内存空间里存放1001, 即为变量a的地址。

在程序设计中, 如果存在语句:a=10;直接给a赋值, 我们称为直接访问。

如果使用语句:*Pointer=10;即通过指针变量来赋值, 前面的*是间接运算符号, 意思是求Pointer内部存储地址所标识的内存单元, 也就是a。此时, 该赋值是通过间接访问来实现的。如图3

以上就是指针实现的基本解释, 很多优秀的程序员都说指针是C语言中的精华, 的确如此, 很多优秀的程序员写程序都非常依赖指针, 因为它很方便, 实际上指针所访问的对象是没有限制的, 他可以指向任何类型的变量, 前提是只要我们知道内存地址。因此指针也并不安全, 在开发网络程序的时候, 尽量要少使用指针。

2 指针在数组中的使用

简单的来解释下数组, 数组是“同类型变量的有限集合”。数组在内存中占用连续的内存单元 (地址连续) , 来存储数组中的每一个元素。数组是预先分配好指定长度的内存单元, 供数组元素使用。它并不支持动态内存分配。

下面以字符型数组为例来分析字符指针和一维数组之间的关系。数组名其实就是这一组内存单元的首单元, 它的地址就是整个数组的入口地址;数组名是一个指针, 它的基类型为基本数据类型, 不过在具体操作的时候不允许给数组名重新赋值, 可以把数组名理解为cons Pointer (指针常量) 。在程序中可以这样操作:

即同类型的指针, 完全可以胜任数组名的任务。一点问题没有而且可以运行的很好。当然, 我们可以进一步把代码这样来写:

改成

下面以基本整型二维数组为例来分析指针和二维数组之间的关系。二维数组名也代表二维数组的首地址, 也是一个指针常量, 只是其基类型为数组类型, 称为行指针 (a指向第0行, a+1指向第一行) 。可以把二维数组看做一个特殊的一维数组, 只是每个元素又是一个一维数组而已。在程序中可以这样操作:

/*把二维数组看做一维数组时, 每个数组元素a[0]、a[1]、a[2]又包含一个一维数组, 该一维数组又包含4个元素, 这样a[0]、a[1]、a[2]即成为相应的数组名, 也成为一个指针常量, 其基类型为基本整形;所以可以把a[0]、a[1]、a[2]分别赋给指针数组的相应元素*/

p=a;/*p为行指针, 其基类型为数组类型*/

3 总结

指针本身与指针所指向的变量不是一个单元, 一定要弄清楚指针变量的基类型。一个指针ptrold加上或减去一个整数n后, 结果是一个新的指针ptrnew, ptrnew的类型和ptrold的类型相同, ptrnew所指向的类型和ptrold所指向的类型也相同。

参考文献

[1]C语言初学者入门讲座:<http://www.gshu.cn/detail/0/337.htm>

C语言学习 篇10

C / C + + 语言程序设计是计算机专业本科生的专业基础课程,也是大学计算机基础系列课程的核心课程。通过本系列课程的学习,旨在使学生掌握程序编写的基本技能,培养程序设计思想和方法,养成良好的编程风格,编制高效可靠的程序,进而具备灵活利用所学知识解决实际问题的能力。长期以来,高校都在不断地探索和实践关于C /C + + 语言程序设计课程的教学模式。传统的教学一般着重概念和语法规则,缺少学习的实用性和针对性,实验环节与课堂教学环节存在不同程度的脱节。面对繁复枯燥的语法规则和程序结构,学生也很难的有学习兴趣。因此,根据质量工程的相关要求,研究教学改革方案,找出目前C /C + + 语言程序设计课程在教学中存在的主要问题,对现有的教学方法和教学手进行现代化研究与实践,具有十分重要的现实意义。

2 C / C + + 语言程序设计教学现状

本人承担了多年的C /C + + 语言程序设计课程的教学工作,在教学过程中发现了现有教学模式的一些问题和不足,分析总结如下:

( 1) 教学中过度依赖课本,“照本宣科”现象比较普遍。受中学教育模式影响,大多数教师授课时倾向于依赖选定的教材或很少几本参考教材授课,课程章节固化,凡事以课本为主,课本里有什么就讲什么,授课内容“流程化”“格式化”。这虽然有助于教学内容的结构和完整,学生也能直接沿用中学培养的习惯获得知识,但这只不过是“填鸭式”教学的延伸。长此以往,学生学习的主动性会逐渐丧失,对课本外的东西提不起兴趣,学习也只是为了应付考试。

( 2) 过于偏重理论教学,实践教学所占比重较小。在有限的课时教学中,传统方式仅能将基本的、重要的概念、语义语法规则教授给学生,而对其应用方式、更深层次的知识点、探索及发散性的科学问题涉及较少。课程考核也大多片面强调书面成绩,对于C /C + + 语言程序设计这种实践性要求强的课程,书面考试并不能反映出学生对课程的掌握水平,反而误导部分学生靠“死记硬背”课本上的知识点和程序片段蒙混过关。

( 3) 课程衔接性差,在知识体系中,没能很好形成与“计算机文化基础”和“计算机应用基础”的衔接,也没有把C /C + + 语言知识应用于专业问题的描述和求解中。在教学中,缺少利用所学知识解决实际问题的教学引导,书本与现实隔离,很难做到“学为所用”。

针对上述问题,结合本校实际,对C /C + + 语言课程教学的主要环节教学模式、上机实践、课程考核等方面进行了研究与探讨。

3 C / C + + 语言程序设计教学改革与探索

结合C /C + + 课程的特点,针对传统教学中存在的问题,积极进行课堂教学、实验教学和考核方式的改革,具体从以下几个方面进行了一定的探索实践:

3. 1课程教学改革

( 1) 打破课本章节格式,探索“实例驱动式”和“阶梯式”教学方法针对C /C + + 语言程序设计的课程特色,强调“从实践中来,到实践中去”的教学目标。在课程教学中,注意引入具体化的实例,使得概念的抽象都来源于实际的例子。比如在C语言教学中发现很多同学上课时能听懂,看别人的程序也能看懂,但自己动手去写时却无从下手。这种情况不是个例,而是学习语言类课程时大多数学生都存在的问题。在教学中可以结合生活中的实际例子来示范,为学生“看”和“写”搭建桥梁,培养程序设计思想。例如,从做面包到C语言程序编写的示例( 图1) 。在教学中,可以引导学生分步考虑面包的制作工序,操作中需要摒除详细的细节部分,抽象出核心的流程,然后一步一步代入C语言的程序设计结构和语法规则,使得学生对课程内容有熟悉度和参与感。

通过“做面包”的例子,可以结合变量在内存中存储的方式,帮助学生掌握“变量需要先声明才能使用”的知识点,同时也铺垫了函数的概念,还可以进一步扩展加入分支结构( 面包的不同口味) ,循环结构( 制作多份面包) 等。通过动员学生逐步思考,一起动手做面包,把计算思维与有趣的课堂互动结合起来,既提高了学生学习的兴趣,也“润物细无声”地把编程思维传达到学生心中。而后在学生通过课程学习已经建立了一定的编程思维的基础上,再结合我校的卓越工程师计划,把实际项目引入课堂中来,深入引导实例教学,实现学生从“课本学习”到“实践学习”的转变,通过项目实例让学生亲身感受所学知识的用处,也为毕业后的职业发展打下扎实的基础。

另一方面,知识的获取需要一个循序渐进的过程。这就需要教师在备课时,将同类型问题找出来,将这些问题按难易程度排队上课,先让学生解决最简单的问题,再依次提高问题的难度。通过教学,学生对于同类问题的解决就有了比较深刻的认识。比如上面提到的做面包的问题,同一个问题可以分级扩展,从做一份面包到做多份面包,从做单一口味的面包到做多种口味的面包,从做面包到卖面包,进而面包分类,存货处理等等。通过与实际例子结合循序引进,反复强调知识点加深学生印象,打牢基础。C语言的开设一般在大学一年级,此时着重培养学习兴趣和规范学生养成良好的编程习惯,而C + + 的学习着重于面向对象的程序设计,一般开设于大二下学期或者大三上学期,在C + + 面向对象语言学习时,结合类和对象的概念以及继承和派生等等知识点还可以对“做面包”的问题再做进一步扩展,从做面包到做馒头、饼干等各种糕点,鼓励学生动脑思考逐渐扩充功能,可以自己编程开发一个具备制作、买卖、分类、存货处理等功能的糕点铺。教学中可以根据需要切换各种生活场合,提高学生兴趣,强调编程思想源于生活,服务于生活,学生在日常中也会经常回想起课堂所学知识,达到事半功倍的效果。

( 2) 高效利用团队教学资源,体现从计算机基础课到专业课的顺利交接

C / C + + 语言程序设计立足于引导学生建立基本的“人机互动”概念,构建编程思维理念,掌握核心程序设计方法,开发“发现问题,凝练问题,求解问题”的能力。它是计算机基础课到计算机专业课程的重要衔接,所以在课程设计上,要逐步培养学生从“使用计算机工具→理解计算机工作原理→开发计算机软硬件”的计算思维。但这个目的只通过一门课程是无法完成的,在教学过程中,需要利用院系优秀的教学资源,组建教学团队,使得课程选取→大纲制定→计划培养→落实实施形成系统化,脉络化的结构,加强课程之间的衔接和交互,增加知识的穿插讲授,使得各个课程相辅相成,相得益彰,起到事半功倍的作用。通过对不同专业需求的分析,在制定教学大纲时注重课程的衔接,在实际操作中注重教学团队中任课教师课堂情况的反馈,通过强化教学内容和学生掌握情况双重渠道,避免课程之间的脱节。针对一门课程,学生学分的获取和讲授课时的结束并不意味着本门课程的结束,在形成系统化的课程设计后,可以有效利用每门课程的信息反馈,有的放矢的对学生薄弱环节进行再强化,达到扎实学习,学以致用,而不仅仅是应付考试的良性循环机制。

3. 2上机实践改革

计算机语言类课程的学习关键在于动手实践,但即使意识到了实践的重要性,学生真正动手也往往受限于上机时间和实践平台。在传统的实验教学模式下,大多此类课程都由实验指导书作为参照,学生按图索骥的输入一些代码,完成一些任务。评判的方式也大多依赖于教师,学生实践得不到实时反馈,积极性很难调动起来,课下也不会有意识去编程实践。通过借助我校搭建的ACM/ICPC竞赛平台,学生平时有充足的题目可供练习。而且所有题目规范,输入输出格式明确,评判也由机器在后台完成,正确与否也能实时反馈。通过多组数据的测试,竞赛平台能够发现并迫使学生修正编程中存在的容易人为忽略的问题。另外,ACM/ICPC竞赛平台还可以按照班级、年级、专业等排名,学生也能实时监控自己做题的数量、成功率等,并可以通过Q&A的环节进行错误求助并与其他同学沟通。学生的学习积极性一下子就提高了起来,出错的题目,知识点通过讨论也能自己在课余时间解决。

通过与ACM/ICPC竞赛平台的结合,主要实现了以下几个 方面的探索:

( 1) 激发学生学习兴趣,创造兴趣学习小组

在ACM/ICPC的平台的辅助下,激发学生分组分队共同解决问题,成立程序编写互帮互助小组,采用不定时竞赛的方式对各小组进行考评,并辅以奖励和惩罚措施,尽量让每个学生都不落队。通过组队,同学之间也有了更好的沟通渠道,每个人成为团队的一部分,培养了学生的分享和互帮互助的团队合作精神。

( 2) 鼓励学生涉猎算法等相关知识,培养计算思维

通过ACM选拔,ACM战队的主力队员会通过集训学习数论、计算几何、动态规划、二分图、博弈论、网络流等相关知识,但竞赛选拔的队员屈指可数。这些优秀的种子选手和现有的ACM培训资源却未被大多数学生利用。C /C + + 语言类教学和大多数计算机类课程所追求目标,其核心就是培养学生的计算思维。我们采用了通过优秀学生和ACM集训队带队教师的不定期讲座和培训,以及培训资料等资源的共享来吸引更多的学生参与到程序设计的队伍中来,并已经在学生中通过口耳相传起到了一定的影响力。在上机课中适度引入部分难度稍大的,需要借助其他相关知识才能解决的问题,鼓励学生查阅资料,自己动脑,动手去分析和解决问题,养成良好的主动学习习惯。

3. 3课程考核改革

课程设置的目的不是考试,而是解决问题,特别对于C /C + + 语言程序设计类需要实践的课程尤其是这样。传统的卷面考试不仅不能反映学生对课程的掌握程度,反而诱导出诸如靠划重点、死记硬背程序企图蒙混过关拿到学分的问题。所以在对C /C + + 语言程序设计考核时,我们采用了平时成绩和期末机考相结合的方式。考试成绩分三部分组成,上课活跃度占30% ,分组实践占30% ,期末机考占40% 。其中,上课活跃度包括出勤次数和上课提问情况; 分组实践指通过小组成员共同努力分析、解决实例的情况,一般在上机时根据进度提出难度稍高的问题,鼓励学生通过团队协作,查阅课外资料等来完成; 期末机考是从ACM平台上抽取一部分题目作为期末考核,考察学生实际编程能力。

4结束语

在研究与探索C /C + + 语言程序设计教学的过程中,通过“阶梯式”“实例驱动”的教学方法,辅以ACM竞赛平台,优化配置团队教学资源,追求实用性、系统化、高效率的课程实现,全面提升学生的对程序设计的实践应用能力。通过教学改革,提高学生在学习中发现问题、分析问题和解决问题的能力,培养学生编程思维,计算思维。实践表明,本文提出的教学改进方法可以激发学生学习的热情和兴趣,有效地提高了C /C + + 语言程序设计课程的教学效果。

摘要:针对C/C++语言程序设计课程语法规则繁琐、概念抽象等特点,结合我校实际情况,通过深入分析课堂教学、上机实验及课程考核等教学环节,从教学方法、实验设计及课程考核三方面提出改进方案,提出了以培养学生计算思维为最终目标,以提高学生学习兴趣,激发学生学习积极性为目的,以“阶梯式”“实例驱动”为教学方法,整合教学团队共同促进教学的模式。通过教学改革,激发了学生对C/C++语言程序设计的学习兴趣,培养了学生的动手实践能力,提高了教学质量水平。

C语言中指针链表的学习探讨 篇11

关键词:动态;链表

中图分类号:TP311.12

C语言中存储数据的结构用的最普遍的是数组,包括简单类型的数组,指针数据和结构体数组等,但是他们在实际应用中,会因为实现定义过大的数组容量而造成内存的浪费,或者因为保守的预测分配而满足不了实际使用的要求,这时就需要另一种方法来解决这个问题,这就是动态数据结构和动态分配内存技术。链表就是这样一种最简单的动态数据结构。那么如何让学生能够很好的学习并掌握它,本人就近几年的教学过程中经过探讨,采用了图示法进行教学,效果很好。

1 基本概念的理解

1.1 指针的理解

(1)指针与简单变量。通过图1所示理解指针与简单变量的关系,当把变量i的地址存入指针变量p1后,就可以说这个指针指向了该变量。

如需要指向下一个元素,只需要指针往后移动即可!

1.2 结构体的理解

(1)结构体类型。结构体类型是一种专门组织和处理复杂关系的数据结构,是一种自定义类型。同一个数据对象由于应用不同定义的类型也有所不同。比如处理学生的信息,可以有很多种方式:

结构体中的成员名可增,可减,形成新的结构体类型。

(2)结构体变量与数组。以上是结构体类型的定义,变量定义方式有三种,这里不一一举例,可根据自己的个人习惯选择不同的定义方式。比如上面两个类型,分别定义简单变量,可这样定义:

struct student s1,s2; struct stu s3,s4;

如定义数组,结合前面所学数组的知识,可这样定义:

struct student s[10]; struct stu s1[20];

2 指针链表

掌握了前面指针和结构体的知识内容,对于指针链表的理解和掌握是非常重要的。

2.1 指针链表的定义

这里主要讲解单项链表结点的结构体类型的定义,对于初学者掌握了这种定义,对于以后学习更为复杂的链表知识的理解是很有帮助的。

单项链表结点的定义:

struct node

{

int data; //存放的数据,可以定义其他更为复杂的数据结构

struct node *next; //指向struct node类型数据,用此建立链表

};

2.2 指针链表的建立

定义好类型后,再定义变量,并将链表建立起来,形成整数数据按升序建立的数据链,下面通过函数create来实现链表的建立。

struct node *create()

{

struct node *head,*p,*q,num;

head=NULL;

scanf("%d",&num;);

while(num!=0)

{

p=(struct node *)malloc(sizeof(struct node ));

if(p==NULL)

{

printf("Allocation failure\n");

exit(0);

}

p->data=num;

p->next=NULL;

if(head==NULL)

head=p;

else

q->next=p;

q=p;

}

return head;

}

2.3 指针链表的插入

链表建立完成后,最常用的操作之一就是对链表的数据进行增加,也就是插入操作,具体程序通过insert函数实现。

struct node*insert(struct node*head,sturct node*r,int*x)

{

struct nod *p,*q;

if(head==NULL)

{

head=r;

r->next=NULL;

}

else

{

p=head;

while(*x>p->data&&p-;>next!=NULL)

{

q=p;

p=p->next;

}

if(*xdata)

{

if(p==head)

head=r;

else

q->next=r;

p->next=p;

}

else

if(p==NULL)

{

p->next=r;

r->next=NULL;

}

return head;

}

2.4 指针链表的删除

对于链表中数据的删除也是链表数据中操作的重点,具体实现过程通过函数deletenode实现。

struct node*deletenode(struct node*head,int*x)

{

struct nod*p,*q;

if(head==NULL)

{

printf("This is a empty list.");

return head;

}

p=head;

while(*x!=p->data&&p-;>next!=NULL)

{

q=p;

p=p->next;

}

if(*x==p->data)

{

if(p==head)

head=p->next;

else

q->next=p->next;

free(p);

}

else

printf("NOT FOUND");

return head;

}

3总结

单向结点链表的主要操作就是建立,插入和删除数据,而且是链表当中最简单的一种形式,只有理解和掌握单向结点链表的基本操作,才有可能处理更为复杂的数据对象,在课堂上通过以上三个函数的编写与引导,学生对于链表有了初步的认识,并起到了良好的效果。

参考文献

[1]杜友福.C语言程序设计[M].北京:科学出版社,2012.

[2]龚民,朱秀兰.C语言程序设计教学探讨[J].电脑知识与技术,2009.

作者简介:刘山根(1976.8-),男,籍贯:河南新乡,职务:广东省华侨职业技术学校教务科副科长。

浅析C语言递归 篇12

所谓递归,简而言之就是在调用一个函数的过程中又直接或间接地调用该函数本身,以实现层次数据结构的查询和访问。在函数中直接调用函数本身,称为直接递归调用。在函数中调用其它函数,其它函数又调用原函数,这就构成了函数自身的间接调用,称为间接递归调用。

而采用递归方法来解决问题,必须符合以下三个条件:

1、可以把要解决的问题转化为一个新问题,而这个新的问题的解决方法仍与原来的解决方法相同,只是所处理的对象有规律地递增或递减。

说明:解决问题的方法相同,调用函数的参数每次不同(有规律的递增或递减),如果没有规律也就不能适用递归调用。

2、可以应用这个转化过程使问题得到解决。

说明:使用其他的办法比较麻烦或很难解决,而使用递归的方法可以很好地解决问题。

3、必定要有一个明确的结束递归的条件。

说明:一定要能够在适当的地方结束递归调用。不然可能导致系统崩溃。

好知道是这样以后;我们来写一个众多教材上的程序:使用递归的方法求n!。当n>1时,求n!的问题可以转化为n*(n-1)!的新问题。比如n=4:

第一部分:4*3*2*1n*(n-1)!

第二部分:3*2*1(n-1)(n-2)!

第三部分:2*1 (n-2)(n-3)!

第四部分:1(n-4)!4-4=0,得到值1,结束递归。我给的源程序如下:

可以看到,加上两条printf()和getchar()语句后,可以察看各级调用及其中间答案,很清楚的看到程序的执行过程。运行结果如图1所示,当主函数第一次调用fac0函数的时候,由于n=4不等于0和1,并不立即返回结果1,而是执行c=n*fac(n-1),用实参n-1 (值为3)调用fac()函数自己,即递归调用fac(3)。于是进入第二层调用fac(),这时也没有得出结果,继续用实参n-1(值为2)调用fac()函数自己。同样经过第三层调用后进入第四层调用,这时候n=1,算出1!=1,满足结束递归的条件,然后把得出的结果1返回给第三次调用的fac函数,得出2*1!=2,然后把结果2返回给第二次调用的fac函数,得出3*2!=6,最后第一次调用的fac函数根据第二次调用的返回值算出4!=4*3!=4*6=24,结束整个递归调用,得出最终结果并输出。

我们做事情,一般都是从头开始的,而递归却是从末尾开始的。比如上面的函数,当n>1的时候,就只能求助于n-1,而(n-1)1时,就求助于n-2,然后……直到(n-k)=1时,函数fac终于有了返回值1了,它再从头开始计算,然后一直算到n为止。所以说,递归简直就是一个数学模型,它的工作过程就是自己调用自己。以下是几点对递归的说明:

1、当函数自己调用自己时,系统将自动把函数中当前的变量和形参暂时保留起来,在新一轮的调用过程中,系统为新调用的函数所用到的变量和形参开辟另外的存储单元(内存空间)。每次调用函数所使用的变量在不同的内存空间。

2、递归调用的层次越多,同名变量的占用的存储单元也就越多。一定要记住,每次函数的调用,系统都会为该函数的变量开辟新的内存空间。

3、当本次调用的函数运行结束时,系统将释放本次调用时所占用的内存空间。程序的流程返回到上一层的调用点,同时取得当初进入该层时,函数中的变量和形参所占用的内存空间的数据。

4、在开发过程中使用printf()和getchar()可以看到执行过程,并且可以在发现错误后停止运行。

很多人说所有递归问题都可以用非递归的方法来解决,能不用递归就不用递归。但是对于一些比较复杂的递归问题用非递归的方法往往使程序变得十分复杂难以读懂,而函数的递归调用在解决这类问题时能使程序简洁明了有较好的可读性,因此很多问题用递归可很容易解决。同时由于递归调用过程中,系统要为每一层调用中的变量开辟内存空间、要记住每一层调用后的返回点、要增加许多额外的开销,因此函数的递归调用通常会降低程序的运行效率(在许多情况下,速度的差别不太明显)。

我曾经碰到过这样一个动物繁殖问题:若一头小母牛,从出生起第四个年头开始每年生一头母牛,按此规律,第n年时有多少头母牛?

如果不用递归函数来做,每当母牛到第4岁的时候才会生下一头小母牛,所以,每年增加的新的1岁小母牛都是上一年3岁的母牛加上4岁的母牛生下数量之和,分析过程如图2所示,给出程序如下:

程序虽然简短,但是可读性太差,不易理解。那么如果用递归函数求此问题呢?

我们先写出函数表达式:f(n)=f(n-1)+f(n-3)

为什么f(n)=f(n-1)+f(n-3)呢,请看:

f(n)-f(n-1)=f(n-3)

因为第n年要比n-1年多的牛,都是大于三岁的牛生的小牛,而f(n-3正是那些在n年大于三岁的牛,然后它们在第n年生下相同数量的小牛。源代码如下:

运行结果如图3所示:

可见,递归函数的主要优点是可以把算法写的比使用非递归函数时更清晰更简洁,而且某些问题,特别是与人工智能有关的问题,更适宜用递归方法。递归的另一个优点是,递归函数不会受到怀疑,较非递归函数而言,某些人更相信递归函数。编写递归函数时,必须在函数的某些地方使用if语句,强迫函数在未执行递归调用前返回。如果不这样做,在调用函数后,它永远不会返回,造成无穷递归。在递归函数中不使用if语句,是一个很常见的错误。此外,象汉诺塔问题就只能靠递归才能解决,但是现实中很多问题都是比较简单的,没有象汉诺塔那么复杂,我们只要会用递归编程来为我们解决一些问题就行了,所以就不必深究了。

参考文献

上一篇:国学的当代演绎下一篇:夏季汽车如何保养