软件研究室培训(C语言)课件_第1页
软件研究室培训(C语言)课件_第2页
软件研究室培训(C语言)课件_第3页
软件研究室培训(C语言)课件_第4页
软件研究室培训(C语言)课件_第5页
已阅读5页,还剩77页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

C语言C语言1主要内容一、学习编程语言的一些建议

二、指针三、预处理器四、结构、联合和枚举五、sizeof六、参考书籍和资料主要内容一、学习编程语言的一些建议2(1)多动手学习编程唯一方法是编写调试程序。看再多的书,效果也比不上设计调试一个简单程序。(2)养成良好的编程习惯

养成好习惯很难。所以良好的编程习惯要从一开始培养。

(3)注意细节

不应漏过任何一个技术细节。只有细节的日积月累,才能有技术上的提高。

(4)了解开发环境

在学习编程语言的同时,也需要了解编程语言的开发、使用环境。如硬件平台(51、MSP430、PC)、操作系统(uCOS、Linux、DOS)、集成开发环境(KeilC,BorlandC)等。(5)多看书、勤思考很多国外的经典书籍是由拥有几十年经验的质深程序员编写的。这些书里最重要的是融入了作者多年的编程经验。多看书,再结合自身平时的积累,不断思考总结是提高编程技术最有效的办法。第一章学习编程语言的一些建议(1)多动手第一章学习编程语言的一些建议3第二章指针2.1指针的基本概念2.1.1指针是什么?

指针是一个变量,它的值是另外一个变量的地址。例12.1.2指针的类型

指针所存储的那个变量类型,就称为指针的类型。例2有三个不同类型的指针:intI[2],*pI=&I[0];右边的三个运算有何不同?pI++;charC[2],*pC=&C[0];pC++;floatF[2],*pF=&F[0];pF++;0x00C7指针变量int*p0x00C7变量inta上面例中的两个0x00C7有什么区别?第二章指针2.1指针的基本概念2.1.2指针的42.1.3指针的三个要素

指针指向的地址(指针的内容);指针指向的地址上的内容;指针本身的地址。

例3:intA,*pA,**ppA;pA=&A;ppA=&pA;在复杂的指针都可以通过下表来分析:

&ppAppA*ppA**ppA

&pApA*pA

&AA第二章指针2.1.3指针的三个要素第二章指针5第二章指针2.1.4指针的大小(指针变量占用的内存空间)与所用的CPU寻址空间大小和类型有关,而与指针类型无关。8位CPU的指针长度为1~2个字节(51单片机的情况较为复杂,是1~3个字节);16位CPU的指针长度为2个字节(如MSP430);32位CPU的指针长度为4个字节(如Intel80386)。上面所述是通常情况,并不是全部符合。第二章指针2.1.5指针的初始化

变量在没有赋值之前,其值不定的。对于指针变量,可以表述为:指向不明。程序访问了一个没有初始化的指针:int*p;p的内存是随机的一个数,比如:0x3FF0073D程序随即访问内存地址:

0x3FF0073D0x3FF0073D是哪里的内存?说不定正好是Windows老大要用的内存,你竟敢访问!Windows一生气,蓝屏。一个指向不明的指针,是非常危险的!!!

因此,指针在使用前一定要初始化;在使用前一定要确定指针是非空的!!!

第二章指针2.1.4指针的大小(指针变量占用的内存空6第二章指针2.2数组与指针对于数组的两个概念:1.C语言中只有一维数组,数组的大小必须在编译时作为一个常数确定下来。数组的元素可以是任何类型,甚至是数组,由此可以方便地得到多维数组;2.数组的任何操作,即使采用数组下标进行的运算都等于对应的指针运算。可以用指针行为替代数组下标的运算。例4:inta[4],*p;p=a;//等价于p=&a[0];*(a+2)=0;//等价于a[2]=0;p[2]=0;//等价于a[2]=0;第二章指针2.2数组与指针7但数组不同于指针:数组名a是指向数组起始位置的“常量”。因此,不能对数组名进行赋值操作。

例5:inta[4],*p;p=a;//正确a=p;//错误p++;//正确a++;//错误第二章指针但数组不同于指针:第二章指针8第二章指针2.3空指针与通用指针(1).空指针是个特殊指针值,也是唯一对任何指针类型都合法的指针值。一个指针变量具有空指针值,表示它当时没指向有意义的东西,处于闲置状态。空指针值用0表示,这个值绝不会是任何程序对象的地址。给一个指针赋值0就表示要它不指向任何有意义的东西。为了提高程序的可读性,标准库定义了一个与0等价的符号常量NULL,程序里可以写:p=NULL;//注意不要与空字符NUL混淆,NUL等价于‘\0’或者:p=0;注意:在编程时,应该将处于闲置的指针赋为空指针;在调用指针前一定要判断是否为空指针,只有在非空情况下才能调用。第二章指针2.3空指针与通用指针9(2).通用指针通用指针可以指向任何类型的变量。通用指针的类型用(void*)表示,因此也称为void指针。下面的第三行定义了两个通用指针:intn,*p;double*q;void*gp1,*gp2;可以直接把任何变量的地址赋给通用指针。例如,有了上面定义,下面赋值是合法的:gp1=(void*)&n;可以把通用指针的值赋给普通的指针。如果被赋值指针与通用指针所指变量的类型不符,需要写强制转换:p=(int*)gp1;第二章指针(2).通用指针第二章指针10第二章指针2.4函数指针2.4.1函数指针的定义函数指针即指向函数地址的指针。利用该指针可以知道函数在内存中的位置。因此也可以利用函数指针调用函数。

函数指针的定义方法:

<类型>(*<函数指针名>)(......)

例如:

int(*func)(void)

这里,func就是一个函数指针。

注意:int*func(void)和int(*func)(void)的区别

int*func(void);//这是返回一个整型指针的函数

int(*func)(void);//这是一个函数指针

第二章指针2.4函数指针112.4.2函数指针的使用

例6:假定有下面的函数声明

intptr;

intfn(int);

int(*fp)(int);

指出下面的语句是否合法?,为什么?。

fp=fn;//正确,将函数fn的地址赋给fp

fp=fn(5);//错误,返回给fp的结果不是一个函数地址。

fp=&ptr;//错误,ptr的地址不在程序代码区,两种数据类型不能转换。

从上面的例子可以看出:

(1)不能将普通变量的地址赋给函数指针;

(2)不能将函数的调用赋给函数指针

(3)可以将函数名赋给一个函数指针第二章指针2.4.2函数指针的使用第二章指针12第二章指针2.4.2函数指针的用途一旦函数可以通过指针被传递、被记录,这开启了许多应用,特别是下列三者:多态(polymorphism):指用一个名字定义不同的函数,这函数执行不同但又类似的操作,从而实现“一个接口,多种方法”。多线程(multithreading):将函数指针传进负责建立多线程的

API中:例如Win32的

CreateThread(...pF...)。回调(call-back):所谓的回调机制就是:「当发生某事件时,自动呼叫某段程序代码」。事件驱动(event-driven)的系统经常透过函数指针来实现回调机制,例如

Win32的

WinProc其实就是一种回调,用来处理窗口的讯息。第二章指针2.4.2函数指针的用途132.4.3函数指针数组例7:在一个计算器的例子中,有如下一些语句:

switch(oper){

case

ADD:

result=add(op1,op2);

break;

case

SUB:

result=sub(op1,op2);

break;

...}

对于一个复杂的计算器,switch语句将非常长。我们可以用函数指针数组来完成。

double

add(double,double);

double

sub(double,double);

...

double

(*oper_func[])(double,double)={add,sub,...};第2个步骤是用下面语句替换前面整条switch语句:

result=oper_func[oper](op1,op2);

oper从数组中选择正确的函数指针,而函数调用操作符将执行这个函数。第二章指针2.4.3函数指针数组第二章指针14第三章预处理器3.1预处理器的作用可以将预处理器看作为一个文本编辑器。它只对程序的文本起作用,而不会进行其他任何依赖于C语言语法的工作。它负责在正式编译前将源程序进行转换,替换宏定义、删去无需编译的代码、插入包含的文件等等。出于几个主要原因,预处理器为我们提供了一些简化的途径:首先,可以通过改变一个数字并重新编译程序来改变一个特殊量的所有实例。其次,可以定义一些东西,它们看起来象函数但没有函数调用所需的运行开销。例如,putchar()和getchar()通常实现为宏以避免对每一个字符的输入输出都要进行函数调用。第三章预处理器3.1预处理器的作用15第三章预处理器3.2#define3.2.1带参数的宏定义当需要有一个程序块,看上去和用起来像函数一样,但是没有函数调用时的开销时,可以采用宏定义。宏定义可以象函数一样带参数,如:

例8:

#defineAdd(a,b)((a)+(b))

注意:

(1)括号的作用预防由运算符优先级引起有关的问题。例9:#defineAdd(a,b)a+b……Average=Add(Num1,Num2)/2;

例中,运算式最终由预处理器展开为:Average=Num1+Num2/2;第三章预处理器3.2#define16

(2)不要忽视空格的作用例10:#definef(x)((x)-1)……Num=f(Num);

上面这个语句会被展开成什么,a还是b,为什么?a.Num=((Num)-1);b.Num=(x)((x)-1)(Num);

(3)宏不是类型定义例11:#definePWORDint*typedefPBYTEchar*;……PWORDpA,pB;//pB是什么类型?PBYTEpC,pD;//pD是什么类型?第三章预处理器(2)不要忽视空格的作用第三章预处理器173.3#include<>

#include“”有什么区别?#include<>

:在使用<>时,编译器将会到指定的系统或标准头文件目录查找头文件。通常用于标准或系统提供的头文件。#include“”:在使用“”时,编译器将会到用户创建的项目文件所在目录查找头文件。通常用于程序自己的头文件。例12:

#include<stdio.h>#include<math.h>#include“disp.h”#include“output.h”第三章预处理器3.3#include<>

#include“18第三章预处理器3.4条件编译(1).条件编译可以按照不同的编译条件,组织成不同的代码。例13:

#defineMCS51……#ifdefMCS51Baud=9600;#elifMSP430Baud=4800;#elifPC104Baud=115200;#endif

……第三章预处理器3.4条件编译19(2).条件编译可用于调试。将用于调试的语句放入条件编译中。例14:#defineDEBUG……TotalNum=a+b;#ifdefDEBUGprintf(“Thetotalnumis:%d”,TotalNum);#endif

……当程序调试结束后,只需将#defineDEBUG一句取消,即可将程序中所有的调试语句删去。第三章预处理器(2).条件编译可用于调试。将用于调试的语句放入条件编译中20(3).在通用的模块中,可以通过条件编译实现功能的可裁减。例14:#defineTMR_SET_EN……#ifdefTMR_SET_ENvoidTMR_Set(chartmr_init,chartmr_mod,chartmr_fnct){……}#endif

……如果不需要TMR_Set功能,只需将#defineTMR_SET_EN语句删除即可。轻松实现TMR模块的可裁减特性。第三章预处理器(3).在通用的模块中,可以通过条件编译实现功能的可裁减。21(4).在宏定义时,避免重复定义。例15:#ifndefTRUE#defineTRUE1#endif#ifndefFALSE#defineFALSE0#endif第三章预处理器(4).在宏定义时,避免重复定义。第三章预处理器22第四章结构、联合和枚举4.1结构体结构是由若干(可不同类型的)数据项组合而成的复合数据对象,这些数据项称为结构的成分或成员。(1)字段C语言的结构还提供了一种定义字段的机制,使人在需要时能把几个结构成员压缩到一个基本数据类型成员里存放,这可以看作是一种数据压缩表示方式。例16:structpack{unsigneda:2;unsignedb:8;unsignedc:6;}pk1,pk2;结构变量pk1或者pk2的三个成员将总共占用16位存储,其中a占用2位,b占用8位,c占用6位。第四章结构、联合和枚举4.1结构体23(2)结构体内部的成员的对齐在计算结构体长度(尤其是用sizeof)时,需要注意!根据不同的编译器和处理器,结构体内部的成员有不同的对齐方式,这会引起结构体长度的不确定性。例17:#include

<stdio.h>struct

a{char

a1;char

a2;char

a3;}A;

struct

b{short

a2;char

a1;}B;

voidmain(void){

printf(“%d,%d,%d,%d”,sizeof(char),sizeof(short),sizeof(A),sizeof(B));

}

在TurboC2.0中结果都是

1,2,3,3

在VC6.0中是

1,2,3,4第四章结构、联合和枚举(2)结构体内部的成员的对齐第四章结构、联合和枚举24第四章结构、联合和枚举字节对齐的细节和编译器实现相关,但一般而言,满足三个准则:

1)结构体变量的首地址能够被其最宽基本类型成员的大小所整除;

2)结构体每个成员相对于结构首地址的偏移量(offset)都是成员大小的整数倍,

如有需要编译器会在成员之间加上填充字节(internaladding);

3)结构体的总大小为结构体最宽基本类型成员大小的整数倍,如有需要编译器会在最末一个成员之后加上填充字节(trailingpadding)。对于上面的准则,有几点需要说明:

1)结构体某个成员相对于结构体首地址的偏移量可以通过宏offsetof()来获得,这个宏也在stddef.h中定义,如下:

#defineoffsetof(s,m)(size_t)&(((s*)0)->m)

2)基本类型是指前面提到的像char、short、int、float、double这样的内置数据类型,这里所说的“数据宽度”就是指其sizeof的大小。由于结构体的成员可以是复合类型,比如另外一个结构体,所以在寻找最宽基本类型成员时,应当包括复合类型成员的子成员,而不是把复合成员看成是一个整体。但在确定复合类型成员的偏移位置时则是将复合类型作为整体看待。第四章结构、联合和枚举字节对齐的细节和编25第四章结构、联合和枚举4.2联合体在一个结构(变量)里,结构的各成员顺序排列存储,每个成员都有自己独立的存储位置。联合变量的所有成员共享从同一片存储区。因此一个联合变量在每个时刻里只能保存它的某一个成员的值。(1)联合变量的初始化联合变量也在可以定义时直接进行初始化,但这个初始化只能对第一个成员做。例如下面的描述定义了一个联合变量,并进行了初始化:例18:

uniondata{charn;floatf;};uniondatau1={3};//只有u1.n被初始化第四章结构、联合和枚举4.2联合体264.3枚举枚举是一种用于定义一组命名常量的机制,以这种方式定义的常量一般称为枚举常量。一个枚举说明不但引进了一组常量名,同时也为每个常量确定了一个整数值。缺省情况下其第一个常量自动给值0,随后的常量值顺序递增。(1)给枚举常量指定特定值与给变量指定初始值的形式类似。如果给某个枚举量指定了值,跟随其后的没有指定值的枚举常量也将跟着顺序递增取值,直到下一个有指定值的常量为止。例如写出下面枚举说明:enumcolor{RED=1,GREEN,BLUE,WHITE=11,GREY,BLACK=15};这时,RED、GREEN,、BLUE的值将分别是1、2、3,WHITE、GREY的值将分别是11、12,而BLACK的值是15。第四章结构、联合和枚举4.3枚举第四章结构、联合和枚举27(2)用枚举常量作为数组长度例19:

typedefenum{WHITE,RED,BLUE,YELLOW,BLACK,COLOR_NUM}COLOR;……floatBallSize[COLOR_NUM];上例中当颜色数量发生变化时,只需在枚举类型定义中加入或删去颜色。无需修改COLOR_NUM的定义。与大量使用#define相比既简洁又可靠。如:

typedefenum{WHITE,RED,BLUE,COLOR_NUM}COLOR;第四章结构、联合和枚举(2)用枚举常量作为数组长度第四章结构、联合和枚举28第五章sizeof5.1sizeof的定义和使用sizeof是C/C++中的一个操作符(注意!不是函数!就像return一样)。其作用就是返回一个对象或者类型所占的内存字节数。sizeof有三种使用形式,如下:

1)sizeof(var);//sizeof(变量);

2)sizeof(type_name);//sizeof(类型);3)sizeofvar;//sizeof变量;所以,

inti;

sizeof(i);//ok

sizeofi;//ok

sizeof(int);//ok

sizeofint;//error为求形式统一,不建议采用第3种写法,忘掉它吧!第五章sizeof5.1sizeof的定义和使用295.2数组的sizeof

数组的sizeof值等于数组所占用的内存字节数,如:

例21:char*ss="0123456789";sizeof(ss);//结果4,ss是指向字符串常量的字符指针sizeof(*ss);//结果1,*ss是第一个字符charss[]="0123456789";sizeof(ss);//结果11,计算到‘\0’位置,因此是10+1sizeof(*ss);//结果1,*ss是第一个字符

charss[100]="0123456789";sizeof(ss);//结果100,表示在内存中的大小100×1strlen(ss);//结果10,strlen是到‘\0’为止之前的长度intss[100]="0123456789";sizeof(ss);//结果200,ss表示在内存中的大小100×2strlen(ss);//错误,strlen的参数只能是char*且必须以‘\0’结尾第五章sizeof5.2数组的sizeof

数组的sizeof值等于30第五章sizeof5.3结构体的sizeof

参见(结构体内部的成员的字节对齐)

5.4当数组作为函数的参数时,

sizeof的使用

编译器把数组参数当作指针对待

,因而在使用sizeof时,报告的是指针的大小。例22:charS[]=“1234567890”;size_tfoo(char*s){……return(sizeof(s));}voidmain(void){size_tsz=foo(S);//sz=4(PC),sz=2(MSP430)size_tsz1=sizeof(S);//sz1=11;}第五章sizeof5.3结构体的sizeof31第六章可移植性6.1数据类型的大小

在C和C++里,基本数据类型大小并没有明确的规定,仅给出下列一些规则:sizeof(char)<=sizeof(short)<=sizeof(int)<=sizeof(long);sizeof(float)<=sizeof(double);char至少是8位,short和int至少是16位,long至少是32位。如:在8051上int为16位,占2个字节;在PC机上int为32位。占4个字节。因此,我们建议用类似下面的类型定义来替代:typedefunsignedcharBOOLEAN;typedefunsignedcharINT8U;typedefsignedcharINT8S;typedefunsignedintINT16U;typedefsignedintINT16S;typedefunsignedlongINT32U;typedefsignedlongINT32S;typedeffloatFP32;typedefdoubleFP64;第六章可移植性6.1数据类型的大小32第六章可移植性6.2char的符号问题

char数据类型到底是有符号还是无符号,C和C++并没有给出明确规定。

不同编译器和平台,对char是否有符号规定不一。无符号char的范围为0~255;有符号char的范围为–128~127。因此,在移植时会出现溢出(无符号有符号)或负数变为正数(有符号无符号)。解决办法:在变量无需用到负数时,明确定义其为unsignedchar或是INT8U;在变量可能用到负数时,明确定义其为signedchar或是INT8S。6.3

结构体对齐问题参见(结构体内部的成员的字节对齐)第六章可移植性6.2char的符号问题33第六章可移植性6.4

字节顺序大多数微处理器的存储结构是8位地址空间。字节许多数据项地址数字和字符串太长,不能用单个字节保存必须用一系列连续字节保存当使用用多字节保存的数据时,字节顺序就成为一个问题。对字节顺序的两个通行的方法被广泛使用:第一个方法是调用小ENDIAN就是低字节首先保存。例如一个16位整数值0x1234用小ENDIAN方法保存两个连续的字节如下地址内容+0+10x340x12第二个方法是调用大ENDIAN就是高字节先保存低字节后保存例如一个16位整数值0x1234用大ENDIAN方法保存两个连续的字节如下地址内容址+0+1+0+10x120x34第六章可移植性6.4字节顺序+0+10x340x12346.5使用标准库

尽量使用C语言定义的标准库。这样,从一个系统移植到另一个系统,你的代码很有希望还能具有同样的行为方式。8051的LCALL指令保存下一个指令在堆栈中地址的低字节首先推入堆栈因此地址是以小ENDIAN格式保存;所有别的16位和32位值以大ENDIAN格式保存高字节先保存例如LJMP和LCALL指令期望地址以大ENDIAN格式保存;浮点数根据IEEE-754格式保存以大ENDIAN格式首先保存高字节如果8051嵌入应用平台和别的CPU通讯必须知道其所用的字节顺序。第六章可移植性6.5使用标准库8051的LCALL指令保存下35第六章参考书籍和资料6.1C语言初级教材(1)《C程序设计语言》(TheCProgrammingLanguage

)作者:BrianW.Kernighan,DennisM.Ritchie简介:本书是由C语言的设计者BrianW.Kernighan和DennisM.Ritchie编写的一部介绍标准C语言及其程序设计方法的权威性经典著作。一本必读的程序设计语言方面的参考书。(2)《CPrimerPlus中文版》(CPrimerPlus)作者:StephenPrata简介:作为核心计算机技术成熟、完整的参考书籍,PrimerPlus系列历经十数年不衰,通过学习《CPrimerPlus(第五版)中文版》,你将奠定坚实的C编程基础。(3)《C和指针》(PointersonC)作者:KennethA.Reek简介:本书提供与C语言编程相关的全面资源和深入讨论。全书覆盖了数据、语句、操作符和表达式、指针、函数、数组、字符串、结构和联合等几乎所有重要的C编程话题。书中给出了很多编程技巧和提示。第六章参考书籍和资料6.1C语言初级教材366.2C语言进阶书籍(1)《C陷阱与缺陷》(CTrapsandPitfalls)作者:AndrewKoenig简介:作者以自己1985年在Bell实验室时发表的一篇论文为基础,结合自己的工作经验扩展成为这本对C程序员具有珍贵价值的经典著作。本书的出发点不是要批判C语言,而是要帮助C程序员绕过编程过程中的陷阱和障碍。(2)《C专家编程》(ExpertCProgramming

)作者:PerterVanDerLinDen简介:展示了最优秀的C程序员所使用的编码技巧。书中C的历史、语言特性、声明、数组、指针、链接、运行时、内存以及如何进一步学习C++等问题进行了细致的讲解和深入的分析。全书撷取几十个实例进行讲解,对C程序员具有非常高的实用价值。

(3)《C语言编程常见问题解答》(CProgramming:JusttheFaqs

)作者:PaulS.R.Chisholm等简介:这是一本专门解答c语言编程常见问题的著作。书中所覆盖的内容相当广泛,并附有大量鲜明的例子。第六章参考书籍和资料6.2C语言进阶书籍第六章参考书籍和资料37第六章参考书籍和资料6.3嵌入式编程书籍(1)《C/C++嵌入式系统编程》(ProgrammingEmbeddedSystemsinCandC++)作者:MichaelBarr简介:本书中的技术和范例代码均可直接应用于各种实际的嵌入式系统项目。有过嵌入式系统开发经验的读者,也将从中获益匪浅。(2)《嵌入式实时操作系统μC/OS-II》(MicroC/OS-IITheReal-TimeKernel)作者:JeanJ.Labrosse简介:μC/OSII是源码公开的实时内核,是专为嵌入式应用设计的。通过对μC/OSII源代码的分析与描述,讲述了多任务实时的基本概念、竞争与调度算法、任务间同步与通信、存储与定时的管理以及如何处理优先级反转问题;介绍如何将μC/OSII移植到不同CPU上,如何调试移植代码。

(3)《嵌入式系统构件》(EmbeddedSystemsBuildingBlocks

)作者:JeanJ.Labrosse简介:本书介绍了构建嵌入式系统的一些通用模块,如键盘扫描器、显示器接口、计量器和输入/输出。大部分代码都是用可移植的C语言编写。第六章参考书籍和资料6.3嵌入式编程书籍38第六章参考书籍和资料6.4网上资源(1)中国软件开发网/是面向IT专业技术人员和软件开发及应用企业,以专业社区为中心的IT专业知识传播网站。(2)C

语言常见问题集http://ols3.tnc.idv.tw/prg/cpp/ccfaq/ccfaq.html英文

C-FAQ(2004

7

3

日修订版)的中文版。(3)『C程序设计』读书笔记/minute/c.php

(4)技术中心服务器\\文件夹《软件组培训资料》(5)单片机C语言论坛/C51BBS论坛是国内著名的单片机论坛(6)ProgramminginChttp://www.lysator.liu.se/c/index.html关于C语言编程的资源非常多,但都是E文的第六章参考书籍和资料6.4网上资源39THEENDTHEEND40演讲完毕,谢谢观看!演讲完毕,谢谢观看!41C语言C语言42主要内容一、学习编程语言的一些建议

二、指针三、预处理器四、结构、联合和枚举五、sizeof六、参考书籍和资料主要内容一、学习编程语言的一些建议43(1)多动手学习编程唯一方法是编写调试程序。看再多的书,效果也比不上设计调试一个简单程序。(2)养成良好的编程习惯

养成好习惯很难。所以良好的编程习惯要从一开始培养。

(3)注意细节

不应漏过任何一个技术细节。只有细节的日积月累,才能有技术上的提高。

(4)了解开发环境

在学习编程语言的同时,也需要了解编程语言的开发、使用环境。如硬件平台(51、MSP430、PC)、操作系统(uCOS、Linux、DOS)、集成开发环境(KeilC,BorlandC)等。(5)多看书、勤思考很多国外的经典书籍是由拥有几十年经验的质深程序员编写的。这些书里最重要的是融入了作者多年的编程经验。多看书,再结合自身平时的积累,不断思考总结是提高编程技术最有效的办法。第一章学习编程语言的一些建议(1)多动手第一章学习编程语言的一些建议44第二章指针2.1指针的基本概念2.1.1指针是什么?

指针是一个变量,它的值是另外一个变量的地址。例12.1.2指针的类型

指针所存储的那个变量类型,就称为指针的类型。例2有三个不同类型的指针:intI[2],*pI=&I[0];右边的三个运算有何不同?pI++;charC[2],*pC=&C[0];pC++;floatF[2],*pF=&F[0];pF++;0x00C7指针变量int*p0x00C7变量inta上面例中的两个0x00C7有什么区别?第二章指针2.1指针的基本概念2.1.2指针的452.1.3指针的三个要素

指针指向的地址(指针的内容);指针指向的地址上的内容;指针本身的地址。

例3:intA,*pA,**ppA;pA=&A;ppA=&pA;在复杂的指针都可以通过下表来分析:

&ppAppA*ppA**ppA

&pApA*pA

&AA第二章指针2.1.3指针的三个要素第二章指针46第二章指针2.1.4指针的大小(指针变量占用的内存空间)与所用的CPU寻址空间大小和类型有关,而与指针类型无关。8位CPU的指针长度为1~2个字节(51单片机的情况较为复杂,是1~3个字节);16位CPU的指针长度为2个字节(如MSP430);32位CPU的指针长度为4个字节(如Intel80386)。上面所述是通常情况,并不是全部符合。第二章指针2.1.5指针的初始化

变量在没有赋值之前,其值不定的。对于指针变量,可以表述为:指向不明。程序访问了一个没有初始化的指针:int*p;p的内存是随机的一个数,比如:0x3FF0073D程序随即访问内存地址:

0x3FF0073D0x3FF0073D是哪里的内存?说不定正好是Windows老大要用的内存,你竟敢访问!Windows一生气,蓝屏。一个指向不明的指针,是非常危险的!!!

因此,指针在使用前一定要初始化;在使用前一定要确定指针是非空的!!!

第二章指针2.1.4指针的大小(指针变量占用的内存空47第二章指针2.2数组与指针对于数组的两个概念:1.C语言中只有一维数组,数组的大小必须在编译时作为一个常数确定下来。数组的元素可以是任何类型,甚至是数组,由此可以方便地得到多维数组;2.数组的任何操作,即使采用数组下标进行的运算都等于对应的指针运算。可以用指针行为替代数组下标的运算。例4:inta[4],*p;p=a;//等价于p=&a[0];*(a+2)=0;//等价于a[2]=0;p[2]=0;//等价于a[2]=0;第二章指针2.2数组与指针48但数组不同于指针:数组名a是指向数组起始位置的“常量”。因此,不能对数组名进行赋值操作。

例5:inta[4],*p;p=a;//正确a=p;//错误p++;//正确a++;//错误第二章指针但数组不同于指针:第二章指针49第二章指针2.3空指针与通用指针(1).空指针是个特殊指针值,也是唯一对任何指针类型都合法的指针值。一个指针变量具有空指针值,表示它当时没指向有意义的东西,处于闲置状态。空指针值用0表示,这个值绝不会是任何程序对象的地址。给一个指针赋值0就表示要它不指向任何有意义的东西。为了提高程序的可读性,标准库定义了一个与0等价的符号常量NULL,程序里可以写:p=NULL;//注意不要与空字符NUL混淆,NUL等价于‘\0’或者:p=0;注意:在编程时,应该将处于闲置的指针赋为空指针;在调用指针前一定要判断是否为空指针,只有在非空情况下才能调用。第二章指针2.3空指针与通用指针50(2).通用指针通用指针可以指向任何类型的变量。通用指针的类型用(void*)表示,因此也称为void指针。下面的第三行定义了两个通用指针:intn,*p;double*q;void*gp1,*gp2;可以直接把任何变量的地址赋给通用指针。例如,有了上面定义,下面赋值是合法的:gp1=(void*)&n;可以把通用指针的值赋给普通的指针。如果被赋值指针与通用指针所指变量的类型不符,需要写强制转换:p=(int*)gp1;第二章指针(2).通用指针第二章指针51第二章指针2.4函数指针2.4.1函数指针的定义函数指针即指向函数地址的指针。利用该指针可以知道函数在内存中的位置。因此也可以利用函数指针调用函数。

函数指针的定义方法:

<类型>(*<函数指针名>)(......)

例如:

int(*func)(void)

这里,func就是一个函数指针。

注意:int*func(void)和int(*func)(void)的区别

int*func(void);//这是返回一个整型指针的函数

int(*func)(void);//这是一个函数指针

第二章指针2.4函数指针522.4.2函数指针的使用

例6:假定有下面的函数声明

intptr;

intfn(int);

int(*fp)(int);

指出下面的语句是否合法?,为什么?。

fp=fn;//正确,将函数fn的地址赋给fp

fp=fn(5);//错误,返回给fp的结果不是一个函数地址。

fp=&ptr;//错误,ptr的地址不在程序代码区,两种数据类型不能转换。

从上面的例子可以看出:

(1)不能将普通变量的地址赋给函数指针;

(2)不能将函数的调用赋给函数指针

(3)可以将函数名赋给一个函数指针第二章指针2.4.2函数指针的使用第二章指针53第二章指针2.4.2函数指针的用途一旦函数可以通过指针被传递、被记录,这开启了许多应用,特别是下列三者:多态(polymorphism):指用一个名字定义不同的函数,这函数执行不同但又类似的操作,从而实现“一个接口,多种方法”。多线程(multithreading):将函数指针传进负责建立多线程的

API中:例如Win32的

CreateThread(...pF...)。回调(call-back):所谓的回调机制就是:「当发生某事件时,自动呼叫某段程序代码」。事件驱动(event-driven)的系统经常透过函数指针来实现回调机制,例如

Win32的

WinProc其实就是一种回调,用来处理窗口的讯息。第二章指针2.4.2函数指针的用途542.4.3函数指针数组例7:在一个计算器的例子中,有如下一些语句:

switch(oper){

case

ADD:

result=add(op1,op2);

break;

case

SUB:

result=sub(op1,op2);

break;

...}

对于一个复杂的计算器,switch语句将非常长。我们可以用函数指针数组来完成。

double

add(double,double);

double

sub(double,double);

...

double

(*oper_func[])(double,double)={add,sub,...};第2个步骤是用下面语句替换前面整条switch语句:

result=oper_func[oper](op1,op2);

oper从数组中选择正确的函数指针,而函数调用操作符将执行这个函数。第二章指针2.4.3函数指针数组第二章指针55第三章预处理器3.1预处理器的作用可以将预处理器看作为一个文本编辑器。它只对程序的文本起作用,而不会进行其他任何依赖于C语言语法的工作。它负责在正式编译前将源程序进行转换,替换宏定义、删去无需编译的代码、插入包含的文件等等。出于几个主要原因,预处理器为我们提供了一些简化的途径:首先,可以通过改变一个数字并重新编译程序来改变一个特殊量的所有实例。其次,可以定义一些东西,它们看起来象函数但没有函数调用所需的运行开销。例如,putchar()和getchar()通常实现为宏以避免对每一个字符的输入输出都要进行函数调用。第三章预处理器3.1预处理器的作用56第三章预处理器3.2#define3.2.1带参数的宏定义当需要有一个程序块,看上去和用起来像函数一样,但是没有函数调用时的开销时,可以采用宏定义。宏定义可以象函数一样带参数,如:

例8:

#defineAdd(a,b)((a)+(b))

注意:

(1)括号的作用预防由运算符优先级引起有关的问题。例9:#defineAdd(a,b)a+b……Average=Add(Num1,Num2)/2;

例中,运算式最终由预处理器展开为:Average=Num1+Num2/2;第三章预处理器3.2#define57

(2)不要忽视空格的作用例10:#definef(x)((x)-1)……Num=f(Num);

上面这个语句会被展开成什么,a还是b,为什么?a.Num=((Num)-1);b.Num=(x)((x)-1)(Num);

(3)宏不是类型定义例11:#definePWORDint*typedefPBYTEchar*;……PWORDpA,pB;//pB是什么类型?PBYTEpC,pD;//pD是什么类型?第三章预处理器(2)不要忽视空格的作用第三章预处理器583.3#include<>

#include“”有什么区别?#include<>

:在使用<>时,编译器将会到指定的系统或标准头文件目录查找头文件。通常用于标准或系统提供的头文件。#include“”:在使用“”时,编译器将会到用户创建的项目文件所在目录查找头文件。通常用于程序自己的头文件。例12:

#include<stdio.h>#include<math.h>#include“disp.h”#include“output.h”第三章预处理器3.3#include<>

#include“59第三章预处理器3.4条件编译(1).条件编译可以按照不同的编译条件,组织成不同的代码。例13:

#defineMCS51……#ifdefMCS51Baud=9600;#elifMSP430Baud=4800;#elifPC104Baud=115200;#endif

……第三章预处理器3.4条件编译60(2).条件编译可用于调试。将用于调试的语句放入条件编译中。例14:#defineDEBUG……TotalNum=a+b;#ifdefDEBUGprintf(“Thetotalnumis:%d”,TotalNum);#endif

……当程序调试结束后,只需将#defineDEBUG一句取消,即可将程序中所有的调试语句删去。第三章预处理器(2).条件编译可用于调试。将用于调试的语句放入条件编译中61(3).在通用的模块中,可以通过条件编译实现功能的可裁减。例14:#defineTMR_SET_EN……#ifdefTMR_SET_ENvoidTMR_Set(chartmr_init,chartmr_mod,chartmr_fnct){……}#endif

……如果不需要TMR_Set功能,只需将#defineTMR_SET_EN语句删除即可。轻松实现TMR模块的可裁减特性。第三章预处理器(3).在通用的模块中,可以通过条件编译实现功能的可裁减。62(4).在宏定义时,避免重复定义。例15:#ifndefTRUE#defineTRUE1#endif#ifndefFALSE#defineFALSE0#endif第三章预处理器(4).在宏定义时,避免重复定义。第三章预处理器63第四章结构、联合和枚举4.1结构体结构是由若干(可不同类型的)数据项组合而成的复合数据对象,这些数据项称为结构的成分或成员。(1)字段C语言的结构还提供了一种定义字段的机制,使人在需要时能把几个结构成员压缩到一个基本数据类型成员里存放,这可以看作是一种数据压缩表示方式。例16:structpack{unsigneda:2;unsignedb:8;unsignedc:6;}pk1,pk2;结构变量pk1或者pk2的三个成员将总共占用16位存储,其中a占用2位,b占用8位,c占用6位。第四章结构、联合和枚举4.1结构体64(2)结构体内部的成员的对齐在计算结构体长度(尤其是用sizeof)时,需要注意!根据不同的编译器和处理器,结构体内部的成员有不同的对齐方式,这会引起结构体长度的不确定性。例17:#include

<stdio.h>struct

a{char

a1;char

a2;char

a3;}A;

struct

b{short

a2;char

a1;}B;

voidmain(void){

printf(“%d,%d,%d,%d”,sizeof(char),sizeof(short),sizeof(A),sizeof(B));

}

在TurboC2.0中结果都是

1,2,3,3

在VC6.0中是

1,2,3,4第四章结构、联合和枚举(2)结构体内部的成员的对齐第四章结构、联合和枚举65第四章结构、联合和枚举字节对齐的细节和编译器实现相关,但一般而言,满足三个准则:

1)结构体变量的首地址能够被其最宽基本类型成员的大小所整除;

2)结构体每个成员相对于结构首地址的偏移量(offset)都是成员大小的整数倍,

如有需要编译器会在成员之间加上填充字节(internaladding);

3)结构体的总大小为结构体最宽基本类型成员大小的整数倍,如有需要编译器会在最末一个成员之后加上填充字节(trailingpadding)。对于上面的准则,有几点需要说明:

1)结构体某个成员相对于结构体首地址的偏移量可以通过宏offsetof()来获得,这个宏也在stddef.h中定义,如下:

#defineoffsetof(s,m)(size_t)&(((s*)0)->m)

2)基本类型是指前面提到的像char、short、int、float、double这样的内置数据类型,这里所说的“数据宽度”就是指其sizeof的大小。由于结构体的成员可以是复合类型,比如另外一个结构体,所以在寻找最宽基本类型成员时,应当包括复合类型成员的子成员,而不是把复合成员看成是一个整体。但在确定复合类型成员的偏移位置时则是将复合类型作为整体看待。第四章结构、联合和枚举字节对齐的细节和编66第四章结构、联合和枚举4.2联合体在一个结构(变量)里,结构的各成员顺序排列存储,每个成员都有自己独立的存储位置。联合变量的所有成员共享从同一片存储区。因此一个联合变量在每个时刻里只能保存它的某一个成员的值。(1)联合变量的初始化联合变量也在可以定义时直接进行初始化,但这个初始化只能对第一个成员做。例如下面的描述定义了一个联合变量,并进行了初始化:例18:

uniondata{charn;floatf;};uniondatau1={3};//只有u1.n被初始化第四章结构、联合和枚举4.2联合体674.3枚举枚举是一种用于定义一组命名常量的机制,以这种方式定义的常量一般称为枚举常量。一个枚举说明不但引进了一组常量名,同时也为每个常量确定了一个整数值。缺省情况下其第一个常量自动给值0,随后的常量值顺序递增。(1)给枚举常量指定特定值与给变量指定初始值的形式类似。如果给某个枚举量指定了值,跟随其后的没有指定值的枚举常量也将跟着顺序递增取值,直到下一个有指定值的常量为止。例如写出下面枚举说明:enumcolor{RED=1,GREEN,BLUE,WHITE=11,GREY,BLACK=15};这时,RED、GREEN,、BLUE的值将分别是1、2、3,WHITE、GREY的值将分别是11、12,而BLACK的值是15。第四章结构、联合和枚举4.3枚举第四章结构、联合和枚举68(2)用枚举常量作为数组长度例19:

typedefenum{WHITE,RED,BLUE,YELLOW,BLACK,COLOR_NUM}COLOR;……floatBallSize[COLOR_NUM];上例中当颜色数量发生变化时,只需在枚举类型定义中加入或删去颜色。无需修改COLOR_NUM的定义。与大量使用#define相比既简洁又可靠。如:

typedefenum{WHITE,RED,BLUE,COLOR_NUM}COLOR;第四章结构、联合和枚举(2)用枚举常量作为数组长度第四章结构、联合和枚举69第五章sizeof5.1sizeof的定义和使用sizeof是C/C++中的一个操作符(注意!不是函数!就像return一样)。其作用就是返回一个对象或者类型所占的内存字节数。sizeof有三种使用形式,如下:

1)sizeof(var);//sizeof(变量);

2)sizeof(type_name);//sizeof(类型);3)sizeofvar;//sizeof变量;所以,

inti;

sizeof(i);//ok

sizeofi;//ok

sizeof(int);//ok

sizeofint;//error为求形式统一,不建议采用第3种写法,忘掉它吧!第五章sizeof5.1sizeof的定义和使用705.2数组的sizeof

数组的sizeof值等于数组所占用的内存字节数,如:

例21:char*ss="0123456789";sizeof(ss);//结果4,ss是指向字符串常量的字符指针sizeof(*ss);//结果1,*ss是第一个字符charss[]="0123456789";sizeof(ss);//结果11,计算到‘\0’位置,因此是10+1sizeof(*ss);//结果1,*ss是第一个字符

charss[100]="0123456789";sizeof(ss);//结果100,表示在内存中的大小100×1strlen(ss);//结果10,strlen是到‘\0’为止之前的长度intss[100]="0123456789";sizeof(ss);//结果

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论