c语言程序学习心得

2024-11-17

c语言程序学习心得(8篇)

1.c语言程序学习心得 篇一

这个我从eehome贴过来的。写的非常的好。我们用学单片机不要停在演示的基础上。只能让单片机完成局部事。这样我们永远不会走出流水灯地狱!!

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

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

一路学习过来的过程中,帮助最大之一无疑来自于网络了。很多时候,通过网络,我们都可以获取到所需要的学习资料。但是,随着我们学习的深入,我们会慢慢发现,网络提供的东西是有限度的,好像大部分的资料都差不多,或者说是适合大部分的初学者所需,而当我们想更进一步提高时,却发现能够获取到的资料越来越少,相信各位也会有同感,铺天盖地的单片机资料中大部分不是流水灯就是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);

if(KeyValue ==(KEY_VALUE_1 | KEY_DOWN))P0 = ~1;if(KeyValue ==(KEY_VALUE_1 | KEY_LONG))P0 = ~2;if(KeyValue ==(KEY_VALUE_1 | KEY_CONTINUE)){ P0 ^= 0xf0;} if(KeyValue ==(KEY_VALUE_1 | KEY_UP))P0 = 0xa5;} } 按住第一个键,可以清晰的看到P0口所接的LED的状态的变化。当按键按下时候,第一个LED灯亮,等待2 S后第二个LED亮,第一个熄灭,表示长按事件发生。再过500 ms 第5~8个LED闪烁,表示连按事件发生。当释放按键时候,P0口所接的LED的状态为: 灭亮灭亮亮灭亮灭,这也正是P0 = 0xa5这条语句的功能。

2.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.

3.浅谈C语言的学习技巧 篇三

关键词:C语言;C语言学习;C语言技巧

中图分类号:TP312文献标识码:A文章编号:1009-3044(2007)12-21713-03

Shallowly Discusses the C Language Learning Skills

CHEN Wu

(Electronic Information Institute of Yangtze University, Jingzhou 434023, China)

Abstract:C language as a broad international popular computer language, Over the years we have been like to learn but difficult to learn. This article is Based on a brief introduction to the C language, combined with years C language learning experience of the author, analysis of the skill of learning C language and participation computer rank test.

Key words:C language; C language studies; the C language skill; C program

1 学习C语言的意义

一提起C语言,大家就有说不出的忧与喜,为什么呢?很多人都认为它难学,但如果学进去了,就会觉得其乐无穷。

在学习C语言之前,我们先了解一下C语言的发展史。C语言是1972年由美国的Dennis Ritchie设计发明的,并首次在UNIX操作系统的DEC PDP-11计算机上使用。它由早期的编程语言BCPL(Basic Combind Programming Language)发展演变而来。在1970年,AT&T贝尔实验室的 Ken Thompson根据BCPL语言设计出较先进的并取名为B的语言,最后导致C语言的问世。随着微型计算机的日益普及,出现了许多C语言版本。由于没有统一的标准,使得这些C语言之间出现了一些不一致的地方。为了改变这种情况,美国国家标准研究所(ANSI)为C语言制定了一套ANSI标准,成为现行的C语言标准。

C语言是国际上广泛流行的计算机语言,既可以用来写系统软件,也可以用来写应用软件。许多著名的软件,如DBASEⅢ PLUS、DBASEⅣ 都是由C 语言编写的。学好C语言,可以为以后学习其他语言打下一个良好的基础,比如网络编程语言PHP的绝大部分内容与C语言相同,如果学过C语言,那么再学PHP就会非常轻松。

2 TC2.0的使用方法

TC2.0是TURBO C2.0的简称。Turbo C是美国Borland公司的产品,Borland公司是一家专门从事软件开发、研制的大公司。该公司在1987年首次推出Turbo C 1.0产品,1988年,Borland公司又推出Turbo C1.5版本,增加了图形库和文本窗口函数库等,而Turbo C 2.0则是该公司1989年出版的。TC2.0下载好后,要安装在C盘的根目录下,也就是地址C:TC下。安装好后,打开TC的应用程序,将出现以下界面:

其中顶上一行为Turbo C 2.0主菜单,中间窗口为编辑区,接下来是信息窗口,最底下一行为参考行。这四个窗口构成了Turbo C 2.0的主屏幕,以后的编程、编译、调试以及运行都将在这个主屏幕中进行。其实,上面这么多的菜单栏,只要掌握几个功能就行了。首先,按F10键,进入主菜单,用键盘上的“←”和“→”键可以选择菜单中所需要的菜单项,被选中的项以“反相”形式显示(如菜单中的各项原来是以白底黒字显示,选中后就会改为黒底白字显示)。此时按回车键或者“↓”键,就会出现一个下拉菜单。开始选择File的下拉菜单中的new选项,创建一个新的文件,当把源程序编写好后,按F9键,完成编译和连接,这时屏幕上会显示编译或连接时有无错误的信息框,按任意键,信息框消失,光标停留在出错之处。修改完毕后,再按F9,再次进行编译与连接,直到不再显示出错为止,再按Ctrl+F9组合键,系统就会执行程序,如果需要输入数据,则应从键盘输入数据,按回车键后,再按Alt+F5查看结果。最后选择File下拉菜单下的Save或者Save as保存,Save是保存在默认目录下,Save as是自己选择目录保存。最后按Alt+X关闭窗口。在使用TC时一定要习惯使用键盘,虽说有些版本的TC可以使用鼠标进行操作,但在计算机二级考试中的TC可不能用鼠标。再提一点,就是TC2.0与TC3.0的区别,最明显的是在TC2.0中有很多头文件不用加上去,但用TC3.0编写程序时,在每个程序的开头,都要加上#include(stdio.h),如果要进行算术运算,就要加上#include(math.h),而这些在TC2.0中都不需要加。

3 C语言内容概述

C语言的内容大致可以分为这么几块:(1)C语言的运算符与运算顺序;(2)C语言的三种基本程序结构,顺序,选择,循环;(3)数组;(4)函数;(5)指针;(6)结构体与共用体;(7)文件。本文针对这些内容,每一块简要介绍几句,也许对大家的C语言学习有些许帮助。

3.1 C语言的运算符与运算顺序

学好C语言的运算符和运算顺序,这是学好《C程序设计》的基础,C语言的运算非常灵活,功能十分丰富,运算种类远多于其它程序设计语言。当多种不同运算组成一个运算表达式,即一个运算式中出现多种运算符时,运算的优先顺序和结合规则显得十分重要。在学习中,只要对此合理进行分类,找出它们与我们在数学中所学到运算之间的不同点之后,记住这些運算也就不困难了,有些运算符在理解后就会牢记心中,将来用起来得心应手,而有些可暂时放弃不记,等用时再记不迟。

3.2 C语言的三种基本结构

C语言的三种基本结构是构成程序的最基本的框架,顺序结构的程序设计是最简单的,只要按照解决问题的顺序写出相应的语句就行,它的执行顺序是自上而下,依次执行。顺序结构可以独立使用构成一个简单的完整程序,常见的输入、计算,输出三步曲的程序就是顺序结构,例如计算圆的面积,其程序的语句顺序就是输入圆的半径r,计算s = 3.14159*r*r,输出圆的面积s。不过大多数情况下顺序结构都是作为程序的一部分,与其它结构一起构成一个复杂的程序,例如选择结构中的复合语句、循环结构中的循环体等。在学习选择结构时,要注意它的嵌套使用,也就是一个if语句中又有一个if语句,在if的判断语句中要注意一个问题,就是=与==的区别,比如if(b=1)与if(b==1),在执行完if(b=1)后,b的值为1,而执行完if(b==1)后,b的不变。在循环语句中,要注意break与continue的用法与区别,break语句是使程序从循环体中退出,从而结束循环;而continue语句是结束本次循环,进入下一个循环。

3.3 数组

数组就是一个同类元素的集合,也就是说,一个数组中的元素必须是同类型的,数组主要有一维数组与二维数组,而二维数组的本质就是一维数组,说通俗一点,二维数组就是把一维数组拆成了几行。要注意对二维数组的几种定义与初始化的方法:

(1)可以只对部分元素赋初值,未赋初值的元素自动取0值。

例如:int a[3][3]={{1},{2},{3}}; 是对每一行的第一列元素赋值,未赋值的元素取0值。赋值后各元素的值为:1 0 0 2 0 0 3 0 0。int a [3][3]={{0,1},{0,0,2},{3}}; 赋值后的元素值分别为 0 1 0 0 0 2 3 0 0。

(2)如对全部元素赋初值,则第一维的长度可以不给出。

例如:int a[3][3]={1,2,3,4,5,6,7,8,9}; 可以写为:int a[][3]={1,2,3,4,5,6,7,8,9}。

C语言中,只能单个引用数组元素,而不能一次引用整个数组元素,在对数组进行引用与赋值时,要学会使用循环语句。还有一点就是要注意把字符串赋给数组的几种方法。

3.4 函数

函数是C源程序的基本模块。从函数定义的角度看,函数可分为库函数和用户定义函数两种。C语言的函数兼有其它语言中的函数和过程两种功能,从这个角度看,又可把函数分为有返回值函数和无返回值函数两种。从主调函数和被调函数之间数据传送的角度看又可分为无参函数和有参函数两种。函数定义的一般格式:

[数据类型] 函数名([形式参数])

{类型说明;

语句;}

函数一般都有返回值,用return语句返回返回值。要学会函数的递归调用与嵌套调用,注意函数的值传递与地址传递。

3.5 指针

有人说,指针是C语言的灵魂,它C语言中广泛使用的一种数据类型,学习指针是学习C语言中最重要的一环,能否正确理解和使用指针是我们是否掌握C语言的一个标志。同时, 指针也是C语言中最为困难的一部分,在学习中除了要正确理解基本概念,还必须要多编程,上机调试。只要做到这些,指针也是不难掌握的。假定P是指针,那么P就是指针变量,它的内容是地址量,*P是指针的目标变量,它的内容是数据,&P是指针变量占用的存储区域地址。把整型变量a 的地址赋予p可以有以下两种方式:

(1)指针变量初始化的方法

int a;int *p=&a;

(2)赋值语句的方法

int a;int *p;p=&a;

不允许把一个数赋予指针变量,故下面的赋值是错误的:int *p;p=1000; 被赋值的指针变量前不能再加“*”说明符,如写为*p=&a 也是错误的。指针与数组的联系也是十分密切的。有如下例子:

int *p,s[10];

P=s;

可以用&s[i],s+i,p+i来表示s[i]的地址。

int *p,s[10][10];

p=s;

可以用&s[i][j]和p+i*10+j来表示s[i][j]的地址。

把字符串的首地址赋予指向字符类型的指针变量。例如:char *pc;pc="c language"; 或用初始化赋值的方法写为:char *pc="C Language"; 这里应说明的是并不是把整个字符串装入指针变量,而是把存放该字符串的字符数组的首地址装入指针变量,要特别注意指针作为函数参数时,可以在调用一个函数时得到多个由被调函数修改的值,还有返回指针的问题,许多库函数都是返回指针值,在调用时一般都要设立相应的指针来接受返回值。

3.6 结构体与共用体

结构和联合是两种构造类型数据,是用户定义新数据类型的重要手段。要学会结构体与共用体类型数据的定义方法和引用方法与用指针和结构体构成链表,链表的建立、输出,这些是以后编写一个大型程序必不可少的。

3.7 文件

C系统把文件当作一个“流”,按字节进行处理。C文件按编码方式分为二进制文件和ASCII文件。C语言中,用文件指针标识文件,当一个文件被 打开时,可取得该文件指针。文件在读写之前必须打开,读写结束必须关闭。文件可按只读、只写、读写、追加四种操作方式打开,同时还必须指定文件的类型是二进制文件还是文本文件。文件可按字节,字符串,数据块为单位读写,文件也可按指定的格式进行读写。文件内部的位置指针可指示当前的读写位置,移动该指针可以对文件实现随机读写。

4 C语言的学习技巧

有人说,C语言的学习就是一个体力活,也就是说,要勤上机。对此,笔者深有感触,可以说学习C语言的过程就是上机的过程。在学习C语言的第一个星期,笔者花了三个小时看书,十五个小时上机。那时,刚接触C语言,不知道从何下手,但笔者悟出了一个道理,就是学习任何一种语言,都要从软件方面着手。到网上查阅资料后,找到了TC2.0,下载安装好后,就照着书本操练起来。当时,笔者对C语言的语法一窍不通,就是照着书一个字母、一个字母的往上敲,边敲边看书上对这个程序的解释,敲完后,就运行程序,但界面上显示在程序中还有几处错误,又照着书一个一个字地对,发现不是这儿少了一个分号,就是那个单词打错了,改过来再次运行就成功了。有时,一个程序调试了半天就是找不到错误所在,就有点泄气了,但是,万事开头难,只要度过了这个难关就好了。就这样,不停的敲程序,不停的改,逐渐有了些感觉。学习就是要有兴趣,有兴趣了就可以学好任何东西。在对C语言地一些基本的语法有所了解后,就尝试着对书上的程序进行一些改动或者自己编写一些小程序,看看改了某个地方后程序会发生什么变化,如果发生了变化,就考虑为什么会发生这些变化,如何消除这些变化,就这样对这些程序进行剖析,对C语言有了更进一步的了解;有时,当脑海中有了某些想法时,就在计算机上进行模拟,当然这些想法有时会成功,有时会失败,每当成功了,就有一种成就感,失败了也不气馁,这样对C语言的兴趣逐渐浓厚起来了,有时一下课笔者就往机房跑,把自己又有的想法与疑惑到电脑上进行试验。学习C语言时,不要怕看长程序,自己多分析几个长的程序后,再去看那些短的程序时,一眼就可以看出程序的目的。

下面再来谈一谈计算机二级考试。全国计算机二级考试每年四月份与九月份各举行一次,由笔试与机试两部分组成,笔试120分钟,机试60分钟,笔试部分又有两部分组成,公共基础知识与C语言程序设计知识,只有选择题与填空题。机试只有三道题,填空,改错与程序设计。笔试只要把历年的考试题做完就可以了,而机试是从一百套题中抽出来的,那一百套题的題库是公开的,考试前把那一百套题做一遍就应该没问题了。在进行机试时,一定要注意这几个问题:做填空题时,一定要把空的下划线去掉,做填空与改错时不要增行删行,因为机试是电脑评卷,电脑在改这两题是是逐行逐行的扫描,一旦发现哪一行与标准答案有不相符的地方就会判为错误,哪怕写对了。做机试题目时,一定要有输出结果,不然就会没分,还有如果题目中这样说:请设计一个程序,将一个数的奇位输出,比如输入7654321,那么就要输出7531。当你把程序修改完或设计好后,一定要输入7654321,而不要输入其它数,电脑评卷时是要看你的结果的,如果结果不是7531,而是其它,可能会判你错。最后做完题一定要保存,最好选择save,而不选save as。

参考文献:

[1]谭浩强. C语言程序设计[M]. 第二版.北京:清华大学出版社,2005.

[2]全国计算机等级考试全真笔试+上机考题解答与训练[M]. 电子科技大学出版社,2005.

[3]全国计算机等级考试二级教程——C语言程序设计[M]. 高等教育出版社,2002.

[4]全国计算机等级考试考试大纲.2006.

4.C语言学习心得报告 篇四

看了一段时间的《C和指针》,这样看的效果不是很好,看着书本当时是懂了,有点恍然大悟的感觉,但是发现并不能真正的把理解的内容加入到自己程序当中,不能很好的去运用它,所以对于C语言我认为理论加实践,是学习C语言的最好方法,看书并不能完全理解书中的内容,也较容易遗忘,学习效率比较低,所以现在学习《数据结构》,在看书的同时,会在Microsoft Visual C++ 6.0软件上实践,严蔚敏写的《数据结构》这本书,比较经典但是这本书是伪算法,并不能直接在计算机上运行,对于初学者上机实践比较困难,另外说一下,我认为学习应该是在模仿中理解,在模仿中创新,所以我选择结合高一凡写的书《数据结构算法实现及解析》,这本书严蔚敏写的伪算法全部用程序实现了,给我上机实践提供了很大的帮助,貌似说到数据结构了,呵呵,回到正题,下面继续说说C语言。

指针是C语言的精华,也是C语言的难点,它就像一把双刃剑,锋利无比但运用的不好也会给自己带来危害,后果比较严重,所以重点来说说指针。

很多初学者弄不清指针和数组到底有什么样的关系,为避免混淆不清,下面总结一下指针和数组的特性。指针是保存数据的地址,任何存入指针变量的数据都会被当作地址来处理,指针变量本身的地址由编译器另外存储,存储在哪里,我们并不知道,间接访问数据,首先取得指针变量的内容,把它作为地址,然后从这个地址读或写入数据。指针可以用间接访问操作符(*)访问,也可以用以下标的形式访问,指针一般用于动态数据结构。数组是用来保存数据的,数组名代表的是数组首元素的地址而不是数组的首地址,所以数组p与&p是有区别的,虽然内容相同,但意义却不同,&p才是整个数组的首地址,数组名是整个数组的名字,数组内每个元素并没有名字,不能把数组当一个整体来进行读写操作。当然数组在初始化时也有例外,如int p[]=“12345”是合法的。数组可以以指针的形式访问如*(p+i);也可以以下标的形式访问p[i],但其本质都是p所代表的数组首元素的地址加上i*sizeof(类型)个字节作为数据的真正地址来进行访问的。数组通常用于存储固定数目且数据类型相同的元素。刚开始的时候我有点分不清指针数组和数组指针,现在也总结一下,指针数组,首先它是一个数组,数组的每个元素都是指针,可以理解为“存储指针的数组”的简称,数组指针,首先它是一个指针,它指向一个数组,在32位系统下永远只占4个字节,至于它指向的数组有多少个字节,并不知道,可以理解为“指向数组的指针”。举个例子,int *p1[10]与int(*p2)[10], 要理解这个问题,首先要清楚操作符的优先级,“[]” 的优先级比“*” 的优先级高,所以首先p1与“[10]”结合构成一个数组p1[10],int *为修饰数组的内容,所以数组元素是指向int类型的指针,所以这个是指针数组,“()” 的优先级比“[]” 的优先级高,“*”与p2结合构成一个指针变量,int修饰数组的内容,即数组的每个元素,数组这里并没有名字,是个匿名数组,现在清楚了p2是一个指针,它指向一个包含10个int类型数据的数组,即为数组指针。下面再说说内存管理,内存分为三个部分:静态区、堆、栈。其实堆栈就是栈,而不是堆。静态区是保存自动全局变量和static变量。静态区的内容在整个程序的生命周期内都存在,由编译器在编译的时候分配。堆是由malloc系统函数分配内存的。其生命周期由free和delete决定。栈保存局部变量。栈上的内容只在函数范围内存在,当函数运行结束,这些内容也会自动被销毁。再说说有关内存需要注意的地方,定义了指针变量,在使用之前一定要初始化使它指向一块合法的内存,不管什么时候,我们在使用指针之前一定要确保指针是有效的。使用malloc系列函数分配内存,使用完之后应及时释放内存,以防止内存泄露。

5.学习C语言的心得体会 篇五

在这次实训中,我对C语言有了一个更深的认识了解,也对这学期的知识得到巩固,还尝试了运行编程,每次运行程序成功,让我对下面的项目充满了信心通过自己的努力最终把最初的理论知识转化成基本技能,这次的实训,是我对C语言的学习产生了农活的兴趣。

还是这次实训,最令人激动的就是同学遇到问题互相帮助虽然只是一个很小得实训,但同学们的满腔人情却是值得骄傲的,我们在实训中取长补短在实训中长知识,提高了我们学习,虽然对有些时候老师没给我们指出解决问题的方法有一些小抱怨,但到了结束是才知道,这种教学让我们自己学会了自学,学会了去看懂别人的代码。更多的是老师的感动,每天在我们来之前家到了机房,给我们我分享他学C语言的感受,还提醒我们注意在编程时与遇到的事项,这些是我们平时上课无发了解到的知识是我们更深层次的极大收获。

通过实训我们也发现了许多问题,在试训中我认识到自己还有很多知识没学好,最基本的知识点没有理清楚,而且许多东西要经过翻书,上网查阅,搜索,遇到一小点错误运行不出来,就会烦躁不安,觉得有些自暴自弃或这抱怨题目有些变态,以后要克服,尽量保持一颗蓝好的心态,学好C语言,用C 语言来编写一个按要求的系统。

对于未来,近程下个学期,我觉得我还有许多方面需要提高。

首先我要继续学习好C语言的基础知识,然后能在电脑上熟练的运用。能写出一些程序,上网的时候多看一些优秀的教程和优秀的代码,遇到问题是多和同学讨论,并且多弄出几套方案,多练习自己结局问题的能力和与同学合作的能力。

6.C语言程序设计心得体 篇六

自从记事以来就听过了电脑这个高科技,现在终于实现了自己的一个小小愿望——成为了计算机系的一名学生,这学期我们学的C语言,以前就听过这个名字,打开书一看简直一窍不通,学好这门科简直就是一个挑战,课前课后都要把书啃一遍,要不然上课就跟不上,终于学期快结束了,我们有一个课程设计,在两个星期内,完成一个复杂的程序,我们的题目是做一个计算器,计算器从小就见过但让我自己设计一个出来就有点困难了。

第一天,我们都按照老师教的画图函数画计算器的外貌,但发现一个一个画实在是太烦琐,后来在老师的点拨下,我们采取了for语句,果然很快地画出来了,接下来的事就是计算了,用什么函数?怎么用?那个上午就坐那发呆,没有什么新突破,自性心严重受到打击,下午立马找了份材料到图书馆去研究,还专门找了位高手教我,研究了一个下午终于有眉目了,第三天我们成功地把计算功能搞定了,但还有闪烁的功能没有搞,这确实是一个问题,这时团结的力量得到了充分的体现,大家齐心协力,有的找资料,有的帮忙改错,有的……俗话说:“三个臭皮匠顶个诸葛亮。”在大家的共同努力下,我们胜利了。

感谢刘金平老师的淳淳教诲,感谢同学的积极帮忙,我的程序终于顺利完成了,接下来的就是写报告收尾了,看着自己的劳动成果写起报告来特有劲,特有灵感。

很感谢这次的课程设计,它使我更加深刻地体会到多看专业书的重要性,只有掌握了一定量的专业知识才能得心应手地解决诸多问题;另外,做任何事都要有耐心,不要一遇到困难就退缩;在学习和工作中要时刻谨记“团结”二字,它好比通向成功的铺路石,不可或缺。

7.c语言程序学习心得 篇七

一、明确学习目的, 激发学生学习兴趣

学习C语言的第一节课就要强调其重要性及学习的必要性, 学习C语言的方法及教材内容框架。以解决简单的实例入手, 编写小的程序, 调试得到所需要的结果, 引起学生学习的好奇心, 并且告诉学生C语言与其他课程的联系, 如数据结构、操作系统、面向对象的高级语言设计等课程, 达到学生有学习C语言的渴望要求。 (

二、丰富教学形式

采用案例分析<?>?任务驱动等教学手段, 培养学生学习兴趣。在教与学的过程中, 教师和学生都是不可缺少的, 离开老师的学和离开学生的教都是不行的, 二者必须统一起来, 才能培养学生学习C语言的兴趣。首先要让学生知道编写程序的一般步骤:建立数据结构;输入 (初始化) ;计算或数据模型的表达;输出。在每一单元的课堂教学前要精心设计好案例, 通过案例可以揭示所学内容, 用多媒体教学、启发式教学、互动式教学等方法使知识有连贯性, 让学生的思维有过度性, 形成学生的知识框架到思维框架, 如指针的难点思维逻辑结构:指针数组、数组指针、指针函数、函数指针;还有指针与一维、二维之间的关系等。学生在课堂教学上集中于一个教学模式的时间不会太长, 所以在课堂上还应该换一个模式——任务驱动式教学, 课前设计好任务, 引导学生去思考问题、发现问题, 通过师生相互之间提问互动、小组讨论等形式解决问题。解决问题之后需要评价, 可以是学生互评, 或是老师评价, 最终要加以鼓励使学生获得信心, 有成就感, 以培养其学习兴趣。

三、因材施教

教学环境离不开老师和学生, 学生接受知识的能力, 建立数学模型的能力, 思维能力是有差异的, 必须了解自己的学生, 才能达到教学目标, 教师必须关爱每一个学生, 在课上以班级为单位, 要想辅导和答疑照顾到每个学生很困难, 为更有效地、全面地提高学生的学习效率, 在上C语言课之前就要将学生进行分组, 组员的层次可以是不同的, 学习时可以相互讨论, 互相促进, 取长补短。在项目设计中可以锻炼学生的合作精神, 每个组选出一个组长, 定期汇报每个学生的学习情况, 另外, 教师无论在多媒体教室、实验室, 还是课外, 都要时刻关注学生的动态, 了解学生的层次, 不管是课上、课下或网上, 要积极与每一位学生交流, 在此基础上, 设计不同的任务给不同层次的学生, 设计的任务相对于每一个层次, 难度要适中, 使学生能够在能力范围内完成, 让学生体验学习的乐趣, 提高学生发现问题、解决问题的能力, 促使每个层次的学生学有所获, 提高学习兴趣。

四、项目综合, 课程应用

在学习课程的后期阶段, 需要进行综合知识的沉淀和总结, 在过程性评价时要注意综合项目的积累, 每一段小的项目是最后综合项目的分支, 这样项目的难度就会降低并具有可完成性, 使学生愿意和主动学习;作为教师可以将C语言的应用前沿介绍给学生, 也可以将学生竞赛应用C语言的实际案例给学生做, 让学生的思维上升一个空间, 保持学习兴趣的可持续性。

五、结语

学生学习C语言程序设计, 离不开教师的引导和帮助, 通过开始、过程中和结束三阶段的案例分析, 任务驱动, 因材施教, 综合应用等教学模式培养和保持学生的兴趣, 在教学方式上, 运用多媒体模式和网络平台, 课内、课外互动和交流, 在教学元素的相互碰撞中让学生学会交流, 学会合作, 学会思考问题, 在愉悦的环境中完成知识的建构, 养成良好的学习习惯, 为后续课程打下良好基础。经过课题组成员的实践, 这种阶段性保持学生的学习兴趣是行之有效的, 并具有一定的可移植性。

摘要:就如何保持和提高学生学习C语言的兴趣提出了教师在教学活动的开始、过程中和结束三个阶段的教学方法和模式, 经过教学实践, 达到了良好的教学效果。

关键词:C语言,程序设计,兴趣,任务驱动,因材施教

参考文献

[1]张桃红, 彭珍, 杨炳儒, 等.C程序设计”课程的KM教学法研究[J].计算机教育, 2010 (2) .

[2]何钦铭, 颜晖.C语言程序设计[M].北京:高等教育出版社, 2008.

8.C语言中指针链表的学习探讨 篇八

关键词:动态;链表

中图分类号: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-),男,籍贯:河南新乡,职务:广东省华侨职业技术学校教务科副科长。

上一篇:在技校如何开展主题班会下一篇:我相信我自己范文