




版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
第5章嵌入式C语言程序设计基础5.1嵌入式C语言的预处理伪指令5.2嵌入式C语言的基本数据类型5.3程序的控制结构5.4函数5.5数组5.6指针5.7构造数据类型5.8汇编语言与C/C++的混合编程5.9嵌入式Linux下C语言编程——文件的操作第5章嵌入式C语言程序设计基础5.1嵌入式C语言的预处理伪指令1.文件包含伪指令文件包含伪指令可将头文件包含到程序中,头文件中定义的内容符号常量,复合变量原型、用户定义的变量原型和函数的原型说明等。编译器编译预处理时用文件包含的正文件内容替换到实际程序中。(1)文件包含伪指令的格式#include<头文件名.h>;标准头文件#include“头文件名.h”
;自定义头文件#include宏标识符
第5章嵌入式C语言程序设计基础5.1嵌入式C语言的预处理伪指令(2)包含文件伪指令的说明●常在头文件名后用.h作为扩展名,可带或不带路经。●头文件可分为标准头文件和自定义头文件。●尖括号内的头文件为标准头文件,由开发环境或系统提供。●双引号内的头文件为用户自定义头文件。搜索时,首先在当前目录中搜索,其次按环境变量include指定的目录顺序搜索。●搜索到头文件后,就将该伪指令直接用头文件内容替换。第5章嵌入式C语言程序设计基础5.1嵌入式C语言的预处理伪指令例5.1标准头文件定义#include<string.h>#include<stdio.h>string.h和stdio.h是标准头文件,按环境变量include指定的目录顺序搜索string.h和stdio.h。例5.2用户自定义头文件定义
#include“s3c2410-adc.h”s3c2410-adc.h头文件是用户自定义有关三星s3c2410的ARM处理器的A/D转换器各寄存器。第5章嵌入式C语言程序设计基础5.1嵌入式C语言的预处理伪指令2.宏定义伪指令宏定义伪指令分为:简单宏、参数宏、条件宏、预定义宏及宏释放。(1)简单宏格式如下:#define宏标识符宏体●宏体是由单词序列组成。宏体超长时,允许使用续行符“\”进行续行,续行符和其后的换行符\n都不会进入宏体。●在定义宏时,应尽量避免使用C语言的关键字和预处理器的预定义宏,以免引起灾难性的后果。●在源文件中,用预处理器伪指令定义过宏标识符之后,就可用宏标识编写程序。当源文件被预处理器处理时,每遇到该宏标识符,预处理器便将宏展为宏体。第5章嵌入式C语言程序设计基础5.1嵌入式C语言的预处理伪指令(2)参数宏格式如下:#define宏标识符(形式参数表)宏体形式参数表为逗号分割的形式参数。●宏体是由单词序列组成。宏体超长时,允许使用续行符“\”进行续行,续行符和其后的换行符\n都不会进入宏体。●使用参数宏时,形式参数表应换为同样个数的实参数表,这一点类似于函数的调用。参数宏与函数的区别在于参数宏的形参数表中没有类型说明符。●预处理器在处理参数宏时使用2遍宏展开。第1遍展开宏体,第2遍对展开后的宏体用实参数替换形式参数。第5章嵌入式C语言程序设计基础例5.3在Linux系统的/include/asm-arm/arch-s3c2410/S3C2410.h头文件中定义了各NandFlash控制寄存器,其源代码如下:#definebNAND_CTL(Nb)__REG(0x4e000000+(Nb))#defineNFCONFbNAND_CTL(0x00)#defineNFCMDbNAND_CTL(0x04)#defineNFADDRbNAND_CTL(0x08)#defineNFDATAbNAND_CTL(0x0c)#defineNFSTATbNAND_CTL(0x10)#defineNFECCbNAND_CTL(0x14)5.1嵌入式C语言的预处理伪指令第5章嵌入式C语言程序设计基础5.1嵌入式C语言的预处理伪指令例5.4在Linux下ARMS3C2410X芯片的A/D转换的驱动程序的头文件s3c2410-adc.h中定义了下面三个宏。#defineADC_WRITE(ch,prescale)((ch)<<16|(prescale))/*ADC通道号与预标值合成一个字*/#defineADC_WRITE_GETCH(data)(((data)>>16)&0x7)
/*获得ADC通道号*/#defineADC_WRITE_GETPRE(data)((data)&0xff)
/*获得ADC的预定标值*/第5章嵌入式C语言程序设计基础
例5.5在Linux下ARMS3C2410X芯片的A/D转换的驱动程序实现代码s3c2410-adc.c中的系统资源和宏定义。#defineDEVICE_NAME"s3c2410-adc“/*定义ADC设备的名字*/#defineADCRAW_MINOR1staticintadcMajor=0;/*定义ADC设备的主设备号*/typedefstruct{structsemaphorelock;/*内核信号量,当多个用户程序同时访问一个ADC控制器时,用lock进行同步*/wait_queue_head_twait;/*等待队列*/intchannel;/*ADC通道号*/intprescale;/*预定标值*/}ADC_DEV;5.1嵌入式C语言的预处理伪指令第5章嵌入式C语言程序设计基础staticADC_DEVadcdev;#defineSTART_ADC_AIN(ch,prescale)\do{ADCCON=PRESCALE_EN|PRSCVL(prescale)|ADC_INPUT((ch));\ADCCON|=ADC_START;\}while(0)/*设置S3C2410X的ADC的通道为ch、预定标值为prescale*///PRESCALE_EN
宏对应ARMS3C2410X芯片的A/D转换控制寄存器的第14位PRSCEMN,即A/D转换器预标器使能;//PRSCVL宏对应ARMS3C2410X芯片的A/D转换控制寄存器的第6位,设置预定标值;//ADC_INPUT宏对应ARMS3C2410X芯片的A/D转换控制寄存器的第3~5位,选择通道号;//ADCCON|=ADC_START;ADCCON0为置1,准备采集数据5.1嵌入式C语言的预处理伪指令第5章嵌入式C语言程序设计基础5.1嵌入式C语言的预处理伪指令(3)条件宏定义格式如下:格式1:#ifdef宏标识符#undef宏标识符#define宏标识符宏体#else#define宏标识符宏体#endif第5章嵌入式C语言程序设计基础格式2:#ifndef宏标识符
#define宏标识符宏体#else#undef宏标识符#define宏标识符宏体#endif其中:
格式1是测试存在,格式2是测试不存在。
else可有,也可没有。5.1嵌入式C语言的预处理伪指令第5章嵌入式C语言程序设计基础5.1嵌入式C语言的预处理伪指令(4)宏释放用于释放原先定义的宏标识符。经释放后的宏标识符可再次用于定义其他宏体。格式如下:#undef宏标识符例5.6#defineSIZE512…buf=SIZE*blks/*宏扩展为buf=512*blks;*/…undefSIZE#defineSIZE128…buf=SIZE*blks/*宏扩展为buf=128*blks;*/第5章嵌入式C语言程序设计基础5.1嵌入式C语言的预处理伪指令3.条件编译伪指令格式如下:#if(条件表达式1)
…#elif(条件表达式2)
…#elif(条件表达式3)…#elif(条件表达式n)…#else…#endif
第5章嵌入式C语言程序设计基础5.1嵌入式C语言的预处理伪指令例5.7#if_B0SIZE==B0SIZE_BYTEtypedefunsignedcharPB0SIZE;#elif_B0SIZE==B0SIZE_SHORTtypedefunsignedshortPB0SIZE;#elif_B0SIZE==B0SIZE_WORDtypedefunsignedlongPB0SIZE;#endif第5章嵌入式C语言程序设计基础5.2嵌入式C语言的基本数据类型
5.2.1数据类型与表达式第5章嵌入式C语言程序设计基础5.2嵌入式C语言的基本数据类型(1)类型修饰符
第5章嵌入式C语言程序设计基础5.2嵌入式C语言的基本数据类型(2)访问修饰符C语言有两个用于控制访问和修改变量方式的修饰符,分别是常量(const)和易变量(volatile)。带const修饰符定义出的常量在程序运行过程中始终保持不变。例如:constintnum;例如:constintnum=100;volatile修饰符用于提醒编译程序,该变量的值可以不通过程序中明确定义的方法来改变。const和volatile可以同时使用。例如,假设0x30是一个只随外部条件而变化的口地址值,那么就恰好需要用下述说明来避免偶然因素所产生的副作用的影响。constvolatileunsignedchar*port=0x30;
第5章嵌入式C语言程序设计基础5.2嵌入式C语言的基本数据类型2.构造数据类型数组是一组连续、有序的存放在一起的具有相同类型的数据。结构体是将不同类型的数据按一定顺序存放在一起的数据结构。共用体是将不同类型的数据都存放在同一起始地址的内存单元中,共用一段内存以节省内存单元。枚举是只有几种可能的值,将其一一列举出来。实际是用符号来表示若干个可取的整型值,它是整型的一个子集。
第5章嵌入式C语言程序设计基础5.2嵌入式C语言的基本数据类型3.指针类型指针可以有效地表示复杂的数据结构;能动态分配内存;能方便地使用字符串;有效而方便地使用数组;在调用函数时能得到多于一个的值;能直接处理内存地址等。指针类型迥异于前述各种数据类型,不管是简单类型的数据,还是构造类型数据,均是代表数据的,而指针类型是代表地址的。第5章嵌入式C语言程序设计基础5.2嵌入式C语言的基本数据类型5.2.2常量1.数值常量(1)整型常量整型常量也称为整型常数或整数。C整型常量按进制分可分为十进制整数,八进制整数和十六进制整数。(2)实型常量实型常量有单精度实型常量和双精度实型常量。可用小数形式或指数形式表示。(3)字符常量第5章嵌入式C语言程序设计基础5.2嵌入式C语言的基本数据类型第5章嵌入式C语言程序设计基础5.2嵌入式C语言的基本数据类型2.字符串常量字符串常量简称字符串,是用一对双引号括起来的字符序列。例如“China”就是一个字符串常量。若数字被定义为字符型之后就不能参与数值运算,如`5`和5是不同的。`5`是字符常量,不能直接参与运算,而只能以其ASCII码值(0x35)来参与运算。第5章嵌入式C语言程序设计基础5.2嵌入式C语言的基本数据类型3.符号常量(1)不带参数的宏定义宏定义命令#define的一般形式是:#define宏名字符串用来终止宏名作用域命令#undef的一般形式是:#undef宏名例5.8:#definePI3.14159/*定义PI为常量,其值是3.14159*/main(){…
}#undefPI/*终止宏名PI的作用域*/f1()第5章嵌入式C语言程序设计基础5.2嵌入式C语言的基本数据类型(2)带参数的宏定义它不是进行简单的字符串替换,还要进行参数替换。其定义的一般形式为:#define宏名(参数表)字符串其中字符串中包括参数表中所指定的参数。在使用时,要将程序中宏名后的实际参数代入字符串中参数的位置。例如:#defineS(a,b)a*b
…
area=S(3,2);经编译预处理,该语句被展开成
area=3*2;
第5章嵌入式C语言程序设计基础5.2嵌入式C语言的基本数据类型
说明:
(1)宏名和参数表左括号之间不能有空格,否则按不带参宏替换了。
(2)字符串中应注意括号的使用,以保证运算次序。如上例改成
area=S(1+2,2); 经展开后变成
area=1+2*2; 这就不合要求了。此时,可改写成 #defineS(a,b)(a)*(b)
…
area=S(1+2,2)
经展开后变成
area=(1+2)*(2); 就不会出现错误了。
第5章嵌入式C语言程序设计基础5.2嵌入式C语言的基本数据类型5.2.3变量1.变量的定义变量定义的一般形式如下:[存储类型]类型说明符[修饰符]标识符[=初值][,标识符[=初值]]…
;变量的定义由5部分组成,方括号中的可有可无,变量定义的具体情况而定。第5章嵌入式C语言程序设计基础5.2嵌入式C语言的基本数据类型(1)类型说明符●对于数字与字符,其常用的类型主要有8种:char、unsignedchar、int、unsigned、long、unsignedlong、float、double。●void类型(抽象型),在具体化时可用类型强制来指定类型说明符中的任意一类。●通过typedef定义的类型别名。为了增加程序的可读性和移植程序时的方便,C语言允许用户为C语言固有的类型用typedef起别名。格式如下:typedefC固有的简单类型或复合类型别名标识符;用别名代替原来的类型,在说明中用作类型说明符。别名一般用大写字符,例如:typedeflongBIGBIGx=80000;第5章嵌入式C语言程序设计基础5.2嵌入式C语言的基本数据类型(2)标识符●变量名可以是C语言中允许的合法标识符。●每一个变量都必须进行类型说明,也就是变量要先定义,后使用。●当一个变量被指定为某一确定类型时,将为它分配若干相应字节的内存空间。如在32位体系的ARM系统中,char型为1字节,int型为4字节,float为4字节,double为8字节。当然,不同的体系结构的系统可能稍有差异。●变量可以在程序内的三个地方定义:在函数内部,在函数的参数(形参)定义中或在所有的函数外部。由此定义的变量分别称为局部变量,形式参数和全局变量。在不同地方定义的变量,其作用域范围不同。在同一层次定义的变量不能与数组、指针、函数和其它变量同名。●变量是用来存放数据的,由于数据有不同的类型,因此要定义相应类型的变量去存放它。这些数据称为相应变量的值。
第5章嵌入式C语言程序设计基础5.2嵌入式C语言的基本数据类型(3)存储类型存储类型指定被说明对象所在内存区域的属性。存储空间分为代码区与数据区两个部分。变量存储在数据区,数据区又可分为静态存储区与动态存储区。静态存储是指在程序运行期间给变量分配固定存储空间的方式。如全局变量存放在静态存储区中,程序运行时分配空间,程序运行完释放。动态存储是指在程序运行时根据实际需要动态分配存储空间的方式。如形式参数存放在动态存储区中,在函数调用时分配空间,调用完成释放。对于静态存储方式的变量可在编译时初始化,默认初值为0或空字符。对动态存储方式的变量如不赋初值,则它的值是一个不确定的值。在C语言中,具体的存储类别有自动(auto)、寄存器(register)、静态(static)及外部(extern)四种。静态存储类别与外部存储类别变量存放在静态存储区,自动存储类别变量存放在动态存储区,寄存器存储类别直接送寄存器。第5章嵌入式C语言程序设计基础5.2嵌入式C语言的基本数据类型●局部变量的存储方式局部变量一般用自动方式存储,用保留字auto加以定义,此时称为自动变量,是动态存储,在函数的调用过程中存在,由编译系统自动处理。例如:voidf(){autointi,j;autofloatx,y;/*局部变量i,j,x,y以自动方式存储*/}C语言规定,自动变量可省去说明符auto。如果希望函数调用完后局部变量的值被保留,不释放其所占存储单元,这时必须将其存储方式定义为静态存储方式,用保留字static加以定义。第5章嵌入式C语言程序设计基础5.2嵌入式C语言的基本数据类型●全局变量的存储方式全局变量一般用外部存储方式存储,用保留字extern加以定义。变量的作用域是构成整个程序的所有程序文件,也就是定义的外部变量可供其它程序文件使用。第5章嵌入式C语言程序设计基础5.2嵌入式C语言的基本数据类型例如5.9程序由两个程序文件与组成。/**/externinta;/*定义extern存储方式变量a*/main(){intpow();intn;intp;scanf(″%d″,&n);p=pow(n);printf(″p=%d\n″,p);}第5章嵌入式C语言程序设计基础5.2嵌入式C语言的基本数据类型/**/externinta;/*申明本文件中使用的是已定义的外部变量a*/intpow(x)intx;{inti,t=1;for(i=1;i<=x;i++)t*=i;return(t);}
第5章嵌入式C语言程序设计基础5.2嵌入式C语言的基本数据类型(4)赋初值部分●若初值省略,则存储类型是自动(auto)●寄存器(register)的变量为随机值●存储类型是static的变量被编译器自动清0●对于指针,无论什么存储类型,一律置为空指针(Null)。使用等号为变量赋初值,注意的是:指针必须用地址量作为初值,字符可用其编码值(例如ASCII码)或用单引号括起的字符作为初值。对于静态变量,只在定义说明时赋初值一次。第5章嵌入式C语言程序设计基础5.2嵌入式C语言的基本数据类型(5)修饰符用于对变量进行特殊的修饰。修饰符包括以下3类:●const——常量修饰符,被修饰的变量或变量指针是常量。●volatile——易失性修饰符,说明所定义的变量或指针,是可以被多种原因修改的。如有的变量在中断服务程序中会被修改,有的会被I/0口修改,这种修改带有随机性,防止丢失任何一次这种修改,因此要把它修饰为易失性的变量,注意,禁止把它作为寄存器变量处理,也禁止对它进行任何形式的优化。●near、far——近、远修饰符,用于说明访问内存中变量在位置上的远近。特别注意:●格式中的方括号是可选项,可有可无。●一个说明语句可以同时说明多个同类型的变量,变量之间用逗号隔开,变量可以在说明时赋初值;●说明语句一定要有终止符“;”结束。第5章嵌入式C语言程序设计基础5.2嵌入式C语言的基本数据类型2.C语言中各种类型的变量
C语言中有以下几种类型的变量:整型变量、实型变量、字符型变量和构造类型变量等。●整型变量用来存放整型数值。整型变量可分为:基本型(int),短整型(shortint或short),长整型(longint或long)和无符号型(unsignedint,unsignedshort,unsignedlong)。●实型变量分为单精度型(float)和双精度型(double)两类。●字符型(char)变量内存放字符型常量,在内存单元中仅占一个字节。其内存中存放的是该字符的ASCII码。●枚举型变量枚举型是一个整型常量的集合。这些常量指定了所有该类型变量可能具有的各种合法值。枚举在我们日常生活中十分常见。例如,星期的枚举为:{星期日,星期一,星期二,星期三,星期四,星期五,星期六}。第5章嵌入式C语言程序设计基础5.2嵌入式C语言的基本数据类型2.C语言中各种类型的变量
C语言中有以下几种类型的变量:整型变量、实型变量、字符型变量和构造类型变量等。●整型变量用来存放整型数值。整型变量可分为:基本型(int),短整型(shortint或short),长整型(longint或long)和无符号型(unsignedint,unsignedshort,unsignedlong)。●实型变量分为单精度型(float)和双精度型(double)两类。●字符型(char)变量内存放字符型常量,在内存单元中仅占一个字节。其内存中存放的是该字符的ASCII码。●枚举型变量枚举型是一个整型常量的集合。这些常量指定了所有该类型变量可能具有的各种合法值。枚举在我们日常生活中十分常见。例如,星期的枚举为:{星期日,星期一,星期二,星期三,星期四,星期五,星期六}。第5章嵌入式C语言程序设计基础5.2嵌入式C语言的基本数据类型5.2.4运算符第5章嵌入式C语言程序设计基础5.2嵌入式C语言的基本数据类型5.2.4运算符说明:(1)+、-、*、%与数学中运算类似,先乘除后加减,也就是按优先级顺序进行运算,优先级小的先运算。(2)求余运算符(%)仅用于整型数据,不能用于实型和双精度实型。它的作用是取整数除法的余数。如1%2的结果是1;10%3的结果也是1。(3)赋值运算符(=)是将右边表达式的值赋给左边的变量。赋值运算符左边必须是变量等有存贮单元的元素,而不能是常量或表达式。(4)++、--仅用于整型变量,指针变量。用于整型变量在原值上加1或减1;用于指针变量是取下一地址或上一地址。(5)+、-、*、/、%可以与赋值号=组成复合赋值运算符+=、-=、*=、/=、%=。第5章嵌入式C语言程序设计基础5.2嵌入式C语言的基本数据类型2.关系运算符和逻辑运算符
第5章嵌入式C语言程序设计基础5.2嵌入式C语言的基本数据类型2.关系运算符和逻辑运算符
说明:(1)当关系运算符两边的值满足关系时为真,返回1;如不满足关系时为假,返回0。(2)关系运算符>、>=、<、<=的优先级相同,如在表达式中同时出现时,则自左向右顺序运算。而“==”与“!=”的优先级低于>、>=、<、<=这四种关系运算符。例如:printf(″%d\n″,5>3>1);运行输出结果为0。因为两个>是同一优先级,5>3的结果为1,而1>1的关系不满足,所以最后结果为0。又如:printf(″%d\n″,1==11<35);运行输出结果为1。因为<的优先级比==高,则11<35的结果为1,而1==1的关系满足,所以最后结果为1。第5章嵌入式C语言程序设计基础5.2嵌入式C语言的基本数据类型3.位运算符
第5章嵌入式C语言程序设计基础5.3程序的控制结构
5.3程序的控制结构程序控制语句用于控制程序的流程,以实现程序的各种结构方式。包括:条件判断语句:if,switch循环执行语句:dowhile,while,for转向语句:break,goto,continue,return第5章嵌入式C语言程序设计基础5.3程序的控制结构
5.3.1选择结构用if语句可以实现简单选择结构。其语法形式为
if(表达式) 语句1 else 语句2
执行顺序是:首先计算表达式的值,若表达式值为true,则执行语句1;否则执行语句2。
if语句中的语句2可以为空,当语句2为空时,else可以省略,成为如下形式:
if(表达式) 语句1
其中,语句1和语句2不仅可以是一条语句,而且可以是大花括号括起来的多条语句,即复合语句。第5章嵌入式C语言程序设计基础5.3程序的控制结构
5.3.2switch语句switch语句的语法形式如下:
switch(表达式) { case 常量表达式1:语句1 case 常量表达式2:语句2 case 常量表达式n:语句n default: 语句 n+1 }第5章嵌入式C语言程序设计基础5.3程序的控制结构
5.3.3循环结构嵌入式LunuxC语言中有三种循环控制语句,即while,dowhile,for循环语句。while和dowhile是两种不同的循环结构,即当型循环和直型循环,其格式如下:1.while语句(当型循环)while语句的语法形式为:
while(表达式) {
循环体语句
}第5章嵌入式C语言程序设计基础5.3程序的控制结构
5.3.3循环结构
2.do-while语句(直型循环)语法形式:
do {
循环体语句
}while(表达式);
执行顺序是:当流程执行到do后,立即执行循环体语句,然后再判断循环条件表达式的值。当表达式为true时,继续执行循环体;否则结束循环,该语句结构使循环至少执行一次。第5章嵌入式C语言程序设计基础5.3程序的控制结构
3.for语句
for语句的使用最为灵活,既可以用于循环次数确定的情况,也可以用于循环次数未知的情况。for语句的语法形式如下:
for(表达式1;表达式2;表达式3)for语句的执行流程为:(1)首先计算表达式1的值。(2)再计算表达式2,如果表达式2的值为false,则退出循环。(3)如果表达式2的值为true,则执行一次循环体,然后计算表达式3的值。(4)转回(2),表达式2的值决定是否继续执行循环体。第5章嵌入式C语言程序设计基础5.3程序的控制结构
5.3.4其他控制语句1.break语句
break语句只用于switch语句或循环体中,作用是使程序从switch语句内跳出或结束本次循环,转去执行后面的语句。由于break语句的转移方向是明确的,所以不需要语句标号与之配合。2.continue语句
continue语句仅用于循环体中,其作用是结束本次循环,接着开始判断循环条件,决定是否继续执行下一次循环。3.goto语句
goto语句的语法格式为
goto<语句标号> goto语句的作用是跳转到语句标号处执行程序。第5章嵌入式C语言程序设计基础5.4函数
5.4函数1.函数定义一个完整的函数定义由两部分组成,即函数头与函数体。函数定义的一般语法形式为:<类型标识符><函数名说明符>(形式参数表){
说明性语句序列; 实现函数功能的语句系列;}
其中,类型标识符规定了函数的返回值类型。函数的返回值是返回给主调函数的处理结果,由函数体部分的return语句带回。无返回值的函数其类型标识符为void,不必有return语句。形式参数表(简称形参表)的内容如下:类型l形参名1,类型2形参名2,…,类型n形参名n第5章嵌入式C语言程序设计基础5.4函数2.调用函数调用函数必须遵守先定义后调用的原则,否则,需要在调用函数之前在主调函数中声明函数原型。函数原型声明形式:(1)<类型标识符><被调函数名>(参数类型1,参数类型2…);(2)<类型标识符><被调函数名>(参数类型1参数名1,参数类型2参数名2…);
函数的调用形式:
<函数名>(实参1,实参2,…,实参n)第5章嵌入式C语言程序设计基础5.4函数
例如在<arch\arm\mm\mm-armv.c>中出现的create_mapping函数定义(省略了括号内“{}”的语句)。
staticvoid__initcreate_mapping(structmap_desc*md){…}定义这个函数的各项含义如下:
(1)类型说明符staticvoid__init,其中的static说明符用于指明这个静态的函数只可被这一文件内的其他函数调用。
void说明符指明该函数是无返值类型。而__init是宏定义的说明符。(2)函数名为create_mapping。(3)这个函数只有一个形式参数md,其形参类说明符是structmap_desc*,是一个指向结构体map_desc的指针。第5章嵌入式C语言程序设计基础5.4函数3.内联函数内联函数与一般函数不同的是,它不是在调用时发生转移,而是在编译时将函数体嵌入在每一个调用语句处。这样就相对节省了参数传递、系统栈的保护与恢复等的开销。
内联函数的定义形式为:<inline><类型标识符><被调函数名>(含类型说明的形参表){
函数体}第5章嵌入式C语言程序设计基础5.4函数例5.12内联函数例题。
#include<iostream.h>#include<iomanip.h>inlineintmax(inta,intb){if(a>b)returna;elsereturnb;}voidmain() { inta,b,c,d; a=210; b=150; c=20; d=max(a,b); d=max(d,c);//编译时两个调用处均被替换为max函数体语句printf(″Thebiggestof%d%d%dis%d″,a,b,c,d)
}
程序运行结果为
Thebiggestof21015020is210第5章嵌入式C语言程序设计基础5.4函数4.系统函数的使用系统函数的原型声明由系统提供,并且已分类存于不同的头文件中。用include指令嵌入相应头文件,然后可使用系统函数。include指令格式如下。#include<标准头文件名.h>
其中:头文件名可带或不带路经,尖括号内为标准头文件,由开发环境或系统提供。若头文件为用户自定义头文件,则头文件在双引号内。例5.13标准头文件定义#include<string.h>#include<sys/types.h>string.h和types.h是标准头文件,按环境变量include指定的目录顺序搜索string.h和types.h。第5章嵌入式C语言程序设计基础5.4函数4.系统函数的使用系统函数的原型声明由系统提供,并且已分类存于不同的头文件中。用include指令嵌入相应头文件,然后可使用系统函数。include指令格式如下。#include<标准头文件名.h>
其中:头文件名可带或不带路经,尖括号内为标准头文件,由开发环境或系统提供。若头文件为用户自定义头文件,则头文件在双引号内。例5.13标准头文件定义#include<string.h>#include<sys/types.h>string.h和types.h是标准头文件,按环境变量include指定的目录顺序搜索string.h和types.h。第5章嵌入式C语言程序设计基础5.5数组
1.一维数组(1)数组的定义数组在使用前必须先声明。声明一个一维数组的形式如下:
<类型标识符><数组名>[数组长度]
注意:数组长度是个常量表达式。数组中的每个元素可以当成普通的变量使用。访问一维数组元素的形式如下:
<数组名>[下标]
注意:下标的值也是从0开始,不能超过该维的长度减1。第5章嵌入式C语言程序设计基础5.5数组
(2)数组的初始化数组的初始化有以下几种方式。形式1:定义时整体初始化与变量在定义时初始化一样,数组也可以在定义时进行初始化,格式如下:
<类型标识符><数组名>[数组长度]={第0个元素值,第1个元素值,…,第n-1个元素值};例如对字这符数组进行初始化:
chara[10]={`a`,`b`,`c`,`d`,`e`,`f`,`g`,`h`,`j`,`k`};第5章嵌入式C语言程序设计基础5.5数组
形式2:定义时部分初始化数组在定义时可对其中的部分数据进行初始化。当“{}”中值的个数少于元素个数时,只给前面部分元素赋值。例如如下定义就是对数组的前5个数据初始化,而后5个数据自动赋0(在字符数组中自动赋`\0`)。
chara[10]={`a`,`b`,`c`,`d`,`e`};第5章嵌入式C语言程序设计基础5.5数组
形式3:数组全部赋值若想要对数组中元素全部赋值,则可以省略数组下标中的常数,在此时,编译器会自动定义数组元素的个数,其格式如下:
<类型标识符><数组名>[]={第0个元素值,第1个元素值,…,第n个元素值};例如:
chara[]={`a`,`b`,`c`,`d`,`e`,`f`,`g`,`h`,`j`,`k`};第5章嵌入式C语言程序设计基础5.5数组
2.多维数组
(1)多维数组的定义多维数组的声明形式如下:
<类型标识符><数组名>[长度1][长度2]…[长度n]
(2)多维数组的引用多维数组中元素的引用和一维数组的引用很相似,可通过下标引用的方式进行的。其表示形式为:
<数组名>[第1维下标][第2维下标]…[第n维下标]第5章嵌入式C语言程序设计基础5.5数组
(2)多维数组的初始化以下是二维数组的初始化形式:
形式1:分段赋值
<类型标识符><数组名>[第1维长度][第2维长度]={{第0个第2维数据组}, {第1个第2维数据组},…,{第n-1个第2维数据组}}
其中,n等于第1维长度。
形式2:按行连续赋值
<类型标识符><数组名>[第1维长度][第2维长度]={{第0个元素值,第1个元值,…,第m个元素值}}其中,m小于或等于第1维长度×第2维长度。第5章嵌入式C语言程序设计基础5.5数组
3.数组作为函数的参数将整个数组作为参数传递给函数,可通过传递不带方括号的数组名来进行,其形式大体上有以下两种:形式1:
<类型标识符><函数名>(类型标识符数组名[],int长度)形式2:<类型标识符><函数名>(类型标识符数组名[长度])第5章嵌入式C语言程序设计基础5.5数组
4.数组与字符串(1)字符数组的初始化有两种特殊的初始化方法,形式如下:
形式1:
char<数组名>[]="字符串"
形式2:
char<数组名>[]={"字符串"}
两种形式效果相同,且在末尾加个“\0”,例如:
charpMyStrinq[]="Thisisacomputer";
它等同于:
charpMyString[]={'T','h','i','s','','a','','c','o','m','p','u','t','e','r','\0'};或
charpMyStrinq[]={"Thisisacomputer"};第5章嵌入式C语言程序设计基础5.5数组
(2)字符串的基本运算
求字符串的长度假设字符串保存在数组pString中,求字符串长度的程序代码如下:
charpString[]="abcd"; intnSize=0; while(pString[nSize]!='\0') nSize++;
执行完毕后,整型变量nSize中保存的值就是字符串pString的长度。第5章嵌入式C语言程序设计基础5.5数组
字符串的复制假设源字符串保存在数组pSource中,目的字符型数组为pDestination,则字符串复制的程序段如下:
charpSource[]="abcd"; charpDestination[]=[5];//源字符串长度+1 intnIndex=0; while(pSource[nSize]!='\0 ') { pDestination[nIndex]=pSource[nIndex]; nIndex++; } pDestination[nIdex]='\0';//标识字符串结束第5章嵌入式C语言程序设计基础5.5数组
字符串的连接假设源字符串保存在字符数组pDest中,要连接进来的字符串保存在字符数组pTocat中,则程序代码为:
charpTocat[]="efg";charpDest[8]="abcd";intnSize=0;intnIndex=0;while(pDest[nSize]!=0)//找到被要连接的字符串的尾部
nSize++;do { pDest[nSize++]=pTocat[nIndex]; nIndex++; }while(pTocat[nIndex]!='\0'); pDest[nSize]='\0';第5章嵌入式C语言程序设计基础(3)字符串处理函数要引入#include<string.h>语句第5章嵌入式C语言程序设计基础(3)字符串处理函数要引入#include<string.h>语句第5章嵌入式C语言程序设计基础5.5数组
字符串的连接假设源字符串保存在字符数组pDest中,要连接进来的字符串保存在字符数组pTocat中,则程序代码为:
charpTocat[]="efg";charpDest[8]="abcd";intnSize=0;intnIndex=0;while(pDest[nSize]!=0)//找到被要连接的字符串的尾部
nSize++;do { pDest[nSize++]=pTocat[nIndex]; nIndex++; }while(pTocat[nIndex]!='\0'); pDest[nSize]='\0';第5章嵌入式C语言程序设计基础5.8汇编语言与C/C++的混合编程汇编语言与C/C++的混合编程在C/C++代码中嵌入汇编指令。在汇编程序和C/C++的程序之间进行变量的互访。汇编程序、C/C++程序间的相互调用。第5章嵌入式C语言程序设计基础5.8汇编语言与C/C++的混合编程5.8.1内嵌汇编指令是嵌入在C/C++程序中使用,但不能直接引用C语言的变量定义,数据交换必须通过ATPCS进行。1.内嵌汇编指令的语法格式__asm__(“instruction...instruction”);//Linuxgcc中支持__asm{ instruction…instruction};//ADS中支持
特别注意:是“__asm”是两个下划线。第5章嵌入式C语言程序设计基础5.8.1内嵌汇编指令2.内嵌汇编指令的特点(1)操作数●内嵌的汇编指令中作为操作数的寄存器和常量可以是C/C++表达式。是char、short、int类型,而且这些表达式都是作为无符号数进行操作。●编译器将会计算这些表达式的值,并为其分配寄存器。第5章嵌入式C语言程序设计基础5.8.1内嵌汇编指令(2)物理寄存器
内嵌汇编指令中使用物理寄存器的限制:
不能直接向PC寄存器中赋值,程序的跳转只能通过B指令和BL指令实现。
在内嵌汇编指令中,不要使用过于复杂的C/C++表达式。
编译器可能会使用R12寄存器或R13寄存器存放编译的中间结果,在计算表达式值时可能会将寄存器R0到R3、R12以及R14用于子程序调用。
指令中不要使用指定的物理寄存器。第5章嵌入式C语言程序设计基础5.8.1内嵌汇编指令(3)常量在内嵌的汇编指令中,常量前的符号#可省略。若表达式前使用了符号#,则必须是一个常量。(4)指令展开内嵌的汇编指令中如果包含常量操作数,该指令可能会被汇编器展开成几条指令。例如指令:
ADDR0,R0,#1023
可能会被展开成下面的指令序列:
ADDR0,R0,#1024SUBR0,R0,#01
MUL指令会被展开成一系列加法和移位操作。第5章嵌入式C语言程序设计基础5.8.1内嵌汇编指令(5)标号C/C++程序中的标号可被内嵌的汇编指令使用。但只有B指令可使用C/C++程序中的标号,指令BL不能使用C/C++程序中的标号。指令B使用C/C++程序中的标号格式:B{cond}label(6)内存单元的分配所用的内存单元的分配都是通过C/C++程序完成的,分配的内存单元通过变量供内嵌的汇编器使用。第5章嵌入式C语言程序设计基础5.8.1内嵌汇编指令(7)SWI和BL指令的使用内嵌SWI和BL指令中3个可选寄存器列表
第1个寄存器列表中的寄存器用于存放输入的参数。
第2个寄存器列表中的寄存器用于存放返回的参数。
第3个寄存器列表中的寄存器的内容可能被被调用的子程序破坏,即这些寄存器是供被调用的子程序作为工作寄存器。第5章嵌入式C语言程序设计基础5.8.1内嵌汇编指令3.内嵌的汇编器与armasm的区别
在功能和使用方法上主要有以下特点:
不能写PC(movpc,lr)
不支持伪指令LDRRn,=expression,但可用指令:LDRRn,expression来代替。
除nop外,不支持ADR、ADRL等伪指令。
指令中的C变量不要与任何物理寄存器重名
LDM/STM指令中的寄存器列表只能使用物理寄存器,不能使用C表达式
不支持指令BX/BLX
用户不用维护数据栈
不要轻易改变处理器模式。
不支持内存分配操作第5章嵌入式C语言程序设计基础5.8.1内嵌汇编指令4.内嵌汇编指令的应用举例下面是在ADS1.2下编译通过程序,程序中内嵌汇编指令。例5.14字符串复制本例中使用了指令BL调用子程序。在内嵌的BL指令中,除了正常的操作数外,还必须增加以下三个可选的寄存器列表:
第1个用于存放输入的参数
第2个用于存放返回的结果
第3个作为子程序的工作寄存器程序代码如下:#include<stdio.h>voidmy_strcpy(char*src,constchar*dst){intch;__asm{loop:LDRBch,[src],#1STRBch,[dst],#1CMPch,#0BNEloop};}intmain(void){constchar*a="HelloWorld!";
charb[20];__asm{ MOVR0,a MOVR1,b BLmy_strcpy,{R0,R1}};printf("OriginalString:%s\n",a);printf(“CopiedString:%s\n",b);return0;}
例中主函数main(void)中的BLmy_strcpy,{R0,R1}指令的输入寄存器列表为{R0,R1};它没有输出寄存器列表;被子程序使用的工作寄存器为ATPCS的默认工作寄存器R0~R3、R12、lr以及PSR。第5章嵌入式C语言程序设计基础5.8.1内嵌汇编指令例5.15使能和禁止中断本例主要介绍怎样利用内嵌的汇编程序实现使能和禁止中断。通过修改CPSR寄存器中的bit7可使能和禁止中断。但这些操作必须在特权模式下进行,因为在用户模式下不能修改寄存器CPSR中的控制位。源程序代码如下。程序代码如下:__inlinevoidenable_IRQ(void){inttmp;__asm{MRStmp,CPSRBICtmp,tmp,#0x80MSRCPSR_c,tmp}}__inlinevoiddisable_IRQ(void){inttmp;__asm{MRStmp,CPSRORRtmp,tmp,0x80MSRCPSR_c,tmp
}}intmain(void){disable_IRQ()
;enable_IRQ()
;}第5章嵌入式C语言程序设计基础5.8.2从汇编程序中访问C程序变量
在C程序中声明的全局变量可被汇编程序通过地址间接访问。具体访问方法如下:
使用IMPORT伪操作声明该全局变量。
使用LDR伪指令读取该全局变量的内存地址,通常该全局变量的内存地址值存放在程序的数据缓冲池中(literalpool)。
根据该数据的类型,使用相应的LDR指令读取该全局变量的值;使用相应的STR指令修改该全局变量的值。第5章嵌入式C语言程序设计基础5.8.2从汇编程序中访问C程序变量各数据类型及对应的LDR/STR指令如下:对于无符号的char类型变量通过指令LDRB/STRB来读/写。对于无符号的short类型变量通过指令LDRH/STRH来读/写。对于int类型的变量通过指令LDR/STR来读/写对于有符号的char类型的变量通过指令LDRSB来读取。对于有符号的char类型的变量通过指令STRB来写入。对于有符号的short类型的变量通过指令LDRSH来读取。对于有符号的short类型的变量通过指令STRH来写入。对于小于8个字的结构型变量,可以通过一条LDM/STM指令来读/写整个变量。对于结构型变量的数据成员,可以使用相应的LDR/STR指令来访问,这时必须知道该数据成员相对于结构型变量开始地址的偏移量。第5章嵌入式C语言程序设计基础5.8.2从汇编程序中访问C程序变量例5.16从汇编程序中访问C程序全局变量在汇编程序中访问C程序全局变量。程序中变量globvl是在C程序中声明的全局变量。在汇编程序中首先用IMPORT伪操作声明该变量;将其内存地址读入到寄存器R1中;再将其内存单元中的值读入到寄存器R0中;修改后再将寄存器R0的值赋于变量globvl。第5章嵌入式C语言程序设计基础5.8.2从汇编程序中访问C程序变量
例5.16从汇编程序中访问C程序全局变量源程序代码如下:AREAglobals,CODE,READONLY EXPORTasmsub IMPORTglobvlasmsub LDRr1,=globvl LDRr0,[r1] ADDr0,r0,#2
STRr0,[r1] MOVpc,lr END第5章嵌入式C语言程序设计基础5.8.3编程序与C/C++程序的相互调用规则—ARPCSATPCS(ARM-ThumbProcedureCallStandard)规则。1.寄存器的使用规则
寄存器的使用必须满足下面规则:
子程序间通过寄存器R0~R3来传递参数,记为A1~A4。被调用的子程序在返回前无须恢复寄存器R0~R3的内容。
子程序中使用寄存器R4~R11来保存局部变量。记为V1~V8。如果在子程序中使用到了寄存器V1~V8中的某些寄存器,则子程度进入时必须保存这些寄存器的值,在返回前必须恢复这些寄存器的值;对于子程序中没有用到的寄存器,则不必进行这些操作。在Thumb程序中,通常只能使用寄存器R4~R7来保存局部变量。第5章嵌入式C语言程序设计基础5.8.3汇编程序与C/C++程序的相互调用规则——ARPCS
寄存器R12用作子程序间的scratch寄存器,常用于子程序间的连接代码段中,记作IP。
寄存器R13用作数据栈指针,记作SP。在子程序中寄存器R13不能用作其他用途。寄存器SP在进入子程序时的值和退出子程序时的值必须相等。
寄存器R14称为链接寄存器,记作LR。它用于保存子程序的返回地址。如果在子程序中保存了返回地址,则寄存器R14可作其他用途。
R15是程序计数器,记作PC。不能作其他用途。第5章嵌入式C语言程序设计基础5.8.3汇编程序与C/C++程序的相互调用规则—ARPCS第5章嵌入式C语言程序设计基础5.8.3汇编程序与C/C++程序的相互调用规则——ARPCS2.数据栈的使用规则有下面4种数据栈:
FD(FullDescending)满递减;
ED(EmptyDescending)空递减;
FA(FullAscending)满递增;
EA(EmptyAscending)空递增。第5章嵌入式C语言程序设计基础5.8.3汇编程序与C/C++程序的相互调用规则—ARPCS
ATPCS规定数据栈为FD(满递减)类型,并且对数据栈的操作是8字节对齐的。异常中断的处理程序可使用中断程序的数据栈。
数据栈指针(StackPoint):最后一个写入栈的数据的内存地址。
数据栈的基地址(StackBase):数据栈的最高地址。ATPSC中的数据栈是FD型,最早入栈的数据所占的内存单元是基地址的下一个内存单元。
数据栈界限(StackLimit):数据栈中可使用的最低的内存单元地址。
数据栈中的数据帧(StackFrames):数据栈是为子程序分配用来保存寄存器和局部变量的区域。第5章嵌入式C语言程序设计基础5.8.3汇编程序与C/C++程序的相互调用规则——ARPCS3.参数的传递规则参数传递规则具体如下:(1)参数个数固定的子程序参数传递规则对于参数个数固定的子程序,如果系统包含浮点运算的硬件部件,浮点参数将按照下面规则传递:
各个浮点参数按顺序处理。
为每个浮点参数分配FP寄存器。方法是:满足该浮点参数需要的且编号最小的一组连续的FP寄存器。第一个整数参数,通过寄存器R0~R3来传递。其他参数通过数据栈传递。第5章嵌入式C语言程序设计基础5.8.3汇编程序与C/C++程序的相互调用规则——ARPCS(2)参数个数可变的子程序参数传递规则
●当参数不超过4个,用R0~R3传递参数,当参数超过4个时,使用数据栈来传递参数。●在参数传递时,所有参数看作是存放在连续的内存字单元中的字数据。然后,依次将各字数据传递到寄存器R0~R3中,如果参数多于4个,将剩余的字数据传送到数据栈中,入栈的顺序与参数顺序相反,即最后一个字数据先入栈。第5章嵌入式C语言程序设计基础5.8.3汇编程序与C/C++程序的相互调用规则——ARPCS(3)子程序结果返回规则子程序返回结果规则如下:
结果为一个32位整数,可通过寄存器R0返回;
结果为一个64位整数,可通过寄存器R0,R1返回,依次类推;
结果为一个浮点数时,可通过运算部件的寄存器F0、D0或者S0来返回;
结果为复合型的浮点数(如复数)时,可通过寄存器F0~Fn或者D0~Dn来返回。
对于位数更多的结果,需通过内存来传递,如通过数据栈来传递。第5章嵌入式C语言程序设计基础5.8.4汇编程序与C/C++程序的相互调用1.C语言程序调用汇编语言程序
汇编语言程序的设计要遵守ATPCS。在汇编语言中需用EXPORT伪操作来说明,使得本程序可被其他程序调用。
C语言程序调用该汇编语言程序之前,需要在C语言程序中使用extern关键词来声明该汇编语言程序。
第5章嵌入式C语言程序设计基础5.8.4汇编程序与C/C++程序的相互调用#include<stdio.h>externvoidmy_strcpy(char*d,constchar*s);intmain(){constchar*srcstr="Firststring-source";chardststr[]="Secondstring-destination";printf("Beforecopying:\n");printf("%s\n%s\n",srcstr,dststr);my_strcpy(dststr,srcstr);printf("Aftercopying:\n");printf("%s\n%s\n",srcstr,dststr);return0;}例5.17C语言程序调用汇编语言程序完成字符串复制
C语言源程序如下:AREASCopy,CODE,READONLYEXPORTmy_strcpymy_strcpyLDRBr2,[r1],#1STRBr2,[r0],#1CMPr2,#0BNEmy_strcpyMOVpc,lrEND
根据ATPCS规则,函数的前4个参数在R0~R3中。在本例汇编程序中,寄存器R0中存放第1个参数,即dststr;寄存器R1中存放第2个参数,即srcstr。C语言代码源程序可保存为strtest.c,汇编语言程序是scopy.s。第5章嵌入式C语言程序设计基础5.8.4汇编程序与C/C++程序的相互调用2.汇编语言中调用C语言中定义的函数
汇编程序的设计要遵循ATPCS,保证程序调用时参数的正确传递。在汇编程序中使用IMPORT伪操作声明将要调用C程序。下面是一个汇编程序调用C程序的例子。其中在汇编程序中设置好各参数的值。本例中有5个参数,分别使用寄存器R0存放第一个参数,R1存放第2个参数,R2存放第3个参数,R3存放第4个参数,第5个参数利用数据栈传送。由于利用数据栈传递参数,在程序调用结束后要调整数据栈指针。例5.18汇编语言程序调用C语言程序//C程序g()返回5个整数的和intg(inta,intb,intc,intd,inte){returna+b+c+d+e;};汇编程序调用C程序g()计算5个整数i,2*i,3*i,4*i,5*i的和EXPORTfAREAf,CODE,READONLYIMPORTg
;使用伪操作数IMPORT声明C程序g()STRlr,[sp,#-4]!;保存返回地址ADDr1,r0,r0;假设进入程序f时,r0中的值为i,r1值设为2*iADDr2,r1,r0;r2的值设为3*iADDr3,r1,r2;r3的值设为5*iSTRr3,[s
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
评论
0/150
提交评论