




已阅读5页,还剩112页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
.,第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.1嵌入式C语言的预处理伪指令,1文件包含伪指令文件包含伪指令可将头文件包含到程序中,头文件中定义的内容符号常量,复合变量原型、用户定义的变量原型和函数的原型说明等。编译器编译预处理时用文件包含的正文件内容替换到实际程序中。(1)文件包含伪指令的格式#include;标准头文件#include“头文件名.h”;自定义头文件#include宏标识符,.,5.1嵌入式C语言的预处理伪指令,(2)包含文件伪指令的说明常在头文件名后用.h作为扩展名,可带或不带路经。头文件可分为标准头文件和自定义头文件。尖括号内的头文件为标准头文件,由开发环境或系统提供。双引号内的头文件为用户自定义头文件。搜索时,首先在当前目录中搜索,其次按环境变量include指定的目录顺序搜索。搜索到头文件后,就将该伪指令直接用头文件内容替换。,.,5.1嵌入式C语言的预处理伪指令,例5.1标准头文件定义#include#includestring.h和stdio.h是标准头文件,按环境变量include指定的目录顺序搜索string.h和stdio.h。例5.2用户自定义头文件定义#include“s3c2410-adc.h”s3c2410-adc.h头文件是用户自定义有关三星s3c2410的ARM处理器的A/D转换器各寄存器。,.,5.1嵌入式C语言的预处理伪指令,2宏定义伪指令宏定义伪指令分为:简单宏、参数宏、条件宏、预定义宏及宏释放。(1)简单宏格式如下:#define宏标识符宏体宏体是由单词序列组成。宏体超长时,允许使用续行符“”进行续行,续行符和其后的换行符n都不会进入宏体。在定义宏时,应尽量避免使用C语言的关键字和预处理器的预定义宏,以免引起灾难性的后果。在源文件中,用预处理器伪指令定义过宏标识符之后,就可用宏标识编写程序。当源文件被预处理器处理时,每遇到该宏标识符,预处理器便将宏展为宏体。,.,5.1嵌入式C语言的预处理伪指令,(2)参数宏格式如下:#define宏标识符(形式参数表)宏体形式参数表为逗号分割的形式参数。宏体是由单词序列组成。宏体超长时,允许使用续行符“”进行续行,续行符和其后的换行符n都不会进入宏体。使用参数宏时,形式参数表应换为同样个数的实参数表,这一点类似于函数的调用。参数宏与函数的区别在于参数宏的形参数表中没有类型说明符。预处理器在处理参数宏时使用2遍宏展开。第1遍展开宏体,第2遍对展开后的宏体用实参数替换形式参数。,.,例5.3在Linux系统的/include/asm-arm/arch-s3c2410/S3C2410.h头文件中定义了各NandFlash控制寄存器,其源代码如下:#definebNAND_CTL(Nb)_REG(0 x4e000000+(Nb)#defineNFCONFbNAND_CTL(0 x00)#defineNFCMDbNAND_CTL(0 x04)#defineNFADDRbNAND_CTL(0 x08)#defineNFDATAbNAND_CTL(0 x0c)#defineNFSTATbNAND_CTL(0 x10)#defineNFECCbNAND_CTL(0 x14),5.1嵌入式C语言的预处理伪指令,.,5.1嵌入式C语言的预处理伪指令,例5.4在Linux下ARMS3C2410X芯片的A/D转换的驱动程序的头文件s3c2410-adc.h中定义了下面三个宏。#defineADC_WRITE(ch,prescale)(ch)16)/*定义ADC设备的主设备号*/typedefstructstructsemaphorelock;/*内核信号量,当多个用户程序同时访问一个ADC控制器时,用lock进行同步*/wait_queue_head_twait;/*等待队列*/intchannel;/*ADC通道号*/intprescale;/*预定标值*/ADC_DEV;,5.1嵌入式C语言的预处理伪指令,.,staticADC_DEVadcdev;#defineSTART_ADC_AIN(ch,prescale)doADCCON=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转换控制寄存器的第35位,选择通道号;/ADCCON|=ADC_START;ADCCON0为置1,准备采集数据,5.1嵌入式C语言的预处理伪指令,.,5.1嵌入式C语言的预处理伪指令,(3)条件宏定义格式如下:格式1:#ifdef宏标识符#undef宏标识符#define宏标识符宏体#else#define宏标识符宏体#endif,.,格式2:#ifndef宏标识符#define宏标识符宏体#else#undef宏标识符#define宏标识符宏体#endif其中:格式1是测试存在,格式2是测试不存在。else可有,也可没有。,5.1嵌入式C语言的预处理伪指令,.,5.1嵌入式C语言的预处理伪指令,(4)宏释放用于释放原先定义的宏标识符。经释放后的宏标识符可再次用于定义其他宏体。格式如下:#undef宏标识符例5.6#defineSIZE512buf=SIZE*blks/*宏扩展为buf=512*blks;*/undefSIZE#defineSIZE128buf=SIZE*blks/*宏扩展为buf=128*blks;*/,.,5.1嵌入式C语言的预处理伪指令,3条件编译伪指令格式如下:#if(条件表达式1)#elif(条件表达式2)#elif(条件表达式3)#elif(条件表达式n)#else#endif,.,5.1嵌入式C语言的预处理伪指令,例5.7#if_B0SIZE=B0SIZE_BYTEtypedefunsignedcharPB0SIZE;#elif_B0SIZE=B0SIZE_SHORTtypedefunsignedshortPB0SIZE;#elif_B0SIZE=B0SIZE_WORDtypedefunsignedlongPB0SIZE;#endif,.,5.2嵌入式C语言的基本数据类型,5.2.1数据类型与表达式,.,5.2嵌入式C语言的基本数据类型,(1)类型修饰符,.,5.2嵌入式C语言的基本数据类型,(2)访问修饰符C语言有两个用于控制访问和修改变量方式的修饰符,分别是常量(const)和易变量(volatile)。带const修饰符定义出的常量在程序运行过程中始终保持不变。例如:constintnum;例如:constintnum=100;volatile修饰符用于提醒编译程序,该变量的值可以不通过程序中明确定义的方法来改变。const和volatile可以同时使用。例如,假设0 x30是一个只随外部条件而变化的口地址值,那么就恰好需要用下述说明来避免偶然因素所产生的副作用的影响。constvolatileunsignedchar*port=0 x30;,.,5.2嵌入式C语言的基本数据类型,2.构造数据类型数组是一组连续、有序的存放在一起的具有相同类型的数据。结构体是将不同类型的数据按一定顺序存放在一起的数据结构。共用体是将不同类型的数据都存放在同一起始地址的内存单元中,共用一段内存以节省内存单元。枚举是只有几种可能的值,将其一一列举出来。实际是用符号来表示若干个可取的整型值,它是整型的一个子集。,.,5.2嵌入式C语言的基本数据类型,3.指针类型指针可以有效地表示复杂的数据结构;能动态分配内存;能方便地使用字符串;有效而方便地使用数组;在调用函数时能得到多于一个的值;能直接处理内存地址等。指针类型迥异于前述各种数据类型,不管是简单类型的数据,还是构造类型数据,均是代表数据的,而指针类型是代表地址的。,.,5.2嵌入式C语言的基本数据类型,5.2.2常量1.数值常量(1)整型常量整型常量也称为整型常数或整数。C整型常量按进制分可分为十进制整数,八进制整数和十六进制整数。(2)实型常量实型常量有单精度实型常量和双精度实型常量。可用小数形式或指数形式表示。(3)字符常量,.,5.2嵌入式C语言的基本数据类型,.,5.2嵌入式C语言的基本数据类型,2.字符串常量字符串常量简称字符串,是用一对双引号括起来的字符序列。例如“China”就是一个字符串常量。若数字被定义为字符型之后就不能参与数值运算,如5和5是不同的。5是字符常量,不能直接参与运算,而只能以其ASCII码值(0 x35)来参与运算。,.,5.2嵌入式C语言的基本数据类型,3.符号常量(1)不带参数的宏定义宏定义命令define的一般形式是:define宏名字符串用来终止宏名作用域命令undef的一般形式是:undef宏名例5.8:definePI3.14159/*定义PI为常量,其值是3.14159*/main()undefPI/*终止宏名PI的作用域*/f1(),.,5.2嵌入式C语言的基本数据类型,(2)带参数的宏定义它不是进行简单的字符串替换,还要进行参数替换。其定义的一般形式为:define宏名(参数表)字符串其中字符串中包括参数表中所指定的参数。在使用时,要将程序中宏名后的实际参数代入字符串中参数的位置。例如:defineS(a,b)a*barea=S(3,2);经编译预处理,该语句被展开成area=3*2;,.,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.2嵌入式C语言的基本数据类型,5.2.3变量1.变量的定义变量定义的一般形式如下:存储类型类型说明符修饰符标识符=初值,标识符=初值;变量的定义由5部分组成,方括号中的可有可无,变量定义的具体情况而定。,.,5.2嵌入式C语言的基本数据类型,(1)类型说明符对于数字与字符,其常用的类型主要有8种:char、unsignedchar、int、unsigned、long、unsignedlong、float、double。void类型(抽象型),在具体化时可用类型强制来指定类型说明符中的任意一类。通过typedef定义的类型别名。为了增加程序的可读性和移植程序时的方便,C语言允许用户为C语言固有的类型用typedef起别名。格式如下:typedefC固有的简单类型或复合类型别名标识符;用别名代替原来的类型,在说明中用作类型说明符。别名一般用大写字符,例如:typedeflongBIGBIGx=80000;,.,5.2嵌入式C语言的基本数据类型,(2)标识符变量名可以是C语言中允许的合法标识符。每一个变量都必须进行类型说明,也就是变量要先定义,后使用。当一个变量被指定为某一确定类型时,将为它分配若干相应字节的内存空间。如在32位体系的ARM系统中,char型为1字节,int型为4字节,float为4字节,double为8字节。当然,不同的体系结构的系统可能稍有差异。变量可以在程序内的三个地方定义:在函数内部,在函数的参数(形参)定义中或在所有的函数外部。由此定义的变量分别称为局部变量,形式参数和全局变量。在不同地方定义的变量,其作用域范围不同。在同一层次定义的变量不能与数组、指针、函数和其它变量同名。变量是用来存放数据的,由于数据有不同的类型,因此要定义相应类型的变量去存放它。这些数据称为相应变量的值。,.,5.2嵌入式C语言的基本数据类型,(3)存储类型存储类型指定被说明对象所在内存区域的属性。存储空间分为代码区与数据区两个部分。变量存储在数据区,数据区又可分为静态存储区与动态存储区。静态存储是指在程序运行期间给变量分配固定存储空间的方式。如全局变量存放在静态存储区中,程序运行时分配空间,程序运行完释放。动态存储是指在程序运行时根据实际需要动态分配存储空间的方式。如形式参数存放在动态存储区中,在函数调用时分配空间,调用完成释放。对于静态存储方式的变量可在编译时初始化,默认初值为0或空字符。对动态存储方式的变量如不赋初值,则它的值是一个不确定的值。在C语言中,具体的存储类别有自动(auto)、寄存器(register)、静态(static)及外部(extern)四种。静态存储类别与外部存储类别变量存放在静态存储区,自动存储类别变量存放在动态存储区,寄存器存储类别直接送寄存器。,.,5.2嵌入式C语言的基本数据类型,局部变量的存储方式局部变量一般用自动方式存储,用保留字auto加以定义,此时称为自动变量,是动态存储,在函数的调用过程中存在,由编译系统自动处理。例如:voidf()autointi,j;autofloatx,y;/*局部变量i,j,x,y以自动方式存储*/C语言规定,自动变量可省去说明符auto。如果希望函数调用完后局部变量的值被保留,不释放其所占存储单元,这时必须将其存储方式定义为静态存储方式,用保留字static加以定义。,.,5.2嵌入式C语言的基本数据类型,全局变量的存储方式全局变量一般用外部存储方式存储,用保留字extern加以定义。变量的作用域是构成整个程序的所有程序文件,也就是定义的外部变量可供其它程序文件使用。,.,5.2嵌入式C语言的基本数据类型,例如5.9程序由两个程序文件file1.c与file2.c组成。/*file1.c*/externinta;/*定义extern存储方式变量a*/main()intpow();intn;intp;scanf(%d,.,5.2嵌入式C语言的基本数据类型,/*file2.c*/externinta;/*申明本文件中使用的是已定义的外部变量a*/intpow(x)intx;inti,t=1;for(i=1;i、=、=、31);运行输出结果为0。因为两个是同一优先级,53的结果为1,而11的关系不满足,所以最后结果为0。又如:printf(%dn,1=1135);运行输出结果为1。因为的优先级比=高,则11b)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.4函数,4系统函数的使用系统函数的原型声明由系统提供,并且已分类存于不同的头文件中。用include指令嵌入相应头文件,然后可使用系统函数。include指令格式如下。#include其中:头文件名可带或不带路经,尖括号内为标准头文件,由开发环境或系统提供。若头文件为用户自定义头文件,则头文件在双引号内。,例5.13标准头文件定义#include#includestring.h和types.h是标准头文件,按环境变量include指定的目录顺序搜索string.h和types.h。,.,5.4函数,4系统函数的使用系统函数的原型声明由系统提供,并且已分类存于不同的头文件中。用include指令嵌入相应头文件,然后可使用系统函数。include指令格式如下。#include其中:头文件名可带或不带路经,尖括号内为标准头文件,由开发环境或系统提供。若头文件为用户自定义头文件,则头文件在双引号内。,例5.13标准头文件定义#include#includestring.h和types.h是标准头文件,按环境变量include指定的目录顺序搜索string.h和types.h。,.,5.5数组,1.一维数组(1)数组的定义数组在使用前必须先声明。声明一个一维数组的形式如下:数组长度注意:数组长度是个常量表达式。数组中的每个元素可以当成普通的变量使用。访问一维数组元素的形式如下:下标注意:下标的值也是从0开始,不能超过该维的长度减1。,.,5.5数组,(2)数组的初始化数组的初始化有以下几种方式。形式1:定义时整体初始化与变量在定义时初始化一样,数组也可以在定义时进行初始化,格式如下:数组长度=第0个元素值,第1个元素值,第n-1个元素值;例如对字这符数组进行初始化:chara10=a,b,c,d,e,f,g,h,j,k;,.,5.5数组,形式2:定义时部分初始化数组在定义时可对其中的部分数据进行初始化。当“”中值的个数少于元素个数时,只给前面部分元素赋值。例如如下定义就是对数组的前5个数据初始化,而后5个数据自动赋0(在字符数组中自动赋0)。chara10=a,b,c,d,e;,.,5.5数组,形式3:数组全部赋值若想要对数组中元素全部赋值,则可以省略数组下标中的常数,在此时,编译器会自动定义数组元素的个数,其格式如下:=第0个元素值,第1个元素值,第n个元素值;例如:chara=a,b,c,d,e,f,g,h,j,k;,.,5.5数组,2.多维数组(1)多维数组的定义多维数组的声明形式如下:长度1长度2长度n(2)多维数组的引用多维数组中元素的引用和一维数组的引用很相似,可通过下标引用的方式进行的。其表示形式为:第1维下标第2维下标第n维下标,.,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.5数组,3.数组作为函数的参数将整个数组作为参数传递给函数,可通过传递不带方括号的数组名来进行,其形式大体上有以下两种:形式1:(类型标识符数组名,int长度)形式2:(类型标识符数组名长度),.,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.5数组,(2)字符串的基本运算求字符串的长度假设字符串保存在数组pString中,求字符串长度的程序代码如下:charpString=abcd;intnSize=0;while(pStringnSize!=0)nSize+;执行完毕后,整型变量nSize中保存的值就是字符串pString的长度。,.,5.5数组,字符串的复制假设源字符串保存在数组pSource中,目的字符型数组为pDestination,则字符串复制的程序段如下:charpSource=abcd;charpDestination=5;/源字符串长度+1intnIndex=0;while(pSourcenSize!=0)pDestinationnIndex=pSourcenIndex;nIndex+;pDestinationnIdex=0;/标识字符串结束,.,5.5数组,字符串的连接假设源字符串保存在字符数组pDest中,要连接进来的字符串保存在字符数组pTocat中,则程序代码为:charpTocat=efg;charpDest8=abcd;intnSize=0;intnIndex=0;while(pDestnSize!=0)/找到被要连接的字符串的尾部nSize+;dopDestnSize+=pTocatnIndex;nIndex+;while(pTocatnIndex!=0);pDestnSize=0;,.,(3)字符串处理函数,要引入#include语句,.,(3)字符串处理函数,要引入#include语句,.,5.5数组,字符串的连接假设源字符串保存在字符数组pDest中,要连接进来的字符串保存在字符数组pTocat中,则程序代码为:charpTocat=efg;charpDest8=abcd;intnSize=0;intnIndex=0;while(pDestnSize!=0)/找到被要连接的字符串的尾部nSize+;dopDestnSize+=pTocatnIndex;nIndex+;while(pTocatnIndex!=0);pDestnSize=0;,.,5.8汇编语言与C/C+的混合编程,汇编语言与C/C+的混合编程在C/C代码中嵌入汇编指令。在汇编程序和C/C的程序之间进行变量的互访。汇编程序、C/C程序间的相互调用。,.,5.8汇编语言与C/C+的混合编程,5.8.1内嵌汇编指令是嵌入在C/C+程序中使用,但不能直接引用C语言的变量定义,数据交换必须通过ATPCS进行。1内嵌汇编指令的语法格式_asm_(“instruction.instruction”);/Linuxgcc中支持_asminstructioninstruction;/ADS中支持特别注意:是“_asm”是两个下划线。,.,5.8.1内嵌汇编指令,2内嵌汇编指令的特点(1)操作数内嵌的汇编指令中作为操作数的寄存器和常量可以是C/C+表达式。是char、short、int类型,而且这些表达式都是作为无符号数进行操作。编译器将会计算这些表达式的值,并为其分配寄存器。,.,5.8.1内嵌汇编指令,(2)物理寄存器内嵌汇编指令中使用物理寄存器的限制:不能直接向PC寄存器中赋值,程序的跳转只能通过B指令和BL指令实现。在内嵌汇编指令中,不要使用过于复杂的C/C+表达式。编译器可能会使用R12寄存器或R13寄存器存放编译的中间结果,在计算表达式值时可能会将寄存器R0到R3、R12以及R14用于子程序调用。指令中不要使用指定的物理寄存器。,.,5.8.1内嵌汇编指令,(3)常量在内嵌的汇编指令中,常量前的符号#可省略。若表达式前使用了符号#,则必须是一个常量。(4)指令展开内嵌的汇编指令中如果包含常量操作数,该指令可能会被汇编器展开成几条指令。例如指令:ADDR0,R0,#1023可能会被展开成下面的指令序列:ADDR0,R0,#1024SUBR0,R0,#01MUL指令会被展开成一系列加法和移位操作。,.,5.8.1内嵌汇编指令,(5)标号C/C+程序中的标号可被内嵌的汇编指令使用。但只有B指令可使用C/C+程序中的标号,指令BL不能使用C/C+程序中的标号。指令B使用C/C+程序中的标号格式:Bcondlabel(6)内存单元的分配所用的内存单元的分配都是通过C/C+程序完成的,分配的内存单元通过变量供内嵌的汇编器使用。,.,5.8.1内嵌汇编指令,(7)SWI和BL指令的使用内嵌SWI和BL指令中3个可选寄存器列表第1个寄存器列表中的寄存器用于存放输入的参数。第2个寄存器列表中的寄存器用于存放返回的参数。第3个寄存器列表中的寄存器的内容可能被被调用的子程序破坏,即这些寄存器是供被调用的子程序作为工作寄存器。,.,5.8.1内嵌汇编指令,3内嵌的汇编器与armasm的区别在功能和使用方法上主要有以下特点:不能写PC(movpc,lr)不支持伪指令LDRRn,=expression,但可用指令:LDRRn,expression来代替。除nop外,不支持ADR、ADRL等伪指令。指令中的C变量不要与任何物理寄存器重名LDM/STM指令中的寄存器列表只能使用物理寄存器,不能使用C表达式不支持指令BX/BLX用户不用维护数据栈不要轻易改变处理器模式。不支持内存分配操作,.,5.8.1内嵌汇编指令,4内嵌汇编指令的应用举例下面是在ADS1.2下编译通过程序,程序中内嵌汇编指令。例5.14字符串复制本例中使用了指令BL调用子程序。在内嵌的BL指令中,除了正常的操作数外,还必须增加以下三个可选的寄存器列表:第1个用于存放输入的参数第2个用于存放返回的结果第3个作为子程序的工作寄存器,程序代码如下:#includevoidmy_strcpy(char*src,constchar*dst)intch;_asmloop:LDRBch,src,#1STRBch,dst,#1CMPch,#0BNEloop;,intmain(void)constchar*a=HelloWorld!;charb20;_asmMOVR0,aMOVR1,bBLmy_strcpy,R0,R1;printf(OriginalString:%sn,a);printf(“CopiedString:%sn,b);return0;,例中主函数main(void)中的BLmy_strcpy,R0,R1指令的输入寄存器列表为R0,R1;它没有输出寄存器列表;被子程序使用的工作寄存器为ATPCS的默认工作寄存器R0R3、R12、lr以及PSR。,.,5.8.1内嵌汇编指令,例5.15使能和禁止中断本例主要介绍怎样利用内嵌的汇编程序实现使能和禁止中断。通过修改CPSR寄存器中的bit7可使能和禁止中断。但这些操作必须在特权模式下进行,因为在用户模式下不能修改寄存器CPSR中的控制位。源程序代码如下。,程序代码如下:_inlinevoidenable_IRQ(void)inttmp;_asmMRStmp,CPSRBICtmp,tmp,#0 x80MSRCPSR_c,tmp,_inlinevoiddisable_IRQ(void)inttmp;_asmMRStmp,CPSRORRtmp,tmp,0 x80MSRCPSR_c,tmpintmain(void)disable_IRQ();enable_IRQ();,.,5.8.2从汇编程序中访问C程序变量,在C程序中声明的全局变量可被汇编程序通过地址间接访问。具体访问方法如下:使用IMPORT伪操作声明该全局变量。使用LDR伪指令读取该全局变量的内存地址,通常该全局变量的内存地址值存放在程序的数据缓冲池中(literalpool)。根据该数据的类型,使用相应的LDR指令读取该全局变量的值;使用相应的STR指令修改该全局变量的值。,.,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.8.2从汇编程序中访问C程序变量,例5.16从汇编程序中访问C程序全局变量在汇编程序中访问C程序全局变量。程序中变量globvl是在C程序中声明的全局变量。在汇编程序中首先用IMPORT伪操作声明该变量;将其内存地址读入到寄存器R1中;再将其内存单元中的值读入到寄存器R0中;修改后再将寄存器R0的值赋于变量globvl。,.,5.8.2从汇编程序中访问C程序变量,例5.16从汇编程序中访问C程序全局变量源程序代码如下:AREAglobals,CODE,READONLYEXPORTasmsubIMPORTglobvlasmsubLDRr1,=globvlLDRr0,r1ADDr0,r0,#2STRr0,r1MOVpc,lrEND,.,5.8.3编程序与C/C+程序的相互调用规则ARPCS,ATPCS(ARM-ThumbProcedureCallStandard)规则。1寄存器的使用规则寄存器的使用必须满足下面规则:子程序间通过寄存器R0R3来传递参数,记为A1A4。被调用的子程序在返回前无须恢复寄存器R0R3的内容。子程序中使用寄存器R4R11来保存局部变量。记为V1V8。如果在子程序中使用到了寄存器V1V8中的某些寄存器,则子程度进入时必须保存这些寄存器的值,在返回前必须恢复这些寄存器的值;对于子程序中没有用到的寄存器,则不必进行这些操作。在Thumb程序中,通常只能使用寄存器R4R7来保存局部变量。,.,5.8.3汇编程序与C/C+程序的相互调用规则ARPCS,寄存器R12用作子程序间的scratch寄存器,常用于子程序间的连接代码段中,记作IP。寄存器R13用作数据栈指针,记作SP。在子程序中寄存器R13不能用作其他用途。寄存器SP在进入子程序时的值和退出子程序时的值必须相等。寄存器R14称为链接寄存器,记作LR。它用于保存子程序的返回地址。如果在子程序中保存了返回地址,则寄存器R14可作其他用途。R15是程序计数器,记作PC。不能作其他用途。,.,5.8.3汇编程序与C/C+程序的相互调用规则ARPCS,.,5.8.3汇编程序与C/C+程序的相互调用规则ARPCS,2数据栈的使用规则有下面4种数据栈:FD(FullDescending)满递减;ED(EmptyDescending)空递减;FA(FullAscending)满递增;EA(EmptyAscending)空递增。,.,5.8.3汇编程序与C/C+程序的相互调用规则ARPCS,ATPCS规定数据栈为FD(满递减)类型,并且对数据栈的操作是8字节对齐的。异常中断的处理程序可使用中断程序的数据栈。数据栈指针(StackPoint):最后一个写入栈的数据的内存地址。数据栈的基地址(StackBase):数据栈的最高地址。ATPSC中的数据栈是FD型,最早入栈的数据所占的内存单元是基地址的下一个内存单元。数据栈界限(StackLimit):数据栈中可使用的最低的内存单元地址。数据栈中的数据帧(StackFrames):数据栈是为子程序分配用来保存寄存器和局部变量的区域。,.,5.8.3汇编程序与C/C+程序的相互调用规则ARPCS,3参数的传递规则参数传递规则具体如下:(1)参数个数固定的子程序参数传递规则对于参数个数固定的子程序,如果系统包含浮点运算的硬件部件,浮点参数将按照下面规则传递:各个浮点参数按顺序处理。为每个浮点参数分配FP寄存器。方法是:满足该浮点参数需要的且编号最小的一组连续的FP寄存器。第一个整数参数,通过寄存器R0R3来传递。其他参数通过数据栈传递。,.,5.8.3汇编程序与C/C+程序的相互调用规则ARPCS,(2)参数个数可变的子程序参数传递规则当参数不超过4个,用R0R3传递参数,当参数超过4个时,使用数据栈来传递参数。在参数传递时,所有参数看作是存放在连续的内存字单元中的字数据。然后,依次将各字数据传递到寄存器R0R3中,如果参数多于4个,将剩余的字数据传送到数据栈中,入栈的顺序与参数顺序相反,即最后一个字数据先入栈。,.,5.8.3汇编程序与C/C+程序的相互调用规则ARPCS,(3)子程序结果返回规则子程序返回结果规则如下:结果为一个32位整数,可通过寄存器R0返回;结果为一个64位整数,可通过寄存器R0,R1返回,依次类推;结果为一个浮点数时,可通过运算部件的寄存器F0、D0或者S0来返回;结果为复合型的浮点数(如复数)时,可通过寄存器F0Fn或者D0Dn来返回。对于位数更多的结果,需通过内存来传递,如通过数据栈来传递。,.,5.8.4汇编程序与C/C+程序的相互调用,1C语言程序调用汇编语言程序汇编语言程序的设计要遵守ATPCS。在汇编语言中需用EXPORT伪操作来说明,使得本程序可被其他程序调用。C语言程序调用该汇编语言程序之前,需要在C语言程序中使用extern关键词来声明该汇编语言程序。,.,5.8.4汇编程序与C/C+程序的相互调用,#includeexternvoidmy_strcpy(char*d,constchar*s);intmain()constchar*srcstr=Firststring-source;chardststr=Secondstring-destination;printf(Beforecopying:n);printf(%sn%sn,srcstr,dststr);my_strcpy(dststr,srcstr);printf(Aftercopying:n);printf(%sn%sn,srcstr,dststr);return0;,例5.17C语言程序调用汇编语言程序完成字符串复制C语言源程序如下:,AREASCopy,CODE,READONLYEXPORTmy_strcpymy_strcpyLDRBr2,r1,#1STRBr2,r0,#1CMPr2,#0BNEmy_strcpyMOVpc,lrEND,根据ATPCS规则,函数的前4个参数在R0R3中。在本例汇编程序中,寄存器R0中存放第1个参数,即dststr;寄存器R1中存放第2个参数,即srcstr。C语言代码源程序可保存为strtest.c,汇编语言程序是scopy.s。,.,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,sp,#-4!;第五个参数5*i通过数据栈传递ADDr3,r1,r1;r3值设为4*iBLg;调用C程序g()ADDsp,sp,#4;调整数据栈指针,准备返回LDRpc,sp,#4;返回END,.,5.8.4汇编程序与C/C+程序的相互调用,3C+程序调用C语言程序C程序和C+程序有很多特性不同,编译链接方法也有所不同,两者不能直接互访。C+程序调用C程序时,在C+程序中使用关键词extern“C”声明被调用的C程序。对于C+中的类(class)或者结构(struct),如果它没有基类和虚函数,则相应的对象的存储结构和ARMC相同。下面的例子说明了这一点。,例5.19C+程序调用C程序/C+程序structS/本结构没有基类和虚函数S(ints):i(s)inti;,extern“C”voidcfunc(S*);/extern声明被用的C程序intf()Ss(2);/初始化该结构对象cfunc(intopen(constchar*pathname,intflags,mode_tmode);intclose(intfd);open函数有两个形式,其中pathname是要打开的文件名(包含路径名称,缺省是认为在当前路径下面)。flags可以去下面的一个值或者是几个值的组合。O_RDONLY:以只读的方式打开文件;O_WRONLY:以只写的方式打开文件;O_RDWR:以读写的方式打开文件;O_APPEND:以追加的方式打开文件;O_CREAT:创建一个文件;O_EXEC:如果使用了O_CREAT而且文件已经存在,就会发生一个错误;O_NOBLOCK:以非阻塞的方式打开一个文件;O_TRUNC:如果文件已经存在,则删除文件的内容。,.,5.9.1文件的创建和读写,使用open的第二种形式,指定mode标志,表示文件访问权限,mode的组合:S_IRUSR用户可以读S_IWUSR用户可以写S_IXUSR用户可以执行S_IRWXU用户可以读写执行S_IRGRP组可以读S_IWGRP组可以写;S_IXGRP组可以执行S_IRWXG组可以读写执行S_IROTH其他人可以读S_IWOTH其他人可以写;S_IXOTH其他人可执行S_IRWXO其他人可读写执行S_ISUID设置用户执行IDS_ISGID设置组的执行ID,.,Linux共用5个数字表示文件的各种权限即:00000。第一位为用户ID;第二位为组ID;第三位为用户权限位;第四位为组的权限;最后一位为其他人的权限。每个数字可以取1(执行权限)、2(写权限)、4(读权限)、0(什么也没有)或者是这几个值的和。,5.9.1文件的创建和读写,.,5.9.1文件的创建和读写,例:要创建一个用户读写执行,组没有权限,其他人读执行的文件。设置用户ID位可以使用的模式是:第1位:1(设置用户ID)第2位:0(组没有设置)、第3位:7:(1+2+4)第4位:0(没有权限,使用缺省)第5位:5(1+4),open(temp,O_CREAT,10705);打开文件成功,open返回一个文件描述符。调用close关闭文件,其中fd是文件描述符。,.,文件打开后,可调用函数read和write读写文件。ssize_tread(intfd,void*buffer,size_tcount);ssize_twrite(intfd,constvoid*buffer,size_tcount);fd是文件描述符,buffer是要写入文件内容或读出文件内容的内存地址,count是要读写的字节数。read从指定文件(fd)中读取count字节到buffer缓冲区中,返回count。若read读到了文件的结尾或者被一个信号所中断,返回值小于count。若是由信号中断引起返回,read会返回-1,且设置errno为EINTR。当程序读到了文件结尾的时候,read会返回0。write从buffer中写count字节到文件fd中,成功时返回实际所写的字节数。,5.9.1文件的创建和读写,.,5.9.1文件的创建和读写,实例用来拷贝文件。#include#include#include#include#include#include#defineBUFFER_SIZE1024intmain(intargc,char*argv)intfrom_fd,to_fd;intbytes_read,bytes_write;charbufferBUFFER_SIZE;char*ptr;,if(argc!=3)fprintf(stderr,Usage:%sfromfiletofilena,argv0);exit(1);/*打开源文件*/if(from_fd=open(argv1,O_RDONLY)=-1)fprintf(stderr,Open%sError:%sn,argv1,strerror(errno);exit(1);,.,5.9.1文件的创建和读写,/*创建目的文件*/if(to_fd=open(argv2,O_WRONLY|O_CREAT,S_IRUSR|S_IWUSR)=-1)fprintf(stderr,Open%sError:%sn,argv2,strerror(errno);exit(1);/*以下代码是一个拷贝文件的代码*/while(bytes_read=read(from_fd,buffer,BUFFER_SIZE)if(bytes_read=-1)while(bytes_write=write(to_fd,ptr,bytes_read),if(bytes_write=-1),.,5.9.2移动文件的读写位置,文件打开时其读写位置指向文件开头,若是以附加的方式打开文件(如O_APPEND),则读写位置会指向文件尾。当read()或write()时,读写位置会随之增加,lseek()是用来控制该文件的读写位置。lseek()函数的原型如下:off_tlseek(intfildes,off_toffset,intwhence);使用时需用到的表头文件是:#include#includelseek()函数中参数fildes为已打开的文件描述词,参数offset根据参数whence来移动读写位置。,.,5.9.2移动文件的读写位置,参数whence为下列其中一种:SEEK_SET参数offset即为
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 现场处置方案编制课件
- 2025年能源行业CCS项目经济性研究报告:市场前景与投资建议
- 2025年物流行业物流园区智能化改造对物流行业行业政策法规的适应报告
- 山西省晋中市左权县2022-2023五年级上学期期中科学试题(含答案)
- 2026届贵州省贵阳市清镇北大培文学校贵州校区化学高一上期末考试试题含解析
- 2025年导游资格证专项训练试卷:导游业务与法规冲刺押题
- 2025年Python大数据处理培训试卷:实战演练与冲刺押题
- 2025年秋季初级经济师职业资格考试 经济基础知识模拟试卷及答案
- 2025年注册会计师(CPA)考试 会计科目历2025年真题解析与模拟试卷
- 江西省白鹭洲中学2026届高二化学第一学期期中学业水平测试试题含解析
- 企业信息化项目建设进度和成果汇报课件
- 高等数学期末试卷及答案
- 从0开始跨境电商-第三章-阿里巴巴国际站入门-OK
- 新能源电站远程监控系统建设方案
- 《紫藤萝瀑布》《丁香结》《好一朵木槿花》
- 2023柔性棚洞防护结构技术规程
- 河流地貌的发育 - 侵蚀地貌
- 离网光伏发电系统详解
- 广告文案写作(第二版)全套教学课件
- 《国家电网公司电力安全工作规程(配电部分)》
- 金融学黄达ppt课件9.金融市场
评论
0/150
提交评论