第一篇:《C专家编程》总结
《C专家编程》总结
开始读《C专家编程》之前,有一个很担心的问题:94年出的讲语言的书,在现在(2012)还有多少是适用的。因此,一边读,一边用VS2010做实验。最后发现大部分内容都还在用。读完后,觉得最精彩的部分有二:一是讲解如何理解声明,二是深入地讲解数组名与指针。下文是将看书过程中所做的笔记进行的整理。
p.s: 以下代码均在VS2010测试过
1.使用无符号数时要特别注意(不推荐使用无符号数)当无符号数与有符号数在同一条表达式中出现时,有符号数会被转换为无符号数。e.g:
int feng =-1;unsigned int yan = 5;bool result =(feng < yan)? true : false;//最后的结果会是false 原因是C语言在计算含有不同类型的表达式时,会将类型向上提升。在本例中,int被提升了unsigned int,从而使-1的补码被解析为很大的整数 2.相邻的字符串常量会被自动合并成一个字符串 e.g:
char *str[] = {“feng” “yan”, “zero”};//“feng”和“yan”被合并成一个了:“fengyan” 3.易出错的优先级
.高于* e.g: *p.f 正确的理解:*(p.f)
[]高于* e.g: int *ap[] 正确的理解:int *(ap[])ap是个数组,其元素为int* 函数()高于* e.g: int *fp()正确的理解:int* fp()fp是返回int*的函数
==和!=高于位操作符 e.g: val & mask!= 0 正确的理解:val &(mask!= 0)==和!=高于赋值符 e.g: c = getchar()!= EOF 正确的理解:c =(getchar()!= EOF)
算术运算高于移位运算 e.g: msb<<4 + lsb 正确的理解:msb <<(4 + lsb)逗号运算符优先级最低 e.g: i = 1, 2 正确的理解:(i = 1), 2 4.理解声明,定义,typedef语句的步骤
a.找标识符
b.找被括号括起来的部分
c.找后缀操作符,如果是(),则表示是函数;如果是[],则表示是数组 d.找前缀操作符,如果是*,则表示“指向XX的指针”
e.找const和volatile,如果const,volatile后面紧跟类型(如int,long),那么它作用于类型,其它情况下,作用于它左边紧邻的项 e.g:
int const * zero;//zero是一个指针,指向一个常量整形
char(*zero)[20];//zero是一个指针,指向一个有20个char元素的数组
typedef void(*ptr_to_func)(int);//ptr_to_func是新类型名,这种类型是一个函数指针,指向接收一个int参数,并返回void的函数
char* const *(*zero)();//zero是一个函数指针,该函数无参数,并返回一个指针,返回的指针指向一个常量指针
char*(*zero[10])(int **p);//zero是一个数组,元素是函数指针,其指向的函数授受一个二维指针,并返回一个指向char的指针 void(*signal(int sig, void(*func)(int)))(int);//signal是一个函数,该函数接收一个int,一个函数指针,并返回一个函数指针
5.左值与右值
左值通常表示存储结果的地方(地址),其值在编译时可知 右值通常表示地址的内容,其值通常要到运行时才知道
6.指针与数组名不等同的情况(定义为数组,却声明为指针,或者反过来)前提知识(假设有定义:int array[10], *ptr;):
a.使用数组名下标访问(如:array[1]),会直接将数组名的地址加上偏移值作为变量的地址(即array[1]的地址)
b.使用指针下标访问(如:ptr[1]),会先取指针指向的内容,然后将这个内容加上偏移值作为变量的地址(即ptr[1]的地址)
不等同的原因:
当定义为数组,却声明为指针时,相当于用指针下标访问的方法来解析一个数组名下标,即先取数组第0号元素的内容,然后将这个内容加上偏移值作为变量的地址,从而访问了不该访问的东西。反之亦然。7.指针与数组等同的情况
a.编译器将表达式中的数组名当作指向该数组第0号元素的指针,下标当作指针的偏移量,即array[i]会被当作*(array + i)
b.编译器将函数参数声明中的数组名当作指向该数组第0号元素的指针,即在函数内部得到的是指针,而不是数组名
基于a情况,可知这条谣言是假的(至少在一维数组中一定不成立): 用指针迭代数组比用下标迭代数组更快
基于b情况,可解释为什么在传递数组后,不能用以下方法计算数组长度
int ArrayLength(int arr[]){ return sizeof(arr)/ sizeof(arr[0]);//返回值必定是1,因为此时的arr是一个指针,而不是数组名 } 注意b情况的将数组改写为指针并不是递归定义的,e.g:
实参 char zero[10][10] 被改写为 char(*zero)[10],这里将数组的数组改写为数组的指针
实参 char *zero[10] 被改写为 char **zero,这里将指针数组改写为指针的指针
实参 cahr(*zero)[10] 不改变,因为此时的zero是指针,而不是数组 8.interposition interposition指用户定义的函数取代库中声明完全相同的函数,注意这不是指重载,而是指下面这种:
void zero();//user defined function void zero();//library function 出现interposition时,要特别注意以下情况:
void zero();//user defined function int main(){ zero();//调用用户定义的函数zero,而不是库函数zero
FengYan();//假设这是另一个库函数,并且函数内调用库函数zero,此时由于interposition,变成调用用户定义的zero return 0;} 备注:
出现interposition时,在VS2010会出现warning: inconsistent dll linkage 9.堆栈段作用
a.存储局部变量
b.函数调用时,存储有关的维护信息
c.用作暂时存储区。e.g: 计算一个很长的表达式时,会把部分结果先压到堆栈中
第二篇:编程知识总结C难点总结
《编程知识总结》—C#难点总结
1.编写菜单中对应子菜单项的消息响应函数,考虑的方面很多,例 如,当前打开一个文件,此文件已经被修改,如果用户需要新建 或者打开另外一个文件,程序要询问用户是否保存当前的文件。
问题的解决:先用纸张写一份详细的业务流程图,在之后的编写 过程中,按照业务流程的规定进行功能的逐步实现。
2.编写判断是否要提醒用户保存的程序,判断是否提醒保存的根据 是什么。我的判断根据验证文件内容是否被修改,如果被修改,提醒用户保存。如果被保存的文件没有文件路径,调用另存为的 响应函数进行执行。
3.无法关闭窗体,当我响应文件菜单下退出菜单项的时候,如果使 用 Close()函数的话,可以就直接退出,但是当我需要以同样的 方式响应右上角关闭按钮的话,事件信息就会进入死循环,而且 永远不会结束。所以,在关闭窗体的时候,目前我用的就是 Application.exit();这个函数,强制性终结这个窗口
4.保存文件,我们打开文件和直接输入信息的文件的情况,对于保 存来说是不一样的,因为打开的时候,应该是直接保存到指定的 文件中,而直接输入的应该提示保存,通过另存为的方式保存。5.文件的删除。C#的 textBox 没有直接为我们提供文本删除的函数,我弄了许久,后来,无意间突然发现通过已经选择的字符串的下 标来操作,就可以删除文件了,具体操作就是,先获取被选择文 件的前半部分,在获取后半部分,而略过中间被选中的部分即可。6.开始的时候没有使用 Using System.IO,系统报错;
7.由于在 MessageBox.Show 后没有写 return 导致错误;
8.初步创建简易记事本后点击打开文件选项并不显示文件的内容,且编译器报错了,是由于没有判断文件名为空,且文件的完整路 径名获取的不准确,将文件名为空的情况及其文件的完整路径名 获取之后,读取文件正确;
9.首先,我在添加单击“新建”响应的代码时,单击进去,结果代 码全部不能运行,必须是双击。
10.获取文件名时,文件名不需要特别处理,程序内部直接处理好的,我在这里画蛇添足了。
11.操作对象必须指代明确,不如要出错。
12.在用 WinForm 做文本文档的时候,在“新建文本文档”项的代码 中,需要在项目中新建一个 new 窗体作为“新建文本文档”,在 form1 的代码中写如下代码: Form frm = new NewForm();frm.Show();this.Hide();然后在 Form2 中下如同样的代码即可。
13.还发现“保存”与“另存为”项的代码编写没有任何差异,考虑 并修改程序无果。代码如下: private void 保存SToolStripMenuItem_Click(object sender, EventArgs e)// { DialogResult save = saveFileD
ialog1.ShowDialog();
if
(save
== DialogResult.OK){ string filepath = saveFileDialog1.FileName;//FileInfo fi = new FileInfo(filepath);string content = richTextBox1.Text;File.WriteAllText(filepath, content, Encoding.Default);} } 14.在保存的时候必须输入保存路径才能正常执行,没有实现文本文 档的基本保存功能。并且在打开一个原本有内容的文本文档之后,加以修改再保存就不能保存新的内容。
15.获取 TextBox 中获取一行的值,使用 TextBox 的 Lines 属性来获 取
16.做删除操作时,由 MessageBoxv.show 来弹出一个确认窗体,把返 回的值赋给一个 DialogResult 类型的变量
17.在复制文件时,CopyTo 函数将第二个参数置为 TRUE 则在复制时,如果文件存在会被覆盖掉。
18.在向文件写入数据时,使用 Encoding 的 Default 的属性可避免输 入中文乱码问题。19.关于上午的文件编程,刚开始对程序要执行的具体操作不够明确,在编程时,功能实现的矛盾性问题很多,其问题在于编程前分析 不够到位;
20.文件编程中起先将打开文件定位为文件夹,导致弹出对话框显示 “不存在此文件”; 21.File.WriteAllLines(),无法将临时修改后的文件信息进行正 确保存,文本没有实现正确的回车换行;File.WriteAllText()能实现;
22.this.Close()无法实现程序的结束 Close()方法是关闭 Form 23.当创建了控件的响应事件后,修改控件的 name 属性,响应事件 名为改变,但并不影响进程的进行,要修改方法名应删除该响应 方法,双击控件,重新获得响应事件。
24.代码经常会出现命名不规范的情况,同时没有写注释的习惯
25.经常会丢掉方法后的括号,如 savedialog1.showdialog()26.一 些 方 法 中 的 细 微 差 别 如 File.Write.AllLine 27.对于异常的处理经常忽略,导致程序的容错处理比较差。
28.当编辑框改变时有两种情况,一种是打开,一种是编辑,当打开 时不应该记录改变标志。解决的办法:做一个打开标志,当打开操作时,赋值为 true,在 文本框的改变函数中判断,如果是打开操作,将打开操作之位 false,同时文本改变标志值为 false
29.此处如何把长整形变量赋值给一个 string 类型? 使用 ToString()30.如何在文本框上添加滚动条。设置 TextBox.ScrollBars
31.textbox 控件大小跟窗体一起变化:刚开始想通过相应窗体大小 改变事件的办法解决,后来通过其他同学得知可以通过更改 textbox 的 Dock 属性(改为 Fill)来解决。
32.当 textbox 空间的文本改变时,进行新建、打开、退出等操作时 应提示用户是否保存更改,通过设置文件更改标志(flag)并相 File.WriteAllText 和 响 textbox 的 textchanged 事件来解决。
33.由于需要提示用户保存更改,刚
开始时代码控制结构写的很复杂,连自己也有点难理解。后来用调用“保存”事件的响应函数(刚 开始一位不能这样)后才解决这一问题。
34.由于刚开始做,命名方面不是很规范,导致了变量、方法和类的 名字不易区分,不能一目了然,间接增加了编写程序所需的时间。解决方法是在编写程序中时刻遵守命名规范。
35.在对文件操作时,文件的路径经常出错,原因是少了转换为绝对 路径的@。
36.对文件注释太少,经常导致前面编过的代码过一段时间就完全看 不懂了,所以,在编程中也要养成随时加注释的习惯,其实这样 做表面上是增加了时间,统筹来看,这样做既方便了自己日后修 改程序,又为阅读我们程序的人提供了便利。
37.文件操作经常出错,程序无法运行,要因此要异常处理,比如说 用 try,catch 对异常处理。
38.在昨天上午的文件操作中,第一个问题就是路径的合法性检测。由于不知道 c#是否存在像 c++ access()一样的方法。最后使用 OpenFileDialog 来限定用户输入。可以使用正则表达式验证 39.再有就是文件操作异常处理。C#的异常处理还是很方便的,昨天 的经验是只要使用 try catch 语句,在 catch 中不作任何处理程 序也不会当掉。
40.MessageBox.Show(“你还没有保存上次的修改,是否保存修改?”, “保存”, MessageBoxButtons.YesNoCancel);选项的判断 问 同 学 得 : 返 回 值 为 DialogResult.No DialogResult.Yes DialogResult.Cancel 41.2 . private
void
Form1_FormClosing(object
sender, FormClosingEventArgs e)中如何取消关闭 问同学得:e.Cancel = true;42.如何实现修改后 “新建”“打开”等提示保存 点打开保存后记 录存储路径
43.新建 2 个成员记录:private string Pathname;private bool savetxt=true;
44.如何在两个窗体间传递值。通过向同学和老师询问,知道传值有 很多种方法,我用的就是调用构造函数,可是老师也说了如果值 比较多的时候,这种方法还是有缺陷的。使用构造函数可以,只是也用对象会好一些。
45.当汉字改成英文时,函数中仍然是汉字,如果直接在 program.cs 中改容易出问题,怎么改? 利用 VS 的代码重构
46.如何改窗体的默认主题样式 可以使用第三方控件完成
47.TextBox 中文本值改变时的响应事件(可以添加事件响应函数)。
48.文 件 打 开 中 将 文 件 写 到 textbox 中 : 定 义 string[] 调 用 File.ReadAllLines 函数将文件内容逐行读入到字符串数组中,然后利用 for 循环将文件内容逐行写出到控件中。
49.填写邮箱和文件路径的时候需要有正确的规格,因为之前接触过 c#不知道该如何完成,后来在经过询问他人和网上资料查询得知 用正则表达式来规范正确
的格式,便避免了这方面因格式而导致 的问题。
50.当输入多行字符串,保存过后进行查看时,当有换行就会出现黑 乎乎的正方形。换行:rn
51.假设某个事件已经做好,可以作为一个模块使用,那么如何在其 他代码中调用此模块
52.记事本的查找、替换模块不知道如何实现 需要使用字符串函数
53.打开 txt 文件的时候中文没有乱码但是保持了之后在打开就有了 乱码 实现:原先是因为保持的时候没有使用 encoding.default 54.保存时文件不能自动保存文件 ?自动保存文件
55.在做简易记事本的途中,创建快捷键的时候遇到一点问题,当在 杂项的 Shortcutkeys 中选择 shift 的时候,它会报属性值无效的 错误,后来无奈之下选择了 Ctrl,居然就正确了
56.还有在 TextBox 的属性 Dock 选择了 fill 的时候,依然没有变成 全部填充,真是百思不得其解!需要将 TextBox 设置为多行文本
57.发现了一种比较简便的方法: 已经做完的某个控件出发的事件,可以作为一个法方来使用。例: private void SaveMenu_Click(object sender, EventArgs e){ //判断文件是否已有路径,若无,选定路径,若有,保存在最新操作的文件路径下 if(path == string.Empty){ if(SolgSave.ShowDialog()== DialogResult.OK){ path = SolgSave.FileName;File.AppendAllText(path, txtEdit.Text, Encoding.Default);} } else { File.AppendAllText(path, txtEdit.Text, Encoding.Default);} } private void ExitMenu_Click(object sender, EventArgs e){ if(txtEdit.Text!= string.Empty){ DialogResult dr = MessageBox.Show(“是否保存当前文本至
”+path,“
记
事
本
”, MessageBoxButtons.OKCancel,MessageBoxIcon.Warning);if(dr == DialogResult.OK){ //使用保存文本方法 this.SaveMenu_Click(sender,e);//SaveMenu 的单击事件 } } //退出程序 Application.Exit();}
58.RichTextBox 与 TextBox 的换行区别问题。在 TextBox 中是以rn 为换行标志,而在 RichTextBox 中是以n 为换行标志。我在 C#程序中使用 RichTextBox 将其文本内容保存为文本文件,即(*.txt)类型的文件时,文本不能正常的换行,且出现一些乱码。这是由于文本文件是以rn 作为换行符,当我改用 TextBox 时,就 没有这样的问题了。
59.文本的选中问题。SelectedText 这一属性可以指向当前控件中选中的文本,60.通常我们会发现,保存操作在文本没有保存的情况下时才会出现 保存文件对话框,当文本已经保存过时,新增的文本会保存到原 文件中,这两个操作需要对文本是否保存过进行判断。我采用了 判断路径的方法对我文本是否保存过进行了判断,当文本路径存 在时,表明文本已经保存过,反之,则没有保存过。
第三篇:C语言编程自我总结
1.编译器选择8级优化时,可能会出现错误。刚写好的程序,建议先用0级优化看能否正常运行,再用更高的优化等级进行优化。
2.a、写中断程序一定要用using语句指定寄存器组。第1、2、3组都可以,不能是0,否则可能会main()函数冲突。从一个中断程序中调用函数必须和中断使用相同的寄存器组(摘自《Keil Cx51 编译器用户手册中文版》P129)。建议把原本中断函数需要调用的函数直接写在中断函数里,无须调用。
b、51单片机的中断有两个优先级。一个中断不会打断另一个相同优先级的中断。这样相同级别中断可以使用同一个组。比如:低优先级的中断函数都用 using 1,高优先级的中断都用 using 2。这样不会冲突。
3.C语言无符号数容易犯的错误。若定义成有符号数char,则不会陷入死循环。
main(){ unsigned char i;for(i = 2;i>=0;i--){ printf(“%d”,i);} }
4.C51忌讳使用绝对定位_at_,因为只要定义变量和变量的作用域,编译器就会把一个固定地址给这个变量,无须人工将其绝对定位,这样可能引发其他问题。
5.bit与sbit的区别:bit定义的位标量的地址是随机的,而sbit定义的位标量的地址是确定的。bit只能访问芯片内部RAM中的可寻址位20H-2FH,而sbit可以访问芯片内部RAM中的可寻址位和特殊功能寄存器中的可寻址位。注意不能直接在程序里用P1^0等位变量,需要经过sbit定义才可以使用。例如:
bit
tem;sbit led=P1^0;tem的地址是随机分配的,而led的地址则固定为0x90.0。sbit变量后面需要跟等号=。6.为了避免由于使用参数宏而带来意外的错误,需要注意以下几点:
6.1 宏的参数必须带括号,例如 #define CIRCLE_SQUARE(R)3.141*(R)*(R)6.2 对所使用的参数宏进行简单地展开检查;
6.3 使用简单表达式、对参数加括号、避免节外生枝的使用方式(例如“++”、“--”一类都属于不必要的附件运算);
6.4 在参数宏定义时,对于运算顺序通过括号进行明确的限定,只要遵循以上几点,就可以避免大多数应用场合的意外错误。
手把手教你写程序
内容:从最简单的程序入手,手把手教你写程序,让同学们拿到一个复杂的程序或者任务,能快速找到切入点,写出程序,再在此基础上优化程序。当拿到一个单片机任务时,不要急于动手写程序,先仔细分析它的以下几个点:
1、它要单片机整体实现什么功能
2、功能细分(模块化),先干什么,再干什么,最后干什么
3、画初步流程图,(把几个模块画出即可)
4、模块之间的分析:一个模块到另一个模块之间,怎么变换,怎么连接(优化流程图)
5、单个模块分析:每个模块要做什么(流程图细化)
6、所有模块结合连接,细化所有流程图
7、分析单个模块每步要用到的方法或者指令
8、总流程图定型
9、纸上写程序,对照流程图分析其可行性,若不可行则返回
10、上机调试,加注释
11、从小到大,一个功能一个功能地调试;
以上十一步,缺一不可(小程序例外)切记:流程图的确定很重要,需反复修改
大忌:拿到任务,不仔细分析就写程序。即使是小程序,我们也要养成良好的编程习惯,不要一味的追求结果。写小程序可能比别人快,若是大程序,一旦出现思维混乱,或者出现程序调试不出结果,那么你花在调试上的时间,要比别人的多。!!!磨刀不误砍柴工!!!程序的优化:属于后期工作,只有调试出来后,才去优化,如果一开始优化和写程序同时进行,一是加重你的思考量,二是出现问题无从下手。无疑增加了写程序的难度。对于一个初学者,写一个程序,本身头脑就处于紧张的状态,思考的问题就很多,如果此时把优化程序也考虑进去,你脑袋的负荷无疑加重,若你头脑精明,你可以把优化的地方,先在纸上记下来,等到调试结果正常,再把你想到的,优化的地方加进去。
7、如果在中断程序中改变了多字节类型的变量,那么中断程序以外的程序中(主程序,子函数)要使用该多字节类型变量的话,读写前要关中断,读写后再开中断。否则会导致偶尔读写错误。(实质为资源冲突)举一反三:
其他的数据类型也可能有这种影响。例如:长整型、浮点型。例如:
unsigned int ms_counter;void T0(){ //定时器程序每100毫秒中断一次,程序略 if(ms_counter<1000)ms_counter++;} void main(void){ //初始化定时器程序每100毫秒中断一次,程序略 unsigned char tt;ms_counter=0;tt=0;//用tt控制只响一次 while(1){ if(ms_counter<400){ if(tt==0){ tt=1;Sound_on();
} } else { Sound_off();} //其他程序 } }
8、sbit变量不能使用extern关键字,使其在不同的文件中被使用,如要在led.c和main.c文件中使用同一个变量led0,有以下下两种办法:
1.在各种文件中重复定义变量,如在led.c中定义sbit led0=P1^0;同样在main.c中定义sbit led0=P1^0;这样,led0就变成了全局变量,可以在两个文件中使用。
2.将sbit led0=P1^0定义到led.h头文件中,均在led.c和main.c中包含led.h这个头文件。
9、在多文件的程序中声明外部变量(extern和)
如果一个程序包含两个文件,在两个文件中都要用到同一个外部变量Num,不能分别在两个文件中各自定义一个外部变量Num,否则在进行程序的连接时会出现“重复定义”的 错误。正确的做法是:在任一个文件中定义外部变量Num,而在另一个文件中用extern对Num作“外部变量声明”。即extern Num;注意若Num为uchar类型,应当写为“extern uchar Num”,否则会当为int,而导致出错。
当使用static声明变量和函数时,需要在定义变量和函数的基础上加上此关键字,而不能单独使用。例如:
static int a;//定义性声明,需要时,直接使用变量a即可 a = 0x01;
static int funA(int a, int b);//声明,且static不起作用 int funA(int a ,int b)//定义,即使funA有static关键字修饰,但由于static不能单独使用,//故funA仍为外部函数。
{ …… } extern对变量进行声明时,如没有初始化,则为引用性声明,不含定义,如需使用此变量,需要进行定义。例如:
extern int a;//引用性声明,不含定义
extern int a = 0x01;//定义性声明,需要时,直接使用变量a即可 int a;//定义
extern对函数进行声明时,如没有函数体,则为引用性声明,不含定义。
extern int funB(int a ,int b);//引用性声明,不含定义,且extern声明可以省略
extern int funC(int a, int b)//定义性声明 { …… }
10、一般的,要尽量减少中断服务程序的内容和长度。因为在主程序中可以还需要随时响应其他的中断或事件。如果一个中断服务程序过程,很可能会影响到主程序对外部信号的检测和响应。通常,在中断程序中只是改变一些变量或标志位,在主程序中再根据变量或标志位的值进行判断,处理相应的事件。
11、在A/D和D/A转换电路中,电源电压和基准电压的稳定性,对转换的精度影响很大。另外,A/D和D/A转换电路中要特别注意地线的正确连接,否则转换结果将是不正确的,干扰影响将很严重。
12、根据C语言标准,左移“<<”和右移“>>”运算要求操作数至少是int,如果不满int,自动转换成int(C语言整型提升)。因此 uchar a=0x01;a<<8;实际运算,并不是8位数左移8位,而是int型左移8位。
13、在中断里调用其他函数,且要进行参数传递时,必须保证被调用函数所使用的寄存器组与中断函数一样,否则会产生不正确的结果。为了保证被调用的函数与中断函数使用的寄存器一致,可对被调用函数使用using,不过此函数只能被中断函数调用。
14、函数不使用using 时,所使用寄存器组保持与此函数被调用前相同,不对RS0和RS1的值进行修改;当使用了using 关键字后,此函数所使用的寄存器组与using所定义的一样。
15、当指定中断程序的工作寄存器组时,保护工作寄存器的工作就可以被省略。使用关键 字using 后跟一个0 到3 的数对应着4 组工作寄存器当指定工作寄存器组的时候默 认的工作寄存器组就不会被推入堆栈这将节省32 个处理周期,因为入栈和出栈都需要2 个处理周期。为中断程序指定工作寄存器组的缺点是所有被中断调用的过程都必须使用 同一个寄存器组否则参数传递会发生错误。
16、如何使用pdata 类型的变量?当要使用到pdata 类型的变量,如下: void main(void){ uchar pdata a;a=0x01;}
则需要进行如下设置,否则pdata 的变量a则会无效。
a、修改STARTUP.A51的内容。默认时,PPAGEENALBE为0,表示不允许pdata类型的变量,须将其值改为1;PPAGE表示pdata类型的变量存储在哪一页,01H表示存放在外部存储器的第1页,地址范围100H至1FFH,此时P2经STARTUP.A51处理后的值为0x01;此项设置需和BL51连接器的设置一致。
b、修改BL51连接器。根据STARTUP.A51中PPAGE所设置的值来填写Pdata的值,如下图。图中Pdata的值可以填写100H至1FFH中任意一个,表示pdata类型的变量从所填
写的值开始存储。例如,当Pdata填写的值为108H时,表示pdata类型的变量从108H开始存储,因此,存储范围变为了108H至1FFH。
另外,存储模式Compact的作用是将没有指定存储类型的变量定义为pdata类型,对uchar pdata a;变量的定义没有影响,但对uchar a;则有影响。
17、XBYTE的用法。XBYTE存在于#include
XBYTE[0x000F]=data; // 此语句表示将data写到外部RAM中的0x000F data=XBYTE[0x000F] // 此语句表示读取外部RAM中0x000F的数据 以下语句与上面的语句等效:
#define EX_RAM XBYTE[0x000F] //将EX_RAM定义为外部RAM的地址0x000F EX_RAM=data;// 此语句表示将data写到外部RAM中的0x000F data=EX_RAM // 此语句表示读取外部RAM中0x000F的数据
18、如何在keil中用汇编实现51中没有的指令
部分MCU与8051兼容,但会增加8051中没有的指令,如华邦的W77E58和N79E352等芯片,具有8051中没有的指令DEC DPTR。如何才Keil中实现此指令呢? 方法1:
在需要执行该指令的地方放置相应的机器码 MAIN:
MOV DPTR,#02H DB 0A5H;由于从数据手册上得知,DEC DPTR的机器码为0A5H,故此处相当于执行了DEC DPTR指令。
AJMP $ END
方法2:
使用宏定义的方法
/*宏定义,表示用DEC_DPTR代替MACRO与ENDM之间的内容*/ DEC_DPTR MACRO
DB 0A5H;此处不能与MACRO同一行 ENDM
MAIN: MOV DPTR,#02H DEC_DPTR;放置机器码0A5H,相当于执行DEC DPTR AJMP $ END
通过将以上两种方法生成的hex文件调入到编程器中,发现代码一样。经测试,同样可以用以上两种方法代替8051中已有的指令。
例如,从数据手册可知,MOV A,#0FH的长度为2字节,机器码的值为74H,0FH。因此,经验证,以下三个程序等效,产生的HEX文件一样 MAIN: MOV A,#55H DB 74H DB 0FH MOV P1,A AJMP $ END
MAIN: MOV A,#55H MOV A,#0FH MOV P1,A AJMP $ END
TEST MACRO DB 74H DB 0FH ENDM MAIN: MOV A,#55H TEST MOV P1,A AJMP $ END
18、汇编中包含头步骤:
例如,T2CON为定时器2的特殊功能寄存器,地址为0C8H,要对此寄存器赋值01H,除了
MOV 0C8H,#01H 和
T2CON EQU 0C8H MOV T2CON,#01H 外,还有用包含头文件的方法 #include
19、指针
C51 提供一个3 字节的通用存储器指针。通用指针的头一个字节表明指针所指的存储 区空间,另外两个字节存储16 位偏移量。对于DATA IDATA 和PDATA 段只需要8 位偏移量。Keil 允许使用者规定指针指向的存储段,这种指针叫具体指针。使用具体指针的好处是节省了存储空间编译器不用为存储器选择和决定正确的存储器操作指令产生代码这样就使代码更加简短但你必须保证指针不指向你所声明的存储区以外的地方否则会产生错误而且很难调试。
由于使用具体指针能够节省不少时间所以我们一般都不使用通用指针。
20、EEPROM存放开关机(复位)次数方法:每次开机(复位)读取EEPROM存放开关机的数据,并加1后重新写入EEPROM。
21、C51中,将printf函数与串口输出结合注意事项:
a、关串口中断;
b、初始化串口,并使TI=1;
c、KEIL里扩展出了b(8位),h(16位),l(32位)来对输入字节宽的设置
在Keil C51中用printf输出一个单字节变量时要使用%bd,若使用%d,则默认为双字节宽度,输出可能会出错。如
unsigned char counter;printf(“Current count: %bdn”, counter);而在标准C语言中都是使用%d: printf(“Current count: %dn”, counter);d、输出数据类型的长度应与定义的数据类型长度一致,如:
uint tem2=97;
printf(“%c,%bdn”,tem2,tem2);第一个输出会出错。
22、我一般不刻意的注意这个,都是从软件自身找问题的。
我写程序时对于软件抗干扰都是在程序状态上考虑意外情况的,例如:
if(a == 1){...} else if(a == 2){....} else{//这个else 一定得加的,即使自己认为不可能出现的情况也要加上
..//经过好多程序走飞的情况发现:大多情况都是缺少这个语句条件的,这 //语句可以写成重新初始化a } 还有程序出现堆栈比较深的运算(例如浮点乘除法后)或中断比较深,我加2个_nop_();
23、STC12C5410AD外部RAM使用方法:
a.在Keil中设置外部RAM的起始地址和大小,如下图
b.将变量定义为xdata即可。
24、中断嵌套
当有外部中断0时,中断标志位IE0由硬件自动置1,进入中断服务程序后,IE0被自动清0。若外部中断0触发信号在执行完中断服务程序后仍没有撤除,就会再次使已经变0的中断标志位IE0置1,再次进入中断服务程序;若在响应中断服务程序期间,再次产生外部中断0触发信号时,此中断不能被识别,因为CPU在响应中断时会自动关闭同一中断。
如果外部中断0比外部中断1的优先级高,当在响应外部中断0期间产生外部中断1时,如果执行完外部中断0后,外部中断1的中断请求标志位IE1仍没有清除的话,将会响应外部中断1的请求;但是如果在响应外部中断0期间,外部中断1的触发信号产生后又撤除的话,IE1也会自动清除,也就是说,执行完外部中断0后,不会去响应外部中断1。
当多个中断源同时向CPU请求中断时,CPU就可以通过中断优先权电路率先响应中断优先权高的中断请求,而把中断优先权低的中断请求暂时搁置起来,等到处理完优先权高的中断请求后再来响应优先权低的中断。
如果某一中断源提出中断请求后,CPU不能立即响应,只要该中断请求标志位不被软件人为清除,中断请求的状态就将一直保持,直到CPU响应中断为止。但是对于串行口中断,即使CPU响应了中断,其中断标志位RI/TI也不会自动清零,而必须在中断服务程序中设置
清除RI/TI的指令后,才会再一次地提出中断请求。
25、在满足应用要求的前提下,选择配较低的单片机,较小的RAM/ROM、较低的ADC分辨率、较低的ADC速率,较少的IO管脚都可以降低单片机的整体功耗。当然了,这个得能满足你产品需求的前提下。
26、对于一个数字系统而言,其功耗大致满足公式:P=CV2f。其中C为系统的负载电容,V为电源电压,f为系统工作频率[2]。功耗与电源电压的平方成正比,因此电源电压对系统的功耗影响最大,其次是工作频率,再次就是负载电容。负载电容对设计人员而言,一般是不可控的,因此设计一个低功耗系统,在不影响系统性能的前提下,尽可能地降低电源的电压和工作频率。对于大多数低功耗单片机来说,工作频率越低,意味着消耗的电流也越小,但是不能认为频率越低,系统整体功耗越小,因为工作频率降低,意味着需要更长的处理时间,其他外围电路消耗的电能就越多。目前有很多单片机都允许有两个或者两个以上的时钟源,低频时钟作为如UART、定时器等外围功能器件的时钟源,高频时钟作为系统的主时钟。在不需要高速运行的场合下,低频时钟也可以作为系统主时钟使用。对于需要在工作状态与空闲状态之间频繁切换的应用,在考虑单片机本身低功耗的同时,应该考虑切换时间和切换电流。考虑到有些场合单片机的工作特点,选择单片机不光要关注工作电流,更应该关注单片机休眠时的静态电流。单片机丰富的低功耗模式和极低的静态电流,在满足特定应用功能的同时,有效降低系统的功耗。尽量关闭MCU内部不用的资源,比如ATmega8内部的模拟比较器,默认是开着的,还有ATmega88内部的大多数资源都可以在不用的时候用软件关闭。
27、定时/ 计数器的实时性
定时/ 计数器启动计数后,当计满回0 溢出向主机请求中断处理,由内部硬件自动进行。但从回0 溢出请求中断到主机响应中断并作出处理存在时间延迟,且这种延时随中断请求时的现场环境的不同而不同,一般需延时3 个机器周期以上,这就给实时处理带来误差差。大多数应用场合可忽略不计,但对某些要求实时性苛刻的场合,可采用动态补偿措施。
所谓动态补偿,即在中断服务程序中对THx、TLx 重新置计数初值时,应将THx、TLx 从回0 溢出又重新从0 开始继续计数的值读出,并补偿到原计数初值中去进行重新设置。可考虑如下补偿方法: CLR EA ;禁止中断
MOV A,T L x ;读TLx 中已计数值 ADD A,#LOW ;LOW 为原低字节计数初值 MOV T L x,A ;设置低字节计数初值 MOV A,#HIGH ;原高字节计数初值送A ADDC A,T H x ;高字节计数初值补偿 MOV T H x,A ;置高字节计数初值 SETB EA ;开中断
28、动态读取运行中的定时器/计数值
在动态读取运行中的定时/ 计数器的计数值时,如果不加注意,就可能出错。这是因为不可能在同一时刻同时读取THx 和TLx 中的计数值。比如,先读TLx 后读THx,因为定时/ 计数器处于运行状态,在读TLx 时尚未产生向THx 进位,而在读THx 前已产生进位,这时读得的THx 就不对了;同样,先读THx 后读TLx 也可能出错。
一种可避免读错的方法是:先读THx,后读TLx,将两次读得的THx 进行比较;若两次读得的值相等,则可确定读的值是正确的,否则重复上述过程,重复读得的值一般不会再错。此法的软件编程如下:
RDTM: MOV A,THx ;读取THx 存A 中 MOV R0,TLx ; 读取TLx 存R0 中
CJNE A,THx,RDTM ;比较两次THx 值,若相等,则读得的值正确,否则重读 MOV R1,A ;将THx 存于R1 中
29、掉电及空闲模式
掉电方式
当PCON中的第二位PD为1时,进入掉电模式,不会执行任何指令,外部时钟停振,片内所有功能部件停止工作,如定时器,串行口,外部中断(部分增强型8051的外部中断可以工作),但片内RAM和SFR的内容保持不变。标准8051从掉电状态退出的惟一方法是硬件复位(部分增强型8051还可以通过外部中断来退出掉电状态),复位后,SFR被重新初始化,但RAM的内容不变。因此,若要使得8051在供电恢复正常后继续执行掉电前的程序,那就必须在掉电前预先把SFR中的内容保护到片内RAM,并在供电恢复正常后为SFR恢复到掉电前的状态。
当PCON的第一位IDEL为1时,进入空闲模式,CPU停止工作,不会执行任何指令,但中断、串行口和定时器可以继续工作。此时,CPU现场(即SP、PC、PSW和ACC等)、片内RAM和SFR中其他寄存器内容均维持不变。退出空闲模式有两种方法:
一、被允许中断的中断源发出中断请求;
二、硬件复位。30、看门狗应用
将喂狗操作(取反指令,如 CPL P1.0)分成两步,放在主程序和中断里执行。如将SETB P1.0放在主程序中,将CLR P1.0放在中断里,这样可以避免主程序跑飞,中断功能正常或者主程序正常,而中断跑飞的情况导致看门狗失效。
31、volatile作用
如果将将变量加上volatile修饰,则编译器保证对此变量的读写操作都不会被优化(肯定执行)。此例中i也应该如此说明。
一般说来,volatile用在如下的几个地方:
1、中断服务程序中修改的供其它程序检测的变量需要加volatile;
2、多任务环境下各任务间共享的标志应该加volatile;
3、存储器映射的硬件寄存器通常也要加volatile说明,因为每次对它的读写都可能由不同意义;
另外,以上这几种情况经常还要同时考虑数据的完整性(相互关联的几个标志读了一半被打断了重写),在1中可以通过关中断来实现,2中可以禁止任务调度,3中则只能依靠硬件的良好设计了。32、51准双向口读取:
只有1条指令:
MOV A,P1为读端口寄存器 有两条指令: MOV A,#0FFH MOV A,P1为读引脚
33、采用C语言和汇编语言混合编程是最佳的选择;
34、系统投入运行的最初时刻,应对系统进行自检和初始化。开机自检在初始化前执行,如果自检无误,则对系统进行正常初始化,通常包括硬件初始化和软件初始化两个部分。硬件初始化是指对系统中的各种硬件资源设定明确的初始状态,如对各种可编程芯片进行编程、对各I/O端口设定初始状态和为单片机的硬件资源分配任务等。软件初始化包括,对中断的安排、对堆栈的安排、状态变量的初始化、各种软件标志的初始化、系统时钟的初始化和各
种变量储存单元的初始化等。
35、按键连击速度一般为3-4次/s。
36、复合键利用两个以上按键同时按下时产生的按键效果,但实际情况下,不可能做到真正的“同时按下”,它们的时间差可以长达50ms左右,故当检测到有KEY1按键按下时,还要等待超过50ms,再判断是否还有其他按键按下,再解析按键。
37、数码管显示闪烁效果时,一般闪烁速度为1-4次/s。
38、大电流和高电压设备的启动和关闭都是由软件指令来完成,这些指令执行后,必然引起强烈的干扰,这些干扰不能算随机干扰,它们与软件完全相关;可以在最后才执行这些可能引起强烈干扰的I/O操作,之后立即进入睡眠状态,这样就不会干扰到CPU,等CPU醒来后,干扰的高峰也基本过去。
39、用积分时间为20ms整数倍的双积分型A/D转换方式,能有效地抑制50Hz工频干扰。40、掉电检测电路必须在电压下降到CPU最低工作电压之前就提出中断申请,提前时间为几百us到数ms,以便掉电中断程序进行掉电保护。
41、用定时器作看门狗:当为专职看门狗时,在主程序中周期性清0定时器计数值,以使定时器中断不能产生,当产生定时器中断时,表明看门狗溢出,此时应执行出错处理程序或者进行复位。当为兼职看门狗时,可以在定时器中断程序对计数值进行加1,若计数值大于某值时,表明看门狗溢出,而主程序中应周期性地对计数值进行清0。
42、中断中,冲突发生的条件:
1)某一资源为中断程序和主程序所使用;该资源可以为1个变量,也可以为1个数组或者1个缓冲区。
2)中断程序或主程序对该资源进行了写操作;
3)主程序不能用一条指令对资源完成读或者写操作。(这条不对,参考深入浅出AVR单片机P100的例子)
当这三个条件均满足时,即有可能发生资源冲突,导致程序偶然运行不正常。为了避免发生冲突,可以在主程序中先关中断,再对资源进行读或写,结束后再开中断。
当主程序对资源的访问比较费时,长期关中断可能影响系统的实时性;解决的办法是尽可能缩短关中断的时间,将一边访问,一边处理的工作方式改为集中访问,分批处理。如果是读该资源,则关中断迅速将该资源的内容转移到缓冲区,再开中断,然后再对缓冲区中的信息进行处理;如果是写该资源,则先边运算边写缓冲区,全部写好后再关中断,然后迅速将缓冲区中的内容复制到该资源中,边可以开中断了。
43、A/B*C的运算方案不如(A*C)/B的运算方案精度高。因此,应尽可能将出现偏差的运算往后排,先进行无偏差或偏差小的运算。在定点运算系统中,加减法只要不超限,是没有偏差的,乘法运算的结果会使字长增加,如双字节乘双字节,积为四字节,如果保留全部结果,则没有偏差的。乘法运算的结果会使字长增加,如双字节乘双字节,积为四字节,如果保留全部结果,则没有偏差;如果受字长限制,则要舍去低位字节,从而产生舍入偏差。除法几乎都是有偏差的能够刚好整除的情况是很少的。在浮点运算系统中,加减法由于要进行对阶操作,当两操作数的阶码相差较大时,绝对值大的数有可能将绝对值小的数淹没,使运算的结果仍为绝对值大的数,一点儿也看不出绝对值小的数对结果的影响。相比之下,浮点乘法和浮点除法引起的偏差就比较小,它们能够保持一个比较稳定的运算精度。另外,不管在定点系统中还是在浮点系统中,都要尽可能避免两个数值相近的数过早相减,因为他们都可能是近似值,相减以后,差的有效数值大大减少,必然带来更大的相对误差。经过后续运算之后,结果可能离真实值相差甚远。再有,尽可能不要用绝对值小的数作分母,否则引起的误差也是很大的。
44、要对软件标志位的使用进行说明;对于全局定义的软件标志,它有惟一的定义;对于局
部定义的软件标志,必须注明其有效范围。
45、软件理论已经证明:任何一个程序(除某些短小的子程序外)都存在错误(缺陷),人们可以通过合理的测试来证明它仍然存在错误,却无法证明它已经没有错误。软件测试应该把发现错误作为目的,而不能把“程序调通”作为目的。
1.P0口能驱动8个TTL电路意思: 8051单片机P0口驱动8个TTL电路的意思,TTL电路输入悬浮时相当于输入高电平,因此P0口输出高电平驱动TTL电路几乎不需输出电流。TTL电路输入为低电平时最少要释放1mA电流,因此P0口输出低电平时吸收的电流大于8mA。TTL输出高电平最大1.6mA,输出低电平时吸收的最大电流 16mA。51输出最好用低电平有效,推动PNP管,因为51复位后IO为高电平,如果用高电平有效推N管的话上电复位后会先让外部电路动做。
2.在51里,有一条指令没有写进书本,机器码为A5,执行操作:将下一个字节跳过而不管它是单字节指令还是双字节或三字节指令的一部分.如果反汇编工具不识别A5指令的话,你在A5以后的程序反汇编后就错乱无章.当成个数据,用db a5 即可
3.有些51系统容易复位,一般是电路设计上的问题。很多电路介绍的复位电路都是10u和8.2k,但是在实践过程中我们发现该电路在电源不稳时很容易复位,特别是附近有大干扰时,如继电器动作等。我建议使用22u和1k的复位电路,有许多电路改为该数值后就工作稳定了。当然,最好的办法还是使用专用复位电路或三极管电路,但是那样要增加成本和体积。
4.电路中的滤波电容一定要注意加上,最好每个芯片都再加一个约0.1uf的电容,这样对电路的稳定性很有好处。如果使用了看门狗电路,就有可能是软件问题,程序工作到某些环节时忘记了复位看门狗,结果计数满了就复位了。
5.如果在中断程序中改变了多字节类型的变量,那么中断程序以外的程序中(主程序,子函数),读写前要关中断,读写后再开中断。举一反三:
其他的数据类型也可能有这种影响。例如:长整型、浮点型。
上面的例子是中断里写,主程序中读。相反主程序写,中断里读也可能出错。
6.教你一招,别说我损。。
写一个测试代码,反复向EEPROM中的某几个不用的空位字节写入0x55,直到把它干到寿命终结不能写为止,如果按照10MS写一个字节计算的话,大约只需要20分钟就能干掉它。然后向这个芯片中烧入你的正常代码,当然了,这个代码中应该有一段上电检测EEPROM这几个字节的代码,先尝试向它写入0Xaa,然后再读出来看看是否写入成功,如果没写入则再来两次,如果始终不能写入,这当作检查通过,如果就判断为检查失败,这个时候代码要装着‘不知情’继续执行正确代码,下面的‘破坏’行为应该如何做就不要我讲了把? 破坏行为要装的掩蔽一点,例如调一段代码檫除FLASH的代码,嘿嘿,那对方肯定以为CHIP质量不好容易出现FLASH数据丢失,如果对方使用了AD什么的,可以偶尔人为让它波动大一点,这样对方一般只会怀疑PCB和硬件电路弄的不好,而不会想到是代码动手脚了,长久以后他的用户肯定也会认为他们的产品质量不好,你这个时候就可以向他的客户推广你的产品了。。
上电写EEPROM的次数要在你自己的产品质量承诺的寿命时间之内,否则你自己的产品也
可能增加维修。。
这个方法特别适合在外接单挣钱的工程师,你可能给了对方几个CHIP做测试,对方测试通过偏说不行,就是不给你余款,然后把CHIP拿去CRACK,妄想省掉这个钱,NND,让他们见鬼去把,俺这招已经对付了不少不良分子。。
7.AD键盘
8.防解密高招
高招, 解密
使用一些带内部晶振和内部EEPROM的单片机,如PIC16F913和ATMEGA8等,带内部晶振的单片机有一个寄存器OSCTUNE(或OSCCAL),这个是芯片厂家用来校准内部晶振的,范围从0-31,出厂时同型号的单片机这个寄存器的值是不一样.我们可以利用一些隐藏功能,将OSCTUNE寄存器的值存入内部的EEPROM中,开机时读取EEPROM的值,再与OSCTUNE的值相比较, 若二者相同系统正常工作,若不
相同则不正常工作.解密者将解密的程序烧写进单片机中后,会发大部分的芯片不能正常工作,因为他们不知道这个隐藏的功能.举例说明: 芯片为PIC16F913,这个厂品有4个按键(KEY0、KEY1、KEY2、KEY3),内部我们可以设定这样子一个隐藏的功能,如果KEY0与KEY1同时按下3秒钟以上,会将OSCTUNE寄存器存入单片机的EEPROM中。
开机复位后,读取EEPROM中的数据,与OSCTUNE寄存器相比较,若二者相同系统正常工作,若不相同则不正常工作。以上有三个重点:
1、对于OSCTUNE寄存器不要进行写的操作,只进行读的操作,因为写了一次以后,就一直是你写的这个数据的。
2、刚才介绍的KEY0、KEY1同时按下3秒钟这个功能,可不能让解密者(包括产品的用户)知道,当然大家可以用别的隐藏的功能。
3、单片机中的OSCTUNE寄存器(或OSCCAL)的值,同一种型号的单片机不是每一个都是一样的,有32个数据,也就是说32个芯片中有一个是与解密的单片机是一样的。这样子造成的后果是:解密者解密了你的程序以后,却发现有些单片机可以正常工作,可有些单片机不能正常工作,可以说是大部分的单片机不能正常工作。
不过需要注意一下:要是遇到强干扰把EEPROM中的数据改变了看客户怎么收拾你!
9.PIC16F887A中,要求SLEEP指令后的下一条指令为NOP;不知51和AVR的芯片是否需要注意这一点。经查,AVR的datasheet无此要求,可能是其唤醒时,存在启动延时。
10.中断随时随刻都有可能产生,故编写程序时,需要时刻注意中断的影响。
11.注意以下语句在某些编译器下,结果可能出错:
unsigned char a,b;
unsigned int sum;
a=0x80;
b=0x80;
sum=a+b;
12.编程序最重要是好维护。几个执行时间和程序的可读性比,和开发时间比,我认为是不用考虑的。为了几个机器周期而把程序搞得很复杂,是非常愚蠢的行为。可是很多人多乐此不疲啊。总体系统的算法是要考虑优化的问题的,这点我是赞同的。天天在技术上对着几行程序去优化,而导致开发速度减慢,是非常愚蠢的行为。
13.串口通信协议:引导码/识别码+长度+命令字+data+校验
通过引导码/识别码、长度、校验三步检测 每当出错则丢弃当前数据并还原接收状态和空间…………
14.当准备调试一块板的时候,一定要先认真的做好目视检查,检查在焊接的过程中是否有可见的短路和管脚搭锡等故障,检查是否有元器件型号放置错误,第
一脚放置错误,漏装配等问题,然后用万用表测量各个电源到地的电阻,以检查是否有短路,这个好习惯可以避免贸然上电后损坏单板。调试的过程中要有平和的心态,遇见问题是非常正常的,要做的就是多做比较和分析,逐步的排除可能的原因,要坚信“凡事都是有办法解决的”和“问题出现一定有它的原因”,这样最后一定能调试成功。
做一个硬件设计人员要锻炼出良好的沟通能力,面对压力的调节能力,同一时间处理多个事务的协调和决断能力和良好平和的心态,还有细心和认真等等。
对初学者来说,在新的领域面前一穷二白,要学的东西太多太多,一味机械地试图学习这些“高深的语法”和“看不懂的技巧”,胡乱模仿些别人的“优秀风格”,不能说完全无所得,只能说会学得很累,而且往往事倍功半---久而久之,信心丧失殆尽,半途而废。学习编程,做自己便好。学习眼前能够看懂的内容,多写自己会写的程序。对于已经学到的东西,仔细地体会、思考,发掘其中的发展;学会用一种研究的心态去考察你的每一个疑问;不可以轻易地人云亦云,在网上看了别人无责任的经验,甚至是写错了,丢在一边,懒得改的东西之后,就放弃了自己的探索;要学会坚持自我,遇到别人先进的做法,在自己还没有体会到自己当前这种做法的劣势之前,不要轻易盲从;同样,使用了先进的方法,在没有同时理解这种做法的优点和缺点之前,请不要轻易地感叹“这种方法好,跟帖顶一下!”“大师高手宁有种乎?”坚持“体会到了才是学到了”的态度,最终形成自己的风格,形成自己的技巧。
第四篇:C语言编程
#include(stdio.h)
main()
{ int question[4]={-1,-1,-1,-1},i=0,j=0,k=0,A=0,B=0,answer[4]={0};
char again='y';
while(again=='y'){ srand((int)time(0));
while(i4){ k=(int)rand()%10;
for(j=0;ji;j++)if(k==question[j]){ k=-1;break;}
if(k==-1)continue;question[i]=k;i++;}/*while i*/
for(i=8;i0;i--)/*还有8次机会*/
{ A=0;B=0;printf(“n你还剩下%d次机会。”,i);
printf(“n请输入四个0-9之间的数字,中间用空格隔开n”);for(j=0;j4;j++)scanf(“%d”,&answer[j]);
for(j=0;j4;j++)
for(k=0;k4;k++)
{ if(answer[j]==question[k]){ if(j==k)A++;else B++;} }/*for*/
if(A==4){ again='0';
printf(“n你赢了,还继续猜吗?(y/n)”);
while(again!='y'&&again!='n')
scanf(“...%c”,&again);break;}/*if*/
printf(“n%dA%dB”,A,B);if(i==1){ again='0';
printf(“n你输了,正确答案是”);
for(j=0;j4;j++)
printf(“%dt”,question[j]);
printf(“n还继续吗?(y/n)”);
while(again!='y'&&again!='n')scanf(“%c”,&again);
printf(“%c”,again);break;}/*if*/ }/*for changce*/ }/*while again*/ printf(“感谢您玩这个游戏。”);}
第五篇:C语言经典编程题(推荐)
C语言经典编程题
题目01:在一个已知的字符串中查找最长单词,假定字符串中只含字母和空格,空格用来分隔不同的单词。
[cpp] view plain copy print?
1.2.3.4.5.6.7.8.9.int main(){
// 用数组定义一个字符串
char array[50] = “zha junju zhamengjun z mengjun”;
char *str = array;// 定义指针变量str,指向数组array
int len = 0;// 定义变量len,用于计数
int max = 0;// 定义变量max,存放最长单词的长度
char *p = 0;// 定义指针变量p,指向最长单词的首字符
10.11.// 判断指针当前指向的字符是不是' '
12.while(*str!= ' ')13.{
14.if(*str!= ' ')// 判断字符是不是空格 15.{
16.len++;// 计数加1 17.18.// 判断最大长度跟len长度的大小
19.if(max < len){
20.max = len;// 如果max小于len,将len赋值给max
21.p = str1];
46.} 47.else
48.{
49.sumDaysOfMonth += pingYear[monthDay;// 定义整型变量days,存储一个月内相隔的天数
54.int sumDays = 0;// 定义整型变量sumDays,存储两个时间点相隔的总天数
55.56.// 得到两个时间点相隔的总天数
57.sumDays = sumDaysOfYear + sumDaysOfMonth + days;58.59.printf(“两个时间点相隔%d天n”, sumDays);60.61.// 根据相隔的天数,判断小明遇到的人
62.if((sumDays % 2 == 0)&&(sumDays % 3 == 0))63.{
64.printf(“小明既结识了帅哥又结识了美女!n”);65.}
66.else if(sumDays % 2 == 0)67.{
68.printf(“小明结识了帅哥!n”);69.}
70.else if(sumDays % 3 == 0)71.{
72.printf(“小明结识了美女!n”);73.} 74.else
75.{
76.printf(“小明没有结识帅哥和美女n”);77.} 78.}
心得体会:(1)利用for循环遍历,if条件来判断是平年还是闰年,求出相隔年数的累加的天数(2)同理,利用第一步的方法,求出相隔月数的累加的天数,只是要注意每月的天数,根据平年和闰年的不同分别保存在两个不同的数组中,以便利于累加
(3)将输入的日减1,计算出当月相隔的天数,最后求出两个日期相隔的总天数。(4)根据总天数取余2和3,判断出小明当天结识的是美女还是帅哥。
题目05:提示用户输入一个正整数n,利用while循环计算并输出:1-2+3-4+5-6+7…+n的和。
[cpp] view plain copy print?
1.2.3.4.5.6.7.8.9.int main(){
// 1.定义变量存储用户输入的整数
int n = 0;
// 2.判断n是否为正整数
while(n <= 0){
// 2.1 提示输入
printf(“输入一个正整数:n”);
10.11.// 2.2 让用户输入
12.scanf(“%d”, &n);13.} 14.15.// 3.计算阶乘
16.int sum = 0;// 存储计算结果
17.int current = 0;// 当前要累加的数值
18.while(current < n){ 19.current++;20.21.// 如果是偶数,就减
22.if(current % 2 == 0){ 23.sum-= current;24.} else { // 如果是奇数,就加
25.sum += current;26.} 27.} 28.29.// 4.输出结果 30.printf(“%dn”, sum);31.32.return 0;33.}
心得体会:
(1)确保从键盘上输入的是一个整数(用while来判断);(2)用while循环来遍历从1到n的值;
(3)通过奇偶性判断所要累加数值的正负性,奇数就累加,偶数就累减。
题目06:提示用户输入一个正整数n,计算并输出n的阶乘结果:1*2*3*…*n。
[cpp] view plain copy print?
1.2.3.4.5.6.7.8.9.int main(){
// 1.定义变量存储用户输入的整数
int n = 0;
// 2.判断n是否为正整数
while(n <= 0){
// 2.1 提示输入
printf(“输入一个正整数:n”);
10.11.// 2.2 让用户输入
12.scanf(“%d”, &n);13.} 14.15.// 3.计算阶乘
16.int result = 1;// 存储计算结果
17.int current = 1;// 当前的乘数
18.while(current <= n){
19.result *= current;// 累乘每次的乘数
20.current++;// 乘完一次就++
21.} 22.23.// 4.输出阶乘结果
24.printf(“%d!= %dn”, n, result);25.26.return 0;27.}
心得体会:
(1)可以利用for循环或者while循环进行遍历,利用累乘即可求出值。(2)还可以利用递归来做,更简单。
题目07:编写一个函数,判断某个字符串是否为回文。回文就是从左边开始读 和 从右边开始读 都是一样的,比如“abcba” [cpp] view plain copy print?
1.2.3.4.5.6.7.8.9.int main(){
printf(“%dn”, isHuiwen(“a”));
return 0;}
/*
返回1代表是回文
返回0代表不是回文
10.*/ 11.int isHuiwen(char *str)
12.{
13.// 1.定义一个指向变量left指向字符串的首字符
14.char *left = str;
15.// 2.定义一个指向变量right指向字符串的末字符
16.char *right = str + strlen(str)1)+ count(n);// 求出每一个阶乘的累加和
58.} 59.60.// 定义和求出一个累加和
61.int count(int n)
62.{
63.if(n == 1)64.return 1;
65.return count(n1;22.23.// 如果左边元素的下标 < 右边元素的下标
24.while(left < right)25.{
26.// 利用中间变量交换两个元素的值
27.int temp = array[left];28.array[left] = array[right];29.array[right] = temp;30.31.// 交换一次后,左边元素下标增加,右边元素下标减小
32.33.left++;34.right--;35.} 36.}
心得体会:
(1)首先要明白一点,为什么不能通过sizeof(array)/ sizeof(int)来求出数组元素的个数?因为当数组作为参数传递的时候,函数的参数array实际上当做变量来存储传来的数组首元素的地址。而每一个指针变量占用8个字节。
(2)分别拿出数组首元素和数组尾元素,然后利用中间变量交换两个元素的值。(3)利用while循环,遍历数组元素,并使left< right保证循环到中间即可,否则每个元素又进行一次交换,结果值没有改变。
(4)函数reverse不需要返回值,因为改变了形参数组也就改变了外面的实参数组,因为数组是按址传递的。