由机器语言到高级语言程序编写编译==_第1页
由机器语言到高级语言程序编写编译==_第2页
由机器语言到高级语言程序编写编译==_第3页
由机器语言到高级语言程序编写编译==_第4页
由机器语言到高级语言程序编写编译==_第5页
已阅读5页,还剩59页未读 继续免费阅读

下载本文档

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

文档简介

第6讲由机器语言到高级语言---程序编写编译6.1混合编程6.2C/C++的嵌入式汇编6.3用C/C++调用汇编6.1混合编程

混合编程即由高级语言来调用或嵌入汇编语言子程序,或用汇编语言调用或嵌入高级语言子程序。汇编程序常以过程的形式同高级语言(如C/C++、BASIC、PASCAL、DELPHI等)一起使用。6.1混合编程

高级语言和汇编语言连接很容易,因为在高级语言编译后生产的编译程序是一个.OBJ的文件,这与汇编程序输出的目标文件一样都是机器语言程序。那么我们就可以利用link将高级语言程序产生的.OBJ程序与汇编程序产生的.OBJ程序连接起来,形成一个.EXE的可执行文件。6.1混合编程高级语言与汇编语言的连接应注意下面几个问题:1、两种语言之间的控制传输问题2、参数的传递3、存储分配问题6.2C/C++的嵌入式汇编在C/C++与汇编语言的混合编程过程中,C/C++调用汇编代码常有两种方法:一、直接在C/C++程序中嵌入汇编语句;二、C/C++调用汇编语言子程序。6.2C/C++的嵌入式汇编在C或者C++中进行嵌入式汇编需要注意以下要点:嵌入式汇编语言代码支持INTEL80X86CPU的全部32位指令系统,但是不能使用伪指令与宏指令语句,也不能使用结构〔STRUCT〕和记录〔RECORD〕;嵌入式汇编语言可以使用C++程序中标识符,包括标号、变量、函数名、常量、宏、类型名、结构和联合的成员以及类对象的公有〔PUBLIC〕成员变量等;嵌入式汇编语言代码中可以使用汇编语言格式的常数〔131AH〕,也可以使用C++格式的常数〔0X131A〕;嵌入式汇编语言不能使用C++语言的运算符;嵌入式汇编语言代码中的转移指令和C++中的GOTO语句都能跳转到汇编语言或者C++定义的标号;嵌入式汇编语言定义的函数返回值的传递方法与预模块调用汇编中汇编语言程序返回值的传递方法相同,在C++程序编译时会产生“NORETURNVALUE〞警告,可以使用#PRAGMAWARNING(DISABLE:4035)预编译语句禁止该警告。7.2C/C++的嵌入式汇编

在C/C++程序中嵌入汇编语句在嵌入式汇编中访问C/C++的数据汇编语言程序段编写C函数汇编程序调用C/C++函数在C/C++程序中嵌入汇编语句

第一种方式在C/C++程序中嵌入汇编语句_ASM操作码操作数<;或换行>操作码是处理器指令或假设干伪指令;操作数是操作码可接受的数据。内嵌的汇编语句可以用分号“;〞结束,也可以用换行符结束;一行中可以有多个汇编语句,相互间用分号分隔,但不能跨行书写。嵌入汇编语句的分号不是注释的开始;要对语句注释,应使用C的注释,如/*…*/。在C/C++程序中嵌入汇编语句第二种方式,_ASM{汇编程序段}采用花括号的汇编语言程序段形式。_ASM{汇编程序段}如下所示:_ASM{MOVAX,15HMOVCX,9HADDAX,CX}在C/C++程序中嵌入汇编语句包含在括号中的汇编代码必须按照特定的格式:1〕指令必须括在引号里。2〕如果包含的指令超过一条,那么必须使用新行字符分隔汇编语言代码的每一行。通常,还包含制表符帮助缩进汇编语言代码,使代码行更容易阅读。例:显示1到1000中任一个数的二进制到十六进制数。#INCLUDE<IOSTREAM.H>CHAR*BUFFER="ENTERANUMBERBETWEEN0AND1000:";CHAR*BUFFER1="BASE";INTB=0;CHARA;VOIDDISPS(INTBASE,INTDATA){INTTEMP;_ASM{MOVAX,DATAMOVBX,BASEPUSHBXTOP1:

MOVEDEX,0

DIVBX

PUSHDX

CMPAX,0JNZTOP1TOP2:

POPDX

CMPDX,BX

JETOP4

ADDDX,30H

CMPDX,39H

JBETOP3

ADDDX,7TOP3:

MOVTEMP,EX}COUT<<(CHAR)TEMP;_ASM{JMPTOP2}TOP4:;}VOIDMAIN(VOID){INTI;COUT<<BUFFER;CIN.GET(A);WHILEA>=’0’&&A<=’9’){_ASM{SUBA,30H}B=B*10+A;CIN.GET(A);}FOR(I=2;I<17;I++){COUT<<BUFFER1;DISPS(10,I);COUT<<(CHAR)(0X20);DISPS(I,B);COUT<<(CHAR)(10);COUT<<(CHAR)(13);}}在使用嵌入式汇编中要注意的几个问题:操作码支持8086/8087指令或假设干伪指令:db/dw/dd和extern;操作数是操作码可接受的数据:立即数、存放器名,还可以是C/C++程序中的常量、变量和标号等;内嵌的汇编语句可以用分号“;〞结束,也可以用换行符结束;使用C的注释,如/*…*/;正确运用通用存放器、标号等。在嵌入式汇编中访问C/C++的数据

内嵌的汇编语句除可以使用指令允许的立即数、存放器外,还可以使用C/C++程序中的任何符号〔标识符〕,包括变量、常量、标号、函数名、存放器变量、函数参数等;C编译程序自动将它们转换成相应汇编语言指令的操作数,并在标识符名前加下划线。一般来说,只要汇编语句能够使用存储器操作数〔地址操作数〕,就可以采用一个C/C++程序中的符号;同样,只要汇编语句可以用存放器作为合法的操作数,就可以使用一个存放器变量。在嵌入式汇编中访问C/C++的数据对于具有内嵌汇编语句的C/C++程序,C编译器要调用汇编程序进行汇编。汇编程序在分析一条嵌入式汇编指令的操作数时,假设遇到了一个标识符,它将在C/C++程序的符号表中搜索该标识符;但8086存放器名不在搜索范围之内,而且大小写形式的存放器名都可以使用。在嵌入式汇编中访问C/C++的数据【例7-2】用嵌入汇编方式实现取两数较小值的函数MININTMIN(INTVAR1,INTVAR2){ASMMOVAX,VAR1ASMCMPAX,VAR2ASMJLEMINEXITASMMOVAX,VAR2MINEXIT:RETURN(_AX);}MAIN(){MIN(100,200);}在

_ASM模块中,可以使C++或ASM的基数计数法,比方0X100和100H是相等的。_ASM块中不能使用“<<〞之类的C++操作符。C++和MASM通用的操作符,比方“*〞和“[]〞那么被认为是汇编语言的操作符也可以使用“TYPE〞来使其与C++风格一致。比方,下面两条语句的作用是一样的:ASMMOVARRAY[6*TYPEINT],0;ARRAY[6]=0;嵌入汇编也能通过变量名直接引用C++的变量。如果C++中的类、结构或者枚举成员具有唯一的名称,如果在“.〞操作符之前不指定变量或者TYPEDEF名称,那么_ASM块中只能引用成员名称。如果成员不是唯一的,那么必须在“.〞之前加上变量名或TYPEDEF名称。STRUCTNAM1{CHAR*FTH;INTNAME12;};STRUCTNAM2{INTWIN;LONGNAME12;};汇编语言程序段编写C函数在调用函数之前应编程将参数以逆序写入到当前运行任务所使用的任务堆栈中,压栈之前堆栈指针可不作调整。被调用的C函数即可正常访问调用者传递的参数,函数调用完毕后需要调整堆栈指针,去除函数调用中参数所占用的堆栈空间。C函数的返回值可以通过访问累加器获得。为了能够编写出可供C/C++调用的函数。应了解C/C++模块与汇编模块的接口机制,而从以汇编形式给出的编译结果中可方便地了解这种机制。C/C++程序中含有嵌入式汇编语言语句时,C编译器首先将C代码的源程序〔.c〕编译成汇编语言源文件〔.asm〕,然后激活汇编程序将产生的汇编语言源文件编译成目标文件〔.obj〕,最后激活link将目标文件链接成可执行文件〔.exe〕。例:将字符串中的小写字母转变为大写字母显示#INCLUDEVOIDUPPER(CHAR*DEST,CHAR*SRC){ASMMOVSI,SRC/*DEST和SRC是地址指针*/ASMMOVDI,DESTASMCLDLOOP:ASMLODSB/*C/C++定义的标号*/ASMCMPAL,'A'ASMJBCOPY/*转移到C的标号*/ASMCMPAL,'Z'ASMJACOPY/*不是’A’到’Z’之间的字符原样复制*/ASMSUBAL,20H/*是小写字母转换成大写字母*/COPY:ASMSTOSBASMANDAL,AL/*C/C++中,字符串用NULL〔0〕结尾*/ASMJNZLOOP}MAIN()/*主程序*/{CHARSTR[]="THISSTARTEDOUTASLOWERCASE!";CHARCHR[100];UPPER(CHR,STR);PRINTF("ORIGINSTRING:\N%S\N",STR);PRINTF("UPPERCASESTRING:\N%S\N",CHR);}编辑完成后,在命令行输入如下编译命令,选项-I和-L分别指定头文件和库函数的所在目录。TCC-B-Iinclude–Llibsjw7-03.cSJW7-04.ASM的主要内容如下所示:_TEXTSEGMENTBYTEPUBLIC'CODE';代码段DGROUPGROUPDATA,BSSASSUMECS:_TEXT,DS:DGROUP,SS:DGROUP_TEXTENDS_DATASEGMENTGWORDPUBLIC’DATA’}已初始化的数据段_SJW01LABELWORDDW5_DATAENDSPUSHAX;为调用SUM压入第三个参数

PUSHWORDPTRDGROUP:_SJW01MOVAX,1PUSHAX;压入第一个参数

CALLNEARPTR_SUM;调用SUMADDSP,6;废除压入堆栈的三个参数

MOVWORDPTRDGROUP:SJW02,AXPUSHWORDPTRDGROUP:_SJW02MOVAX,OFFSETDGROUP:SJ03PUSHAX;压入第一个叁数

CALLNEARPTR_PRINTF;POPCX;废除压入堆栈的两十参数

POPCXSJ01:RET_MAINENDP_SUMPROCNEARPUSHBPMOVBP,SPMOVAX,WORDPTR[BP+4]ADDAX,WORDPTR[BP+6]ADDAX,WORDPTR[BP+8]JMPSHORTSJ02SJ02:POPBPRET_SUMENDP_TEXTENDS_BSSSEGMENTWORDPUBLIC‘BSS’_SJW02LABELWORDDB2DUP(?)_BSSENDSDATASEGMENTWORDPUBLIC‘DATA’SJ03LABELBYTEDB47DB100DB12DB0.DATAENDSTEXTSEGMENTBYTEPUBLIC‘CODE’EXTRN_PRINTF:NEARTEXTENDSPUBLIC_SJW02PUBLIC_SJW01PUBLIC_MAINPUBLIC_SUMEND汇编程序调用C/C++函数

汇编程序中调用C函数相比照较简单,编译器已经提供了相当完善的支持。汇编语句中的CALL语句,可以用于调用其它过程,既可以是其它汇编程序段也可以是C/C++程序中的标准过程。【例7-4】输出HELLOWORLD〞EXTERNTEST();VOIDMAIN(){TEST();}VOIDSHOW(CHAR3STR){PRINTF(“%S〞,STR);};汇编子程序:HELLO.ASM.MODELSMALL,CEXTERNSHOW:NEAR.DATAHELLOSTRINGDB‘HELLO,ASSEMBLY!’,0DH,0AH,’$’ZYSTRINGDB‘HELLO,CPROGRAM!’,0.CODEPUBLIC_TEST_TESTPROCLEADX,HELLOSTRINGMOVAH,09HINT21HLEAAX,ZYSTRINGPUSHAXCALL_SHOWADDSP,2RET_TESTENDPEND【例7-5】调用PRINTF函数CHARSETGS[]=“%S%CHARSETSJ[]=“THISISAGOODDAY!〞;VOIDMAIN(){_ASM{MOVAX,OFFSETSETSJPUSHAXMOVAX,OFFSETSETWHPUSHAXMOVAX,OFFSETSETGSPUSHAXCALLPRINTF;调用PRINTF函数POPBX;去除堆栈POPBXPOPBX}}6.3用C/C++调用汇编

接口调用汇编模块接口

采用模块调用方式进行混合编程一般执行的步骤,首先建立C++源程序〔.CPP〕和汇编源程序并把汇编程序编译成.OBJ文件;然后将编译后的汇编文件和C++源程序放入建立好的C++工程工程〔.PRJ〕中;最后对工程文件进行编译、连接,生成可执行文件。汇编程序的一般格式格式:_TEXTSEGMENTBYTEPUBLIC’CODE’DGROUPGROUP_DATA,_NEWS,CONST_DATASEGMENTWORDPUBLIC’CODE’初始化数据_DATAENDS_NEWSSEBMENTWORDPUBLIC’NEWS’_NEWSENDSASSUMECS:_TEXT,DS:DGROUP,SS:DGROUPPUBLIC_函数名_函数名PROCNEAR/FARPUSHBPMOVBP,SPPUSHSIPUSHDI……程序主体语句POPDIPOPSIPOPBPRET_函数名ENDP_TEXTENDSEND汇编作为主程序来调用C/C++子程序的格式1〕程序开始处参加语句:EXTERN_函数名:NEAR/FAR,说明这个函数是外部的,即将被调用的C/C++子程序。2〕省去主过程语句中关于BP,SI,DI的堆栈操作语言。3)在主过程语句中通过CALL语句实现对外部函数的调用。格式如下:CALLNEARPTR_函数名虽然C/C++程序可以实现许多汇编语言的功能,但还是有不少特性只有汇编语言才能做:执行PUSH和POP操作。访问BP与SP存放器。初始化某些段存放器。执行时间要求严格的例行程序,比方显示视频图形和实现通过端口的I/O。以上代码模板可以用于C/C++函数使用的所有汇编语言函数。当然,如果特定的函数不改变BX、SI或者DI存放器,可以省略相关的PUSH和POP指令。调用汇编模块C/C++程序提供了与汇编语言的接口和在C/C++程序中直接插入汇编指令代码的功能,支持以“远调用〞和“近调用〞方式来调用使用汇编语言编写的函数。调用汇编模块

采用一致的调用协议命名约定声明公用函数名和变量名参数的传递原那么语言子程序中存放器的使用入口参数和返回参数的约定1、采用一致的调用协议C/C++程序具有三种调用协议:_CDECL、_STDCALL和_FASTCALL。汇编语言利用“语言类型〞确定调用协议和命名约定,支持的语言类型有:C、SYSCALL、STDCALL、PASCAL、BASIC和FORTRAN。高级语言的参数通过堆栈传递给汇编语言过程,汇编语言过程将返回结果放在AX或DX:AX中。在BP、DI、SI、DS、SS和方向标志位被改动之前应使用汇编语言过程保存起来。这些存放器是高级语言可能用到的采用一致的调用协议图7-1使用C/C++调用协议时堆栈内容A)近过程调用;B)远过程调用采用一致的调用协议_STDCALL调用约定代码片段;调用函数…PUSHPARAM3PUSHPARAM2PUSHPARAM1CALLSUBPROC…;被调用函数PUSHBPMOVBP,SP…MOVSP,BPPOPBPRET12(去除堆栈)C/C++有两种常用的函数调用约定:_STDCALL和_CDECL采用一致的调用协议_CDECL调用约定代码片段;调用函数…PUSHPARAM3PUSHPARAM2PUSHPARAM1CALLSUBPROCADDSP,12(去除堆栈)…;被调用函数PUSHBPMOVBP,SP…MOVSP,BPPOPBPRET命名约定C/C++程序可以调用汇编语言的子程序或称过程或函数和汇编语言中定义的变量,汇编语言也可以调用C/C++编的函数和定义的变量,但要注意的是,由于C编译后的目标文件自动地在函数名和变量名前一个下划线,这是因为编译系统为了防止和它自己使用的内部函数和变量名发生混淆而造成错误,所以C汇编模块必须使用和C/C++兼容的有关段与变量的命名约定。在C/C++程序中的所有外部名字都包含个前导的下划线字符,如COLUMN。汇编程序引用在C/C++模块中的函数与变量也必须由下划线〔_〕开始。而且,由于C/C++是对大小写字母敏感的,所以汇编模块对于任何公共的变量名应当使用和C/C++模块同样的字母(大写或小写)。声明公用函数名和变量名对C/C++程序和汇编语言使用的公用函数和变量应该进行声明,并且标识符应该一致,C++语言对标识符区分字母的大小写,而汇编不区分大小写。在汇编语言程序的开始局部,应对调用的函数和变量用EXTERN加以说明,其格式为:{EXTERN_函数名:函数类型}或{EXTERN_变量名:变量类型}其中函数类型指明函数是一个近程函数或是一个远程函数〔即处在另一个段中〕,分别表示为NEAR型或FAR型,而变量类型指该变量的数据类型其对应的关系如表7-2而变量类型指该变量的数据类型其对应的关系3、声明公用函数名和变量名如调用C/C++程序中MFPRO()的函数和变量SIGN,它们在C/C++程序开始说明局部为:INTMFPRO(VOID);INTSIGN,JARRAY[10];CHARCH;LONGRESULT;在调用它的汇编程序中那么在程序开始说明为:EXTERN_MFPRO:NEAREXTEMSIGN:WORD,_JARRAY:WORD,CH:BYTE,RESULT:DWORD假设C/C++程序调用汇编语言中的过程(函数)和变量,那么汇编语言中应用PUBLIC进行说明,且函数名和变量名前应带有下划线,即函数名和变量名的第一个符号应是一下划线参数的传递原那么一般C/C++程序和调用的子程序共用一个堆栈,因此在汇编语言子程序中开始必须执行两条指令PUSHBPPOPBP,SP参数的传递原那么传送参数有3种方法1〕用值C/C++调用程序传送变量的一个副本在堆栈中。被调用的汇编模块可以修改传送的值,但不能访问调用程序原来的值。如果有一个以上的参数,C/C++从最右边的参数开始使它们进栈。参数的传递原那么传送参数有3种方法2〕用近引用调用程序传送数据项值的偏移地址。被调用的汇编模块假设和调用程序共享同一个数据段。参数的传递原那么传送参数有3种方法3〕用远引用调用程序传送段与偏移地址〔段在前,然后是偏移地址〕。被调用的汇编模块假设使用与调用程序不同的数据段。被调用的程序可以使用LDS或LES指令来初始化段地址。C/C++程序传送参数到堆栈中是以和其他语言相反的顺序进行的。例如:ADDS〔NUM1,NUM2〕;该语句先使NUM2,后使NUM1进栈,就按次序并调用ADDS。在从被调用模块返回时,C/C++模块〔不是汇编模块〕使SP增量去丢弃传送的参数。在被调用的汇编模块中,用于访问2个传送参数的典型过程如下:PUSHBP;保存BPMOVBP,SP;用SP地址作为基址指针MOVDH,[BP+4];从堆栈中取值MOVDL,[BP+6]…POPBP;恢复BPRET在PUSHBP指令之后,堆栈帧表现为:5、语言子程序中存放器的使用在C/C++程序中定义的变量,在汇编程序里访问很简单,首先在C/C++程序中定义为全局变量,然后在汇编程序中用.GLOBAL定义(变量名前同样需加_),再利用间接寻址的方式即可实现,在C/C++程序中定义的数组,在汇编程序里要方便地访问,可把数组的首地址赋给辅助存放器,利用辅助存放器即可实现数组的有效访问。5、语言子程序中存放器的使用图7-2显示把输入值存放到堆栈中的方式,以及汇编语言函数如何访问它们。BP存放器用作访问堆栈中的值的基址指针。调用汇编语言函数的程序必须知道输入值按照什么顺序存放在堆栈中,以及每个输入值的长度(和数据类型)。5、语言子程序中存放器的使用5、语言子程序中存放器的使用在汇编函数代码中,C样式函数对于可以修改哪些存放器和函数必须保存哪些存放器有着特定的规那么。如果必须保存的存放器在函数中被修改了,那么必须恢复存放器的原始值,否那么在执行返回发出调用的C/C++程序时也许会出现不可预料的结果。如表7-3所示,被调用的函数必须保存BX,DI、SI、BP和SP存放器,这就要求在执行函数代码之前把存放器的值压人堆栈,并且在函数准备返回调用程序时把它们弹出堆栈。这通常是按照标准的开头和结尾格式完成的。5、语言子程序中存放器的使用6、入口参数和返回参数的约定当被调用汇编语言子程序有值返回调用它的C/C++程序时,这个值是通过AX和DX存放器进行传递的,对于小于等于32位的数据扩展为32位,存放在AX存放器中返回;返回值是32位,那么高16位存放在DX存放器中,低16位存放在AX存放器中。更大字节数据那么将它们的地址指针存放在AX中返回。返回值,被调用的汇编模块对于任何返回值使用以下存放器,如表7-4所示:6、入口参数和返回参数的约定6、入口参数和返回参数的约定由于任何类型进行参数传递时都扩展成32位,程序中没有远、近调用之分,所有调用都是ZH位的偏移地址,所有的地址参数也都是32位偏移地址,在堆栈中占4个字节。图7-3给出了采用C++语言调用协议的堆栈示意图。6、入口参数和返回参数的约定6、入口参数和返回参数的约定调用汇编语言函数的C或者C++程序必须知道用于把输入值传递给汇编函数的正确格式,以及如何处理任何返回值。1〕使用整数返回值最根本的汇编语言函数调用把32位整数值返回到AX存放器中。调用函数获得这个值,它必须把返回值作为整数赋值给C变量;C/C++程序生成的汇编语言代码提取存放在AX存放器中的值,并且把它传送到分配给C变量名称的内存位置(通常是堆栈中的局部变量)。这个C变量包含来自汇编语言函数的返回值,可以在整个C/C++程序中正常的使用。6、入口参数和返回参数的约定2〕使用字符串返回值处理返回字符串值的函数要困难一些。和返回整数值到AX存放器中的函数不同,这种函数不能把整个数据字符串返回到AX存放器中(当然,除非字符串的长度是4个字符)。对于字符串返回值的作法是,返回字符串的函数返回指向字符串存储位置的指针。调用这个函数的C或者C++程序必须使用指针变量保存返回值。然后可以通过指针值访问字符串。如图7-4所示。字符串值被包含在函数的内存空间之内,但是主程序可以访问它,因为函数内存空间包含在主程序的内存空间之内。函数返回的32位指针值是字符串开始位置所在的内存地址。在C/C++程序中处理字符串值时,记住字符串必须使用空字符结尾。C和C++语言在变量名称前面使用“*〞说明这个变量包含一个指针,可以创立指向任何数据类型的指针。但是对于字符串,想要创立指向数据类型CHAR的指针。6、入口参数和返回参数的约定图7-4使用字符串返回值6、入口参数和返回参数的约定但是,默认情况下,C/C++程序假设函数的返回值是整数值。必须通知编译器这个函数将返回字符串的指针。创立函数调用的原型将完成这个任务。原型在使用函数之前定义函数格式,以便C编译器知道如何处理函数调用。原型定义函数需要的输入值的数据类型,还有返回值的数据类型。不定义特定的值,只定义需要的数据类型。【例7-6】利用C调用实现S/2^N公式计算的汇编子程序EXTERNINTCHU(INT,INT); MAIN() {PRINTF(“%D\N〞,FUN(1024,6)); MODELSMALL CODE PUBLICFUN CHUPROC PUSHBP MOVBP,SP MOVAX,[BP+4] MOVCX,[BP+6] SHRAX,CL POPBP RET CHUENDP END【例7-7】试编写一个供

温馨提示

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

评论

0/150

提交评论