zoukankan      html  css  js  c++  java
  • NASM网际编译器手册(八)

    第七章 写16位代码(DOS,Windows3/3.1)

    这章主要介绍写MS-DOS或Windows3.x下运行的16位代码是遇到的方法.主要说明如何将程序连接成.EXE或.COM
    文件,如何写.SYS设备驱动程序,如何提供汇编语言和16位C编译器和Borland Pascal之间的接口.


    7.1 生成.EXE文件
    任何在DOS下运行的大型程序都在做为一个.EXE文件来构造:只有.EXE文件需要跨过64K段的内部结构.Windows
    程序由于不支持.COM格式也必须构造成.EXE文件.通常你生成.EXE文件时用obj输出格式生成一个或多个obj文件.
    然后用连接器将它们连到一起.然而,NASM也可以生成用于bin格式的简单的DOS.EXE文件(用DB和DW来构造.EXE
    文件头).和一个支持这个的宏压缩包.感谢Yann Guidon对这个代码的描述.
    NASM也支持本了的.EXE文件,做为将来版本的另一种输出格式.


    7.1.1 用obj格式生成的.EXE文件
    这一段描述了通过.OBJ文件连接成.EXE文件的方法.16位程序语言是用一个适合的的连接器压缩的;如果把你没有
    话,这有一个叫VAL的自由连接器,来自x2ftp.oulu.,fi的LZH文档格式.一个LZH文档可以在找到.这还有
    一个叫FREELINK的\'free\'连接器(虽然它不带源码),可以从www.pcorner.com上找到,另外还有DJ Delori写的djlink可
    以在www.delorie.com上找到.当将几个.OBJ文件连接进一个.EXE文件中时,你应该保证这些obj文件中的一个为程序
    定义了开始.(用..start指定符定义,见第6.2.6节)如果没有模块定义起始点时,则连接器将不知道输出文件头中入口
    给定的值;如果定义过多的起始点,连接器也不知道会用哪一个.这里给出呈个用NASM源码编译成.OBJ的文件和连
    接器连接成.EXE文件的例子.这解释了定义一个堆栈,初始化段寄存器,定义开始点的基本规则,这个文件也包含在
    NASM文档中的test子目录中,名字为objexe.asm:
    segment code
    ..start: mov ax,data
    mov ds,ax
    mov ss,ax
    mov sp,stacktop
    初始化部分将DS设为数据段,并将SS和SP初始为提供堆栈的顶部.注意将一个指定移入SS时,将禁止中断,所以在
    SS和SP间取数时和没有堆栈执行时将不会生成堆栈.指定符..start定义了这个代码的开始点,这也是说这个点将为
    结果执行文件的输入点.
    mov dx,hello
    mov ah,09
    int 0x21
    上面的主程序:将一个欢迎信息指针将入DS:DX(hello是一个隐式相关的段数据,将在设置代码中送入DS中,所以指
    针是有效的)并调用DOS的显示字串命令.
    mov ax,0x4c00
    int 0x21
    这将用另一个DOS系统调用终止程序.
    segment data
    hell db \'hello,world\',13,10,\'$\'
    数据段包含了我们想要显示的字串.
    segment stack stack
    resb 64
    stacktop:
    上面的代码定义了一个包含64个字节非初始化的堆栈段,然后将指针移到它的顶端.堆栈段定向符定义了一个名
    字为stack的堆栈,类型为STACK.对于正确运行程序后者不需要,但如果你的程序没有STACK类型的段则连接器将发
    出一个警告信息.将上面的文件编译成.obj文件并连接成一个有效的.exe文件,运行这个文件时会显示\'hello,world\'
    信息并退出.


    7.1.2用bin格式生成.exe文件
    .exe文件格式的简单在于:它可以通过写一个纯的二进制程序并粘在一个32字节头后面来构造一个.exe文件.这个
    头可以简单的用NASM的DB,DW命令来生成,所以你可以用一个bin的输出格式来直接生成.exe文件.
    在NASM的文档中的misc目录下有一个叫exebin.mac的宏文件.它定义了三个宏:EXE_begin,EXE_stack和EXE_end.
    为了用上面的方法生成一个.EXE文件,你可以在你的源码文件中的开始处用%include包含一文件exebin.mac.然后
    你就可以用宏EXE_begin(无参数)来生成文件头数据.为通用的bin格式写代码-你可以用三个标准段:.text,.data和
    .bss.在文件尾你应该用EXE_end宏(也不带参数)来定义一些标记段尺寸的符号,这些符在用EXE_begin生成的头
    代码中被引用.在这种模式下,你可以结束0x100处开始写代码,就象一个.com文件,事实上,如果你除去.exe文件32
    字节的头,你将会得到一个有效的.com程序.所有段基址都是相同的,所以你可以限制到一个64k的程序就象一个
    .com文件一样.注意一个ORG定向符被EXE_begin宏调用,所以你不应该用显式的调用.
    你不能直接引用你的段基址,这将在头中需要一个重定位,这样事情将变得复杂.所以你将CS拷贝出来的方法得到
    段基址.对于你的.EXE文件一个入口,SS:SP将会设置一个2K堆栈的顶部.你可以通过调用一个EXE_stack宏来调整
    2KB的默认尺寸.例如,要将你程序的堆栈尺寸改变到64字节,则可以用EXE_stack 64.一个生成.EXE文件的例子将
    NASM文档的子目录test中做为binexe.asm给出.


    7.2 生成.COM文件
    写一个大型的DOS程序必须写成.EXE格式的,小型的则可以写成.COM格式的..COM文件是纯二进制的,并很容易用
    bin格式生成它.


    7.2.1 用bin格式生成.COM文件
    .COM文件通常在偏100h位置将入(虽然段可以改变).例如在程序右边的100h开始执行.所以写一个.COM程序,你应该
    用下面的源码:
    org 100h
    section .text
    start: ;将代码放到这
    section .data
    ;将数据项放到这
    section .bss
    ;将非初始化数据放到这
    bin格式将.text段放在文件中最前面,所以你可以在要写的代码前定义数据或BSS项并代码将在它属于的文件前结
    事.BSS(未初始化数据)段不在一个.COM文件中占用空间:相反BSS项的地址将用一个超过文件未尾的指针来解决.
    然而你不能在你运行时相信BSS段被初始化为全0.如果编译上面的程序,你应该用这样的命令行参数:
    nasm myprog.asm -fbin -o myprog.com
    bin格式在没有显式输出文件名指定的情况下将生成一个叫myprog的文件,所你必须重载它并给出一个指定文件名


    7.2.2 用obj格式生成.COM文件
    如果你用.COM程序写了很多模块,你可以希望将几个.OBJ文件编译并连接成一个.COM程序,你可以这样做:直接提供你一个输出.COM的兼容连接器(TLINK这样做),或用一个象EXE2BIN的转换程序来使连接器将.EXE文件转成.COM
    文件.如果你这样做,你需要注意以下事情:
    第一个包含开始执行的代码段应该用一行RESB 100h的语句.这可以保证代码会在偏移100h开始,所以连接或转换器在生成.COM文件时将不必调整文件中的引用地址 .其它的编译器用一个ORG定向符来达到这个目的.
    但在NASM中的ORG是一个指定bin输出格式的定向符,这不意味着它不能做与MSAM兼容编译所做的事.
    你不必定义一个堆栈段.所有你定义的段都应该在一个相同的段中,所以你的代码或数据每次引用一个符号偏移时
    ,所有偏移都是对相同的段基址的偏移.这是因为一个.COM文件被取出时,所有段寄存器都包含相同的值.


    7.3 生成.SYS文件
    MS-DOS设备驱动程序-.SYS文件-是纯二进制文件,与.COM文件相似,除了它们是在初始0开始而不是100h.然而,如果
    你用bin格式写了一个设备驱动程序,你不需要ORG定向符,因为对bin默认开始点为0.相似的,如果你用一个obj,你
    不必在代码段开始处用RESB 100h..SYS文件用一个头结构开始,包含工作驱动程序里指向不同的例程.这个结构在
    代码段的开始处定义,虽然这不是真正的代码.关于.SYS文件更多的信息和头结构中的数据,一本常见问题集将在
    新闻组 comp.os.msdos.programmer上找到.


    7.4 对于16位C程序的接口
    这段说明了写关于调用或被调用 C程序的汇编程序的基本规则.为了做到这点,你应该写一个象.OBJ文件的典型汇
    编模块,然后用它连接到你的C模块来生成一个混和语言程序。


    7.4.1 外部符号名字
    C编译器的常规为:所有定义的全部符号名字(函数或数据)用一个下划线加上名字来在C程序中出现。所以,一个
    C程序员想象在汇编语言程序的printf函数将为_printf。这意味在你的汇编程序中,你可以定义一个不带下划线的
    外部符号并不必和C符号冲突。如果你觉得下划线不方便,你可以定义宏来代替GLOBAL和EXTERN定向符:
    %macro cglobal 1
    global _%1
    %define %1 _%1
    %endmacro
    %macro cextern 1
    extern _%1
    %define %1 _%1
    %endmacro(这些宏格式一次只带一个参数;一个%rep构造可以解决这个。)
    如果你定义一个外部符号如:
    cextern printf
    然后宏将扩展为:extern _printf
    %define printf _printf
    上面你可以象一个符号引用printf,预处理器将前导下划线符号放在需要的地方。cglobal宏也以相同的方式工作。你必须在定义一个符号前用cglobal,但你如果用GLOBAL便可以必须这样做。


    7.4.2 内存模型
    NASM不直接支持不同C内存模型;你必须跟踪你要写的哪一个。这意味着你必须做下面的事:
    用一个单代码段的模式(tiny,small和compact),函数是near型的。这意味着将函数做为参数存入数据段或压入堆栈
    时,函数指针将是一个16位并包含offset域(CS寄存器不会改变它的值,并给出函数全地址中的段部分),函数将
    用序号near CALL指令调用并返回RETN(在NASM中等价与RET)。这就有两种相同的方法,你可以在代码里返回一
    个RETN,也可以用near CALL指令调用外部C程序。
    用多个代码段的模型(medium,large和huge),函数为far的,这就是说函数指针是一个32位长(包含一个16位的段
    值加16位的偏移),函数用CALL FAR调用 (或CALL seg:offset)并返回RETF,并且用CALL FAR来调用外部程序。
    在用一个单一数据段(tiny,small和medium)的模型中,数据指针为16位长的,只包含一个偏移域(DS寄存器不改变它
    的值勤,并经常给出整个数据项地址的段部分。)
    在用多个数据段的模型号中(compact,large和huge),数据指针是32位长,由一个16位的偏移和16位的段。你应该注
    意不能修改你的程序,在后面没有存它时。但ES对你用来访问32位数据指针的内容是自由的。
    大内存模式允许单数据项超过64K尺寸。在其它所有的数据模型中,你可以通过用给出的偏移值做算术运算来
    访问整个数据项,一个段的值是否存在;在大模型中,你必须注意你的指针的数学运算。
    在很多内存模型中,存在一个默认的数据段,整个段地址将保存在DS中在整个程序中。这个数据段做为堆栈
    段保存在SS中,所以函数的本地变量(被存在堆栈中)和全局数据项存在其它段中不用改变DS可以简单被访问。
    通常大数据项被存在其它段中。然而,有些内存模型(通常不为标准的)允许假定SS和DS保存了相同的值被移去。在后面例子中注意函数的本地变量。
    在一个单代码段的模型中,段被定义为_TEXT,所以你的代码段必须用这个名字来连接到主代码段中相同的位置。在一个单数据段的模型中用一个默认的被叫_DATA的数据段。

    7.4.3 函数定义和函数调用
    下面列出16位程序中的C调用。在下面的描述中,调用和被调用 指调用函数和被调用函数。
    调用将函数参数压入堆栈,在剩下的顺序中一个接一个(从右到左,所以第一个指定的参烽将最后被
    压入堆栈)。
    调用然后指行一个CALL指令来将控制传给出被调用者。
    这个CALL为near还是far取决于内存模型。
    被调用者接收控制后,(虽然这并不必须,在函中不用访问它们的参数)开始将SP存入BP中以便可以用BP做为一个基址来在堆栈中找它们的参数。然而,调用者也可以做这个,所以调用部分的常规状态是BP必须
    对任何C函数为可保存的。因此对被调用者,如果它做为一个frame指针设置时,必须
    首先将前一个值压入堆栈。被调用者可以访问它的参数来引用BP。在[BP]中的值保存BP压入堆栈前的值;下一
    字在[BP+2],保存部分返回地址的偏移部分,并通过CALL来隐式压入堆栈。在一个小模式(near)的函数中,返回地
    址的段部分将存在[BP+4]中,并参数存在[BP+6]中。函数的最左边参数,将被最后压入堆栈,在从BP开始的偏移
    是可以访问的;后面的其它值将会比偏移在。在此,象printf这样的函数将带很多的参数,并将参数按相反顺序压
    入堆栈意味着在那个位置找到第一个参数,然后告诉它剩下参数的数量和类型。被调用者可以减小SP的值,以便
    为本地变量分配堆栈空间,将从BP开始访问负偏移.
    被调用者,如果它希望返回一个值给调用者,应该将值保存在AL,AX或DX:AX中取决于值的尺寸。浮点数有时(
    取决于编译)将在ST0中返回。一旦被调用者完成处理,它将从BP中恢复SP,如果它分配了本地空间,弹出BP以
    前的值,并通过RETN或RETF返回,这取决于内存模型。当调用者重新从被调用者得到控制时,函数参数将依然
    在堆栈上面,所以它将加一个立即常数到SP上来移除它们(取代执行一系列的POP指令)。因此如果一个函数被
    意外的用参数的错误号调用会引起一个原型不匹配的错误,从被调用 者那堆栈仍会返回一个敏感的状态,使我
    们知道这有多少个参数被压入堆栈并被移除。用这个调用规则与Pascal程序的相比较是有意义的(在第7.5.1节
    描述)。Pascal有一个简单的规则:没有函数带有可变的参数。然而被调用知道它应传递多少个参数,并能从它
    自己的堆栈中不分配它们用用一个立即参数来RET或RETF指令,所以调用者不必做这个。参数也是从左到右的
    顺序压入堆栈的,而不是从右到左,这表示一个编译器可以很好的保证关于性能上的顺序。因此,你可以在后
    面定义一个C形式的函数下面是一个small model的例子:
    global _)myfunc
    _myfunc: push bp
    mov bp,sp
    sub sp,0x40 ;64字节的本地堆栈空间
    mov bx,[bp+4] ;函数的第一个参数
    mov sp,bp
    pop bp ;取消上面的"sub sp,0x40"
    ret

    对于large-model函数,你应该用RETF来代替RET,用[BP+6]来代替第一个参数[BP+4]。所以,如果一个参数为指
    针,那么子参数的偏移将会因为内存模型来改变:far指针当做为一个参数被传递时会在堆栈上放4个字节,而near
    指针则会放两个。在另一个处理的结束,将从你的汇编代码中调用一个C函数。你可以象下面所做的:
    extern _printrf
    ;然后,更多的...
    push word [myint] ;我的一个整型变量
    push word mystring ;指向我的数据段
    call _printf
    add sp,byte 4 ;\'byte\'保存空间
    ;然后为数据项
    segment _DATAQ
    myint dw 1234
    mystring db \'This number->%d<-should be 1234\',10,10
    这个small-model汇编代码瑟下面的C代码等价:
    int myint=1234
    print("Thie number->%d<-should be 1234\\n",myint);
    在large model中,函数调用代码看上去象这样,这个例子中假设DS已经为_DATA段的段基址,如果不是的话你
    必须先初始化它:
    push word [myint]
    push word seg mystring ;现在压入段,并且...
    push word mystring ;"mystring"的偏移
    call far _print
    add sp,byte 6
    整数值将一个word放到堆栈上,这是因为large model对整数类型尺寸没有影响。第一个参数(最后压入的)将显示
    ,然而,如果为一个数据指针,则必须包含一个段和偏移部分。段将在内存中存在第二个位置上,但必须先压
    入堆栈。(当然,PUSH DS 将为一个短型的指令而不是PUSH WORD SEG mystring,如果DS做为上面的例子被设置
    时)这将变成一个far调用,因为函数将far调用 在large model;并且SP必须加6而不是4来构造另外的word参数。


    7.4.4 访问数据项
    为了得到C变量的内容,或定义C可以访问的变量,你只需用GLOBAL或EXTERN来定义它们.(名字需要用下划线来
    引导,见第7.4.1节),因此从汇编中访问一个int i形式的C变量:
    extern _i
    mov ax,[_i]
    为了定义一个C程序能象int j一样从外部访问,你可以这样做(确信你在一个_DATA段中汇编,如果需要):
    global _j
    _j dw 0
    为了访问一个C的数组,你需要知道数组元素的尺寸.例如,int变量为2个字节长,所以一个C程序象int a[10]定义一个
    数组,你可以用mov ax,[_a+6]来访问a[3].(字节偏移位置6由数组偏移3乘上数组元素的尺寸2得到).在16位的编译器
    中C的基本类型尺寸为:char为1,short和int为2,long和float为4,double为8.为了访问一个C数据结构,你需要从知道你感
    兴趣结构基位址中得到偏移.你也可以将C结构定义转换为NASM的结构定义(用STRUC)或象上面一样计算一个偏移
    来做到这一步.为了做到上面这些,你应该读一个你的C编译器手册来找出它是如何组织数据结构的.NASM用它自己
    STRUC宏不指定结构成员的偏移,所以如果C编译器产生偏移时,你必须在自己指定它.你可以发现如下的结构:
    struct {
    char c;
    int i;
    } foo;
    为四个字节长而不是3个字节,因为int域为2字节对齐.然而这个特性在C编译器是可以配置的,或用命令行
    参数#pargma,你也必须找出你自己的编译器是如何做的.


    7.4.5 c16.mac:16位C接口的帮助宏
    在NASM文档的misc目录下,有一个叫c16.mac的宏.它定义了三个宏:proc,arg和endproc.这些是为用C形式过程定义
    的,在保存调用规则的轨迹方面它们会自动做很多工作.一个用宏来操作的汇编函数例子如下 :
    proc _nearproc
    %$i arg
    %$j arg
    mov ax,[bp+%$i]
    mov bx,[bp+%$j]
    add ax,[bx]
    endproc
    这里定义的_nearproc将为一个带2个参数的函数,第一个(i)为一个整数,第二个(j)为一个指向整数的指针.它将返回
    i+*j.注意arg宏用了一个EQU做为表达式的第一行,因为在宏调用前的标号将会扩展第一行宏.EQU的工作是定义
    %$i为从BP的一个偏移.一个上下文相关本地变量的使用:本地的上下文相关将审美观点proc压入堆栈,而用endproc
    宏弹出堆栈 ,所以相同的参数名字可以在后面的过程中使用.当然你不必这样做.宏对于near函数设置了默认的
    过程代码(tiny,small和compact-model代码),你可以用%define FARCODE来生成far函数( medium,large和huge-mode代
    码).这将通过endproc改变return指令的类型,并改变参数开始点的偏移.宏设置包含在本质上不从属于数据指针是
    近还是远.arg可以带一个可选的参数,给出参数的尺寸 .如果没给出尺寸便设为2,因为象许多函数参数一样为int类
    型.large-model与上面相同:
    %define FARCODE
    proc_farpoc
    %$i arg
    %$j arg 4
    mov ax,[bp+%$i]
    mov bx,[bp+%$j]
    mov es,[bp+%$j+2]
    add ax,[bx]
    endproc
    因为j为一个远指针,所以arg宏的参数被定义成了尺寸为4的参数.当我们从j取数据时,我们必须取出一个段和一个
    偏移.


    7.5 对于Borland Pascal程序的接口
    Borland Pascal程序的接口与16位C程序的接口相似.不同的是:对于C程序接口的前导下划线对于Pascal不需要.
    内存模型经常为large:函数为far,数据指针为far,并且没有数据项能超过64K长.(事实上,一些函数是near,而且那些对
    一个Pascal单元来说为本地的函数,永远不会在外部调用它所有Pascal调用的汇编函数,和所有汇编程序都能调用
    Pascal函数为far型的.)然而所有在Pascal程序定义的静态数据会在一个默认的数据段中,当控制被传到你的汇编代
    码时,它们的段地址将会在DS中.但当默认的数据段为本地变量时这将不成立(它们在堆栈段)并且支态的分配本地
    变量.所有数据指针将是far的.函数调用规则不同-如下描述.一些数据类型如string存储也不同.
    这将限定你们允许使用的段名字-Borland Pascal将忽略在一个段中的定义的代码和数据,这个段的名字不符合上面
    ,具体描述见下面.


    7.5.1 Pascal调用规则
    16位的Pascal调用规则如下 ,下面的描述中,词调用 者与被调用者是指做调用工作的函数和被调用的函数.
    调用者将函数的参数一个接一个按正常的顺序压入堆栈(从左到右,所以第一个参数将被首先压入堆栈).
    调用者然后执行一个far CALL指令将控制传给被调用者.
    被调用者接到控制,开始(虽然事实上不需要,在函数中不必访问它们的参数)将SP入到BP中,以使BP可以做
    为一个基指针来找出堆栈上它们的参数.然而,调用者也可以正确的做这个,所以调用规则的部分状态为BP必须被
    一些函数保存.因此被调用者,如果它将BP做为一个frame指针设置的话,必须将前面的值压入堆栈.
    被调用者可以参照BP来访问它的参数.[BP]中的值将为它压入堆栈前的值.在[BP+2]中将保存返回值的偏移部分,而
    [BP+4]中则保存段值.参数值从[BP+6]开始.函数最右边的参数最后压入堆栈,可以从BP中得到这个偏移;其它后面的
    值则会比偏移大.
    被调用者可能希望以后减小SP的值,以便为本地变量分配堆栈空间,这些变量将从BP中得到负的偏移来被访问.
    被调用者,如果它希望返回一个值给调用者,应该将值丰入AL,AX或DX:AX这取决于值的尺寸.浮点数的值存在ST0中.
    Real(Borland的本身定义的浮点数据类型不能直接被FPU处理)类型的结果将存在DX:BX:AX.对于字串结果的返回值
    ,调用者将在压入参数前将一个临时字串的指针压入堆栈,并且被调用者将在这个位置放入返回的字串值.字串不是
    一个参数,并且不应该用RETF指令从堆栈中移去.一旦被调用者完成处理,如果它分配本地堆栈空间,它将从BP中
    恢复SP,然后弹出BP的值勤,并通过RETF返回.它用一个带立即参数的RET格式来给出要弹出堆栈的字节数.这使从
    堆栈中移除的参数对返回指令产生一个边界反映.
    当调用者重新从被调用者那里得到控制时,函数的参数可能已经从堆栈中移去,所以它不需要做什么工作.因此你
    可以象下面一样定义一个Pascal形式的函数,带两个整型参数:
    global myfunc
    myfunc: push bp
    mov bp,sp
    sub sp,0x40 ;64个字节的堆栈空间
    mov bx,[bp+8] ;函数的第一个参数
    mov bx,[bp+6] ;函数的第二个参数
    ;一些代码
    mov sp,bp ;取消"sub sp,0x40"
    pop bp
    retf 4 ;参数的尺寸为4
    在一个进程结束时,从你的汇编代码中调用一个Pascal函数,可以用下面的代码:
    extern SomeFunc
    ;后面下面
    push word seg mystring ;现在将段压入堆栈,
    push word mystring ;"mystring"的偏移
    push word [myint] ;我的变量中的一个
    call far SomeFunc
    这等于Pascal代码:
    procedure SomeFunc(String: PChar;Int: Integer);
    SomeFunc(@mystring,myint);


    7.5.2 Borland Pascal段命名限制
    由于Borland Pascal内部单元文件格式与OBJ完全不同,当连接时,从一个OBJ文件读取和理解相关信息时将是一
    个写生的过程。然而一个目标文件连接到一个Pascal程序时将必须遵守一些限制:
    过程和函数必须为一个叫CODE,CSEG,_TEXT的段。
    初始化数据必须在一个叫CONST或_DATA的段。
    未初始化的数据必须在一个叫DATA,DSEG,_BSS的段。
    另外一些在目标文件中的段将完全被忽略。GROUP定向符和段属性也将被忽略。


    7.5.3 在Pascal程序中用c16.mac
    c16.mac宏文件在第7.4.5节描述,也可以用来写一个被Pascal程序调用的函数,如果你用%define PASCAL时。这个
    定义保证了函数为far(它执行FARCODE)并使过程返回一个用操作符生成的指令。定义PASCAL不会改变计算参数
    偏移的那些代码;你必须用相反的顺序定义函数的参数。如:
    %define PASCAL
    proc_pascalproc
    %$j arg 4
    %$i arg
    mov ax,[bp+%$i]
    mov bx,[bp+%$j]
    mov es,[bp+%$j+2]
    add ax,[bx]
    endproc
    这定义了与第7.4.5节中的例子在概念上相同的例程:它定义了一个带2个参数的函数,一个整数和一个指向整数
    的指针,将返回整数的和与指针的内容。与large-model C版本的唯一区别为定义PASCAL来代替FARCODE的定义
    ,参数将以相反的顺序定义?

  • 相关阅读:
    Jenkins获取运行job的用户名(在构建历史中展示构建人)
    Android -tool工具UIautomatorviewer提示“不能让屏幕黑屏”
    转: 谈谈关于内存的一些心得体会
    IP地址,子网掩码划分(转)
    重定向子进程控制台程序的输入输出
    正则表达式(1)
    Log4Net使用指南(转)
    使用wireshark抓本机之间的包(转)
    VirtualBox开发环境的搭建详解(转)
    SxsTrace工具使用方法(转)
  • 原文地址:https://www.cnblogs.com/cnlmjer/p/4099879.html
Copyright © 2011-2022 走看看