[=)
1996年 9月 火控雷达技术 第25卷
多种语言混合编程技术‘
l (西安 电子I程研究所 西安 710100)
A f
【摘要】 混合语言煽程是一门高衄程序设计,它不仅涉及剐程序语言本Jl"的结
构,更主要的是它涉及王{各种语言之问相互兼容的界面接口。本文介绍1这种技术的
基本原理,讨论j FORTRAN、C和BASIC之问相互接口的原则、数据转换规则以
及相互通讯的方法,还讨论 j它们和汇编语言之间的接口技术。
关键调。扁每 兼容 接口技术
1概述 骺佬京 哪
程序设计语言自诞生以来,一直在不断地发展和演变。在当今不下数千种的程序设计语言
中,只有其中很少一部分得到了广泛的应用,例如 Basic、FORTRAN、Pascal、C、Cobol等。这
些语言由于应用比较广泛,因而产生了大量的子程序库。但各种语言有其自身的特色和功能一
有着不同的适用场合,如FORTRAN程序适用于进行大型的科学计算}c语言适用于开发系
统程序,编写用户界面l而Cobol语言则适用于商业领域。当需要精确地控制某个特殊的硬件
设备时,必须用汇编码才能满足高效实时的要求。运用多种语言混合编程就是为了充分利用机
器资源,兼用各种语言的特色和功能之长,继承人类已有的优秀软件的开发成果。
FORTRAN是世界上出现最早的高级语言,专门用于大型的科学计算,至今已有4O余年
的历史,在FORTRAN不断发展和演变的过程中,进行过多次的改版和更新,现已发展成为功
能强大的语言之一,目前的FORTRAN已具备了结构化程序设计的功能。在FORTRAN语言
发展演变的4o余年问,人类研究和开发了许多快捷有效的算法,这些算法几乎都有FOR—
TRAN语言的版本。而且在许多科技文献和科技论文中,几乎千篇一律地用FORTRAN语言
来描述其算法的实现,FORTRAN语言的子程序库随处可见。正因为这样,学习和掌握 FOR—
TRAN语言与其它语言的接口方法,以及从其它语言中调用FORTRAN子程序具有实际的
意义。
Microsoft在实现FORTRAN的编译器时考虑到了与C的兼容性,因而在C和 FOR—
TRAN之间很容易实现接口。事实上,对于_个用C语言编写的函数,用C编译器CL编译后
· 本文于1996年3月15日收到
60
维普资讯 6D -b~
1996 年 9 月 九拉唱tì在技术 第 25 卷
.,
多种语亩泪合编程技术.
学春化 TPJI2
瞄霄蛐阳
hnnl (西安也于.:!.程研究所 4自费 710100)
混合婚古编<<Æ叩门高姐税序设计,宫不仅涉及到程序话古本舟的结
构.史主』酷的是它唠Â剑各种幡古之间相互兼容的Jf.幽梯口。本文仲绍丁这种技术的
&.本原础,讨论 1 FORTRAN、C 和 BASlC :t. i可相互梯口的原则、戴据特棋规则以
及相互通讯的方法.il:.讨论丁它们和汇搞幅古之间的接口技术.
蛐调, ø 主主坐立技术
懈的l 概述
山附
程序设计语言自诞生以来,…直在不断地发艇和演变.在当今不下数千仲的程序设计话E宫
中,只有其中很少一部分得到了广泛的应用,例如 &oic 、 FORTRAN、 Pascal 、C、Cobol 等。这
些语ì!f由于应用比较广夜,因而产生了大量的子稳序廊,但各种语育有其自身的特色和功能.
有着不同的适用场合,如 FORTRAN 锐序适用于进行大刻的科学计算,C 谱窗适用于开发系
统程序,编写用户1f.阁,iIii Cobol 话ì!f则适用于商业领域.当需要精确地控制某个特殊的硬件
设备时,必须用汇编码才能满足商效实时的要求.滋用多种语商根舍编程就是为了充分利用机
器资镖,兼用各种语言1的特色和功能之t是,继承人类已有的优秀软件的开发成果.
FORTRAN ~盖世界上出现最早的高级谱亩,专门用于大型的科学忡算.~今已有 40 余年
的历史,在 FORTRAN 不断发展和演变的过程中,进行过多次的政版和更新,现已发展成为功
能强大的语言之一,目前的 FORTRAN 已具备了错构化程序设汁的功能.在 FORTRAN 滔育
发箴演变的 404如年间,人类研究和开发丁许多快挺有效的算法.这些算法几乎都有 FOR
TRAN 谣言的版本。而且在许多科技文献和秘技论文中,几乎千篇一律地用 FORTRAN 语言
来描述其算法的实现 .FORTRAN 语富的子程序障随处可见, JE倒为这样,学习和掌搅 FOR
TRAN 请寄与其它语言的接口方法,以及从其它诵宫中调用 FORTRAN 子稳序具有实际的
意义.
Mìcroooft 在实现 FORTRAN 的编译攒时考虑到了与 C 的兼容性,因而在 C 和 FOR啕
TRAN 之间很容易实现搂口,事实上,对于一个朋 C 语言编写的函数,用 C 编译器 CL 编译后
·本文于 1996 年 3 月 15 日收到
60
第3期 事春化 多种语言混合蝙程技采
可以生成C的函数库,如果对CL增$11/Gc选择项来进行编译,则可生成FORTRAN的函数
库。
BASIC最初是作为一种解释性语言出现的,它用一种与其它语言都不兼容的方法实现初
始化和程序的装入,BASIC对字符串和数组进行动态管理.这些管理子程序是其它语言中不
具有的,更主要的是其它语言不能访问BASIC的管理子程序,因而在BASIC与其它高级语言
之间很难实现接13。BASIC可以调用汇编语言子程序,但实现方法是将汇编后的机器代码嵌
入到了BASIC主程序中,因为不能将其它语言的目标代码嵌入到BASIC程序中,因而解释
BAsIc不能调用其它高级语言的子程序。
Quick BASIC 4,0的出现完全改变了这种状况。它是编译型的,支持多模块编译和连接,
支持混合语言编程。Quick BASIC没有抛弃解释BASIC的任何东西,它不但佩留了而且增强
了其解释特性 由于这个原因,Quick BASIC仍使用一套与解释BASIC相兼容的方法来实现
程序的初始化以及字符串和数组魄动态管理,这样,任何与BASIC混合编程的语言都必须用
BASIC做主程序。BASIC可以调用用其它语言编写的子程序,但其它语盲却不能调用BASIC
子程序(或SUB过程),如果要这样做,则必须用BASIC作主程序,调用另一种语言的子程序,
被调用子程序在内部反调用BASIC,以这种方式来实现到BASIC的间接调用。Microsoft推荐
的调用BASIC的方法如图 1,l示。
BASIC主程序 FORTRAN子程序
匝 1 FORTRAN语言调用队 g【c干程序
混合语言编程是指用一种语言写的程序可以调用用另一种语言写的程序,而不是指在一
个函数或子程序内部混合多种语言去编写程序。例如一个用FORTRAN语言编写的主程序可
以调用一个用C语言编写的函数,混合调用必然包含多个模块,不可能用弼一个编译器编译
所有的源代码模块,只能用相应的编译程序分别编译不同语言的各个模块,然后用连接器把不
同的日标文件连接起来形成可执行文件
这种混合调用包含若干特殊的技术,为了说明这些问题的特殊性,我们考查一个FOR—
TRAN程序调用C程序的过程,假设主程序用 FORTRAN语言编写,而披调用子程序 Itme
用c语言编写.如图 1.2所示。
61
维普资讯
第 3 期 非暴化. ?种幡古说舍毓程技术
可以生成 C 的阁数靡,如果对 CL 增加!Gc 选择项来进行编译,则可1::成 FORTRAN 的函数
障。
BASIC 最初是作为自叩种解樨性语窗出现的,它用一种与其它语宵都不兼容的方法实现初
始化和稳序的装入 .BASIC 对字符南和数组进行动态管理,这些管班子穰序是其它活育中不
具有的,更主要的是其它谐音不能访问 BA51C 的管理子穰序,因而在 BASIC 与其它高级谣言
之间很难实现接口. BASIC 可以调用汇编谓窗子程序,但实现方法是将汇编后的机糖代码嵌
入到了 BA5IC :È程序中,因为不能将其它谢富的目栋代码嵌入到 BASIC 辍序中,因而解释
BASIC 不能调用其它高级语宵的子模fl!,
Quick BASIC ι0 的出现完全改变了这种状况.它是编译烈的,交椅多模块编译和连接.
交椅混合语IZ编稳。 Quick BASIC 没有抛弃解精 BASI院的任何东阔,它不但保留了而1l增强
了其解串串特性,由于这个原因,Quìck BASIC 仍使用一费与解樨 BASIC 相兼容的方法来实现
程凉的初始化以及字符唱和放组的l动态管理,这样,任何与 BASIC 视台编穆的情宵都必须用
BA5IC 做东德序, BASTC 可以词用用其它话苦苦编写的子穰序,但其它话商却不能调用 BASIC
子程序(或 5UB 过程) ,如果要这样做,则必须用 BJ电51巳作主毯序,调用男叩种语宵的子程序,
被调用于程序在内部反调用 BASIC.以这种方式来实现到 BASIC 的问接调用,Microsolt 推荐
的调用 BASIC 的方怯如朗1. 1 示,
BA51C 主程序 FO/il:TRAN 子程序
初始化代码
CALL FSUB
END
5UB BTEST Static
.
END SUB
SUBROVTINE FSUB
CALL BTEST
•
RETURN
END
鸭 1 FORTRAN 悔古训11\ BASIC 于程序
混合谢富编程是指用一种谱言r~的程序可以调用用jj…种语宵写的程序,而不是指控
f函数或子程序内部温含多种语11'去编写程序 a例如一个用 FORTRAN 语窗编写的主穰序可
以词用一个朋 C 谐音编写的画面数,混合调用必然包含多个模块,不可能用同…个编译糠编译
所有的源代码模块,只能用相应的编译银序分别偏译不同语富的各个模块,然后用连接器抱不
同的目标文件迷撩起来形成可执行文件.
这种混合调用包含着干特殊的技术,为了说明这些问题的特殊傲,我们考资…个 FO民
TRAN 程序调用 C 稳序的过程,假设主程序用 FORTRAN 语窗编写,而被调用子稳序 func
用 cf辑:宫缩写.如因1. 2 所~D
61
1996年 9月 火控雷达技术 第25卷
圜2 FORTRAN语言调用C子程序
这里,我们首先遇到了以下几个问题t
a FORTRAN如何调用C函数。
对FORTRAN编译器来说.它有一个外部过程FUNC,该过程接受三个参数,编译器要
做的只是用实参(实元)代替形参(哑元),并对FUNC作一次外部调用即可,至于FUNC在什
么地方定义,以及FUNC是用什么语言编写的,FORTRAN编译器并不知道,事实上也不需
知道,鄢是连接程序(LINK)的事情
b. FORTRAN是如何将参传递给了C函数,c函数又如何并从何处得到实参的数值。
实际上,参数是通过堆栈传递的,一个程序在运行时至少有一个堆栈,堆栈是每个函数的
公用数据区,通过堆栈传递参数既可行又方便。编译器在编译一个函数调用时,它首先把实参
压入堆栈,然后发出一个 CALL调用。被调用函数在执行时.通过调整堆栈指针,从堆栈上取
得实际参数的数值。这里的关键是,调用程序将参数压入堆栈的顺序与被调用程序从堆栈上的
接收参数的顺序必须一致,参数的类型也必须一致。
c. 向子程序传递实参时.既可传递实参的具体数值,又可传递存放实参变量的地址,不
同的约定产生的结果不同。
从这个简单例子可以看出,在进行混合语言编程时,必须遵守某些规定,这些规定包括:语
言的命名约定、调用约定、参数传递规则、数据转换规则以及在编译时选择适当的存储模式并
与指定的库进行连接等等。其关键技术就是要建立各种语言之闻兼容的接口,在两种语言之间
建立有效的通讯方法,做出符台该语言调用约定的某种形式的说明。
在上面的讨论中,我们忽略了子程序与函数的区别,实际上函数是具有返回值的子程序.
它们在汇编代码级是没有区别的,因为它们都通过CALL指令调用。对于调用程序来说-子程
序调用可以不理睬返回值(实际上返回值无定义),而对于函数调用,调用程序需要处理返回
值,否则,调用虽然完成,但其返回会值将丢失。如果C函数是无返回值的(void类型),则
FORTRAN主程序需要作一次到 C函数的子程序调用(CALL);如果 C函数具有返回值,则
FORTRAN主程序需要对C程序作一次函数调用(在表达式中使用它的名字。)
最后,我们介绍一种探查混合调用技术的方法,即如何击发现一个混合调用的交互过程。
通过这里介绍的方法读者可以考查一个函数是如何被调用的、函数的参数是如何被传递的等
信息。这里介绍两种方法,一种是C和FORTRAN都支持的方法,即用eL或FI,的编译选项
62
维普资讯
199&年 9 月
C
FORTRAN 旅程序
C
CALL FUNC丁 (Pl,P2 , P3l
义雄嘴 lti生,f..
/铃 C 源程序赞/
void func (int pl. int p2.
int p3)
回 2 FORTRAN 禄古训J!l C 于"f:t}于
这里,我们首先激到了以下几个问题,
a , FORTRAN 如何调用 C 函数。
第 25 卷
对 FORUAN 编译梯来说,它有一个外部过稳 FUNC.该过混接受个参数.编译稽要
傲的只是用实参(实元)代替形参(哑元) .并对 FUNC 作一次外部调用即可.Æ于 FUNC 在什
么地方定义,以及 FUNC 是用什么语言编写的,FORTRAN 编译锦并不知道,事实上也不带
知道,那是连接程序(UNK)的事情@
b. FORTRAN 是如何将参传递给了 C 函数 .C 函数又如何并从何处得到实参的数值四
实际上.参数是通过堆械传递的,一个辍序在这行时至少有一个城楼,堆战是每个画数的
公用数据区,通过堆械传递参数既可行义方辙。编译将在编译…个函数调用时,它首先把实参
应入雄梢,然后发出一个 CALL 调用.被调用函数夜执行时,通过调'自堆械指针,从堆械上取
得实际参数的数值。这里的关键是,调用辍序将参数 EE入榄枝的顺序与被调用程序从峨榜上的
接收参数的顺序必须…激.参数的类型且也必须一致。
ι 向子程序传递实参时,既可传递实参的具体数值,又可传递存放实参变量的地址,不
阔的约定产生的纷果不同,
从这个简单例子可以看出.在进行混合谱窗编程时,必须遵守某些规定.这些规定包括 2语
言的命名约定、调用约定、参数传递规则、数据转换规则以及在编译时选择适当的存储模式并
与指定的库激行连接等等。其关键技术就是要建立各种谱言之间兼容的接口.在两种语言之间
建立有娥的通讯方法,做出符合该诸霄调用约定的某种形式的说明 U
在上丽的讨论中,我们忽略了子程序与函数的区别,实际上函数是具有返回慎的子程序嘈
它们在汇编代硝纵然没有Iíi剔的,因为它们都通放 CALL 指令调用自对于调用程序来说,子程
序调用可以不现睬返回值(实际上返回惊无定义h而对于函数调用噜调用程序需要处理返阁
值,否则,调用虽然完成,但其返回会值将丢失。如果 C 函数是无返回缆的 (void 类现) .则
FORT民AN :t程序需要作…次到 C 揭数的子程序调用 CCALLl.如果 C 函数具有返回值噜则
FORTRAN 主程序需要对 C 程序作一次函数调用{在表达式中使用它的名字。〕
最后,我们介绍一种探查混合调用技术的方法,即如何去发现一个饱含调闸的变宜过程。
通过这里介绍的方法读者可以考进一个函数是如何被调用的、函数的参数是如何被传递的等
恼息。这里介绍两种方法,一种是 C 相 FORTRAN 都3峭的方法,即用 CL 或孔的编译逃项
62
哥
第3期 李春化 多种语言混合编程技求
/Fa将高级语言源程序转化为汇编语言程序,然后去研究汇编语言的结构,从中可以发现变量
在存储器中是如何被分配的、参数是如何被传递的 函数调用是如何返回值的、以及子程序是
用什么方式被调用的等内容 第二种方法是设计一个可被高级语言调用的汇编语言子程序接
口,在汇编模块中设置断点指令,在高级语言中调用该汇编过程,生成可执行文件后,用DE—
BUG去调试它,特别是可以设计一个可被BASIC调用的汇编语言模块接口.用各种类型的数
据去做实验,进一步可以仔细研究在BASIC的内部,BASIC是如何通过字符串说明符和数组
说明符来管理字符串和数组的。
本文介绍的多数内容就是笔者利用上面介绍的方法去学习、考查和了解混合语言编程技
术的体会。
本文的主要内容包括两部分,第一部分讨论高级语言间相互调用的方法,内容包括:语言
约定规则、内存模式、参数传递方法、各种语言的内部约定、不同语言之间的数据转换方法等,
最后以一个实倒作为第一部分的结束,该倒子分别用BASIC、C和FORTRAN作主程序,调
用一个FORTRAN子程序FFT来计算一个序列的博里叶变换。第二部分主要讨论高级语言
调用汇编语言子程序的方法,内容包括创建一个与高级语盲的接口所必须的7个步骤以及
BASIC、C和FORTRAN调用汇编语言的程序实例
本文的讨论适合于下列版本及其以上版本的语言系列。Quick BASIC 4.0,Microaoft.
FORTRAN 4.0,MicrosoIt C 5.0
2高级语言之间的混合调用技术
2.1语富约定规则
2.1,1命名约定规则
当把用两种语言编写的程序模块连接在一起时,必须保证同一个变量在两个程序模块中
的名字是一样的。编译器将源文件代码翻译成目标文件(.OBJ)时,目标文件中不仅包含了机
器代码,而且包含了所有公共子程序和变量的名字。名字是以ASCII格式存储的,。命名规刚”
系指编译器在将子程序和变量的名字登记在目标文件中时改变名字的方法。当发生一个混合
语言调用时,必须采用一致的命名约定,这样,连接程序在将各个模块连接成一个可执行文件
(,EXE)时,才能在不同的各个模块中找到相互匹配的名字。若被调用的子程序的名字在各个
目标文件中各不相同,连接器将找不到一个匹配的名字,因而将报告一个无法解决的外部引用
错误。 ·
有两种命名规则,第一种是FORTRAN、Pascal和 BASIC使用的命名规则(这三种语言
使用的命名规则基本相同),该规则将每个字符转换为大写字符记录在文件中。对BASIC的名
字,其后缀的类型说明符(如 、&、』、#、$)被忽略。另一种命名约定规则是C语言使用的规
则,因为c是区分大小写字符的,C编译器不做任何大小写转换,并在每个程序名字前插入一
个下划字符( )。
不论是哪种语言,都对变量名字的长度有所限制,而且每种语言可以识别的字符个数不
同。FORTRAN识别前 31个字符,Pascal识别前 8个字符,BASIC识别前40个字符,C识别
前31个字符。若名字的字符个数超过了可以识别的最大长度,那么超出的字符被忽略。对于
63
维普资讯
串 3 期 学品化 乒种语宫混合幽毒草技术
IFa 将高级语言淑程序转化为汇编请寄程序.然后去研究汇编语言f的结构.从中可以发现变戴
在部储器中放如何被分配的、参费t是如何被传递的‘函数调用是如何返回值的、以及子程序是
用什么方式被调用的等内容。第二种方楼是设计一个可被高级语育调用的汇编语育子程序接
口,在汇编槟块中设置E断点指令.在高级语言中调用该汇编过穗.~成町执行文件后.用。E
BUG 去调试1?;.特别是可以设汁一个可被 BASIC 调用的汇编语言7模块接口,用各种类型的数
撤去做实验.波一步可以仔细研究在日ASIC 的内部 .BASIC 是如何通过字符串说明符和数额
说明符来管混字将串和数锁的。
本文介绍的多数内容就是笔者利用上面介绍的方法去学习、考查和了解混合语富编程技
术的体会.
本文的二n耳内睿包括两部分,第喃M部分讨论商银话窗闯栩空调用的方法.内容包括·语言
约定规则、肉?于模式、参披传递方法、各种语宵的内部约定、不同语育之间的数据转换方法等.
散后以一个实例作为第一部分的错柬.该例子分别用 BASIC 、C 和 FORTRAN 作主程序,调
用一个 FORTRAN 子程序 FFT 来计算一个序列的佛!!叶交换"第二部分班费讨论商级语言
词用汇编语言子程序的方法.内容包括创建一个与高级语宵的接口所必须的 7 个步骤以及
BASIC 、C 和 FORTRAN 调用汇编语言F的稳序实例.
本文的讨论适合于下列版本及其以上版本的话窗~ 1iJ -Quick BASIC 4. O. Microsoft-
f-' FORTRAN 4. O,Microsoft C 5. O.
E
2 高级语宫之间的混合调用技术
2. 1 i茜富约定规则
2. 1. 1 命名的lË规则
当te用两种谱~编写的稳序模块泼接在叫起时,必须保证伺叫个交1置在两个稳序模块中
的名字是一样的 4 编译器将耀文件代码翻译成目标文件ιOB])时.目标文件中不仅包含了机
器代码.而且包含了所有公共子程序和变量的名字.名字是以 ASCII 格式存储的f命名规则"
系指编译糠在将子程序和变擎的名字登记在目标文件中时改变名字的方法,当发生一个混合
话窗调用时,必须采用一兹的命名约定,这样.连接程序在将各个模块连接成一个可执行文件
(. EXEl时,才能在不同的各个模块中找到相互阪配的名字"者被调用的子程序的名字在各个
目标文件中各不相同,越接器将找不到叫个匹配的名字.因而将报告一个无法解决的外部引用
错误。
有两种命名规则,第一种是 FOR'FRAN、 PascaJ 和 BASIC 使用的命名规则(这三种谣言
使用的命名规则黛本相同h该规则将每个字符转换为大写字符记录在文件中"对 BASIC 的名
字.其后缀的类型说明符(如%、&、 l 、辉、$ )被忽略.另一种命名约定规则是 C 语宵使用的规
则.因为 C 是区分大小窍字符的,C 编译器不做任何大小写转换,并在每个程序名字前插入一
个下划字符( ).
不论是哪种语言.都对变:1名字的~度有所限制,而且每种语言可以识到的字符个数不
同 o FORTRAN 识别前 31 个字符 .Pascal 识别前日个字符.BASIC 识别前 40 个字符 .C 识别
前 31 个事符,辛苦名字的字符个数超过了可以识别的最大长度,那么超出的字符被忽略,对于
63
l 996年 9月 火控雷达技术 第25卷
FORTRAN程序,有时为了与其它的FOR2~RAN编译器兼容,可能会使用$TRUNCATE元
命令将名字截短为6个,这时应该使用FORTRAN的属性关键字ALIAS来重新命名变量的
名字。BASIC也提供了ALIAS属性关键字来修改变量的名字。
2.1.2调用约定规则
混合语言编程另一个需要遵守的规则是子程序的调用规则。调用规则规定了子程序之间
传递参数的顺序和接收参数的顺序,这些规则还规定了被调用的子程序在返回之前恢复堆栈
的方法。选择的调用规则不同,编译器产生的执行函数、过程或子程序调用时的机器代码不同,
从被调用程序返回时的机器指令也不同。如果发出调用的过程和被调用的过程采用了不一致
的约定,其结果是导致堆找混乱, 起系统崩溃或死机
有两种调用规则,一种称为C的调用规则,一种称为Pascal的调用规则。两种调用规则都
通过堆栈传递参数,所不同的是参数入找的顺序以及从被调用函数返回时调整堆栈平衡的方
法。
a. C的调用规则
假设有如下的C语言函数原型:
void func(int pl,int p2,int p3);
按C的调用约定调用该函数时,参数按从右 左的顺序(p3,p2,p1)入栈,即参数入栈的顺序
与参数出现在参数表中的顺序相反,最后入找的是函数的返回地址。对于上述的C函数func,
调用它的实际的汇编语言代码为
PUSH P3
PUSH P2
PUSH P1
CAI上 Near Ptr
—
tunc
ADD SP.6
最后一条指令用来调整堆栈的平衡,因为编译器知道传递了多少个参数。
b. pascal的调用规则
如果用C语言的关键字Pascat声明前述的函数,
void pascal tunc(int pl,int p2,int p3){
则必须按照PASCAL的惯倒调用该函数,这时参数按从左一右的顺序(P1,P2,P3)入找,后跟
函数的返回地址。即参数入栈的顺序与参数出现在参数表中的顺序相同。除了入找顺序不同
外,Pascal的调用规则还要求被调用函数必须知道传递了多少个参数,并依此来调整堆找的平
衡,调用 func的汇编语言代码如下:
PUSH P1
PUSH 、P2
PUSH . P3
CAI L Near
—
Ptr func
注意此处没有ADD SP,6指令,这条指令由tune函数结束时的返回指令ret 6代替,以清除堆
栈。
用C语言写的函数缺省地使用C的调用规则,而Pascal、FORTRAN和BASIC都缺省地
64
4
正
维普资讯
1996 年 9 月 九i宫曹ii;.;f!,j;. 第 25 ~
FORTRAN 程序.'何时为了与其它的 FORT民AN 编译稽兼容,可能会使用 $T民UNCATE 元
命令将名字截短为 6 个,这时应该使用 FORTRAN 的剧性关键字 ALIAS 来重新命名变蟹的
名字. BASIC t且提供了 ALIAS 鹏性关键字来修改变健的名字。
2. 1. 2 调用约定规则
视台语百缩银另一个需要遵守的规则是子程序的词用规则。调用规则规定了子稳序之|可
传递参数的顺序和接收参数的顺序,这些规则还规定了被调用的矛程序在返回之前恢复堆械
的方法.选择的调用规则不同,编译器产生的执行函数、过穰或子穗序调用时的机器代码不间,
从被调用程序返回时的机器指令也不同,如果发出调用的过程和被调闸的过程是采用了不一致
的约定,其结果是导致壤愤混乱,引起系统崩愤或死机。
街两种调用规则,一种称为 C 的调用规则,一种称为 Pasc圳的调用规则。两种调用规则都
通过堆钱传递参敷.所不同的是参数入峨的顺序以及从被调用函数返回时调整堆钱平衡的方
法"
•. C 的调用规则
假设街如下的 C 语育函fI:原到自
void func (int pl, int p2 .int p3l I
按 C 的调用约定调用该函数时,参数按从布→左的顺~ (p3 .p2 .pU入钱.即参数入梭的顺序
与参敷出现在参数表中的顺序相反,最后入攒的是函数的返回地址。对于上述的 C 函数 func. 4
调用它的实际的汇编语言代码为 正
PUSH P3
PUSH P2
PUSH Pl
CALL Ptr lunc
ADD
缎后…条指令用来调楚楼梯的平衡,因为编译糯知斌传递了多少个参数。
b. 的调附规则
如果用 C 语育的关键字 Pascal 商明前述的函数,
void (int p1. int p2 .int p3) I
则必须按照 PASCAL 的惯例调用该画面数,这时参数按从'!i咱有的顺序(Pl, 入愤.厨跟
函数的返回地址,即参数入梭的顺序与参败出现在参数表中的顺序相同,除了入战顺序不同
外.Pascal 的调用规则还要求被调用函数必须知道传递了多少个参数,并依此来调披堆攒的平
衡,调用 funç 的汇编谛ìJ代码如下:
PUSH Pl
PUSH 、 P2
PUSH _ P3
CA Near Ptr func
放意此处没有 ADD SP , 6 指令,这条指令由 lunc 函数结束时的返回指令 ret 6 代替,以清除堆
钱。
用 C 语霄写的函数缺省地使用 C 的调用规则,而 Pascal、FORTRAN 和 BASIC 都缺省地
64
第3期 卓春化 多种语言混合编程技术
使用Pascal的标准调用规则。每一语言均可通过子程序的头部声明或命令行来改变这种调用
约定(调用模块中的约定和被调用模块中的约定)。一般地,采用被调用程序中的约定较为容
易.例如,FORTRAN函数可以使用C约定来调用 C函数,C函数也可使用 FORTRAN(Pas—
ca1)的约定来调用 FORTRAN子程序或函数。
2.1.3参数传递方法
调用子程序和被调用子程序除了必须遵守统一的调用约定外,还必须知道参数是如何传
递的。有两种参数传递方法:通过值传递和通过引用传递。通过引用传递实际上是传递变量在
内存中的地址。由于在基于80X86系列微机上,变量的地址有近地址与远地址之分,因而Mi—
crosoft编译器支持三种参数传递方法
a. 通过近地址传递
该方法传递的是一个变量的近地址(偏移量),被调用子程序可以直接访问该变量本身。子
程序对变量所做的任何改变都将反映在调用程序中
b. 通过远地址传递
该方法传递的是一个变量的远地址(32位分段地址),与近地址传递参数的方法不同的是
此方法除了传递变量本身的偏移量外,还要传递变量所在的段值。因此通过远地址传递可以访
问大于64K的数据。
c. 通过值传递
该方法仅传递变量的值或变量的副本,而不传递其地址。使用这种方法,被调用子程序只
知道变量的值而不知道变量的存放地址,因此它只能使用变量的值,而不能访问该变量t对某
个值参的修改不会影响调用程序中相应参数的值。
每种语言都提供了缺省的参数传递方法,一般说来,FORTRAN和 BASIC使用引用传递
参数 对于C,除了使用引用传递数组外,其余的非数组参数都是通过值传递的。BASIC可以
使用任何方法(值、近日l用、远引用)传递参数,但只能使用近引用的方式来接收数据(有关BA—
SIC的特殊性在2.4.2中详细讨论)。每种语言都提供了覆盖缺省方式的方法,例如当FOR—
TRAN子程序带有Pascal或C属性时,则缺省的参数传递方式是通过值传递。
为了更好地理解引用传递和值传递的区别,我们举一个FORTRAN程序的例子:
例2.1:说明引用传递和值传递的区别。
假设我们有一个子程序INCW(N),该子程序将传递给它的实参增 l后返回,当以近引用
的方式(中内模式)传递变量时,程序如下t
INTERFACE TO SUBRUTINE INCW (N)
INTEGER*2 N
END
C
C MAIN PROGRAM
C
.
INTEGER*2 N
N:2
CALL INCW (N)
65
维普资讯
如
,
a
'‘
第 3 期 非J.化 J 种 4苦苦混合编税技术
使用 Pascal 的你准调用规则。每一语言均可l!过子程序的头部声明或命令行来改变这种调Ijj
约定〈调用模土是中的约定和被调用模块巾的约定}。一般地,采用被调用程序阶的约定较为容
易.例如 .FORTRAN 函数可以使用 C 约定来调用 C 函数.C 函数也可使用 FORTRAN (Pas-
ca们的约íE来调用 FORTRAN 子穰序或函数。
2. 1. 3 参数传j单方法
调用手程序和被调用子程序除了必须遵守统一的调用约定外,还必须知道参数是如何传
递的 z 有两种草草数传递方法 d盟:l:t值传 j摄相通过引用传递 ì醒过引1m传递实际上是传递交1盖在
内臂,中的地:,扇子在3盖子 80X86 系列微机上.变最的地址商近地址与远地址之分.因 lñî Mi-
crosoft 编坏器支持三种参撒传递方法。
ι 涌过i丘地址传递
该方法传i璋的是…个变攘的近地址(偏移巍 1 .被调用子程序可以:iï接访问该变量本身。子
银!字对变量所做的任何改变都将反映在调用程序中。
b. 通过远地址传i撞
该方法传i握的想…个变酷的竭地址(32 位分段地址) ,与近地址传递参鼓的方法不闷的是
比方法除了传i且变量本身的偏移最外.还要传递交量所在的段值副网此通过竭地址传i理可以访
问大于 64K 的数据"
C. 通过倪传i单
t衷方法仪传递变量的值或变量的副本,而不传递;tl;地址。使用这种方法,被调用予帮序只
知道变量的值而不知源变燎的帘,政地址,网此官只能使用变攘的值,而不能访问该变嚣,对某
个值参的修改不会影响调用程序巾相应参数的倍。
每种语言都提供了踉省的参数传递方法,一般说来.FORTRAN 和 BASIC 使用引用传递
参数。对于 C.除了使用引用传递数级外 , j革命的非数绷参数都是通过值传递的. BASIC 可以
使用任何方法(值、近引用、远引用}传递参数,但只能使用近引用的方式来接收敷掘〈有关 BA
SIC 的特殊性在 中样细讨论)。每种语言都握供了覆盖缺省方式的方法,例如当 FOR
TRAN 子程序带有 PascaJ 或 C 剧性时. líW峡省的参数传i撞方式是通过值传嫌。
为了更好地理解引用传耀和{自传边的区别,我们举-tFORTRAN 耀序的例-f.
例 2. 1: 说明引用传递和债传递的区别 φ
假设钱们有一个子程序 TNCW(N).该子稳序将传i革给它的实参增 1 后返回‘当以i丘m目
的方式(中内棋式)传i撞变黛时,税序如下:
C
INTERF ACE TO SUBRUTJNE INCW (Nl
INTEGER 传 2 N
END
C MAIN PROGRAM
C
INTEGER 骨 2N
N=2
CALL INCW(N)
65
1996年 9月 火控雷达技术 第25卷
END
SUBROUT INCW (N)
INTEGER*2N
N竺N+ l
RETUEN
END
上述源程序对应的汇编语言代码如下:(中内存模式)
BSS SEGM ENT
N DW ?
BSS ENDS
;主程序的汇编代码
main PROC FAR
push bp
moy bp,sp
sub sD,2
mov WORD PTR N。2
mov ax,OFFSET DGROUP:N
push ax
csll FAR PTR INCW
mov ax,0
cwd
mov sp,bp
pop bp
ret
maiI ENDP
;子程序INCW 的汇编代码
INCW PR0C FAR
I赋值 N一2
;取变量N的偏移地址
}将 N的地址压入堆栈
将出口代码返回给操作系统
push bp
mov bp,sp
mov bx,WORD PTREbP+6] I取实参的地址
inc WORD PTR[bx] ;将实参增 1
pop bp
ret 2 ;清除堆栈
INCW ENDP
上面的汇编语言代码可以用FL的/Fa选择项在中模式下来编译产生,为了说明问题的
实质,我们对它进行了简化和改写。
编译程序在非初始化数据段一BSS为变量N分配空间,在用实参N调用[NCW之前,编
译程序先取变量N的地址,再将该地址压入堆栈。相应地,INCW子程序首先调整堆栈指示器
66
维普资讯
1996 -+ 9 月 义捡嘴边技术
END
SUBROUT INCW(Nl
INTEGER 骨 2N
N=N+1
RETUEN
END
上述源程序对应的汇编语育代码如下: (中内 ff愤z\:.)
BSS SEGMENT
N DW ?
日55 ENDS
,:J::程序的汇编代码
m削n PROC FAR
push bp
mov bp ,sp
sub sp ,2
mov WORD PTR N ,2
mov ax ,OFF5ET DGROUP:N
push ax
call FAR PTR INCW
mov ax ,。
cwd
自10V sp , bp
pop bp
ret
mail ENDP
z子程序 INCW 的汇编代码
INCW PROC FAR
push bp
自10V bp ,sp
t 赋假 N出 2
,自主变量tN 的偏移地址
,将 N 的地址服入堆樵
;将剧口代码返回给操作系统
mov bx ,WORD PTR[bp+S] ,取实参的地址
InC WORD PTR[bx] t 将实参增 1
pop bp
ret 2 $淌除雄饿
INCW ENDP
第 254
上丽的汇编语商代码可以用凹,的IFa 选择项在中横式下来编译产线.为了说明问题的
实质,我们对它进行了简化和改窍。
编译磁序:(E非初始化数据段… B5S 为变量 N 分配空间,在用实参 N 词用 INCW 之前,端
详程序先取变最 N 的地址.再将该地址服入堆锁,相E1地 .INCW 子程序首先调肇雄械指示祷
66
•
'
酬
第3期 享春化 多种语言混合编程技术
指针,然后从堆栈中将实参的地址取到BX中,再将实参增1,因此,从子程序返回时,N的值已
由原来的2变成了 3。
下面我们用显式的值 2去调用INCW(CALL INCW(2))重做这个试验,看看会发生什么
变化?
main PROC FAR
push bp
mov bp,sp
8ub sp,4 ;分配局部空间
lCA【 L INCW (2)
mov. WORD PTR[bp一4];2 ;将值2存放在局部变量中
lea ax,WOTD PTREbp---4] ;取局部变量的地址
push a:K
call FAR PTR INCW
mov ax,0 .
mov sp,bp
pop bp
ret
— —
main ENDP
INCW PROC FAR
push bp
mov bp,sp
mov bx,WORD PTREb~+6]
inc WORD PTR[bx]
pop bp
ret 2
INCW ENDP
观察上面的汇编代码可以发现,不论是用变量作参数还是用表达式作参数,其最终表现都是一
样的,即都通过引用传递参数。
下面是一个通过值来传递参数的试验。
INTERFACE TO SUBROUTINE INCW(N)
INTEGER*2 NEVALUE]
END
C
C MAIN PROGRAM
C
INTEGER *2N
N一2
CAI L INCW (Ny
67
维普资讯
第 3 期 非暴4ι F 种t晶宫混合蝙程拉术
指针,然后从城t!ì中将实参的地址取到日X 巾,再将实参增1.因此,从子程序返回时,N 的值已
由原来的 2 变成了 L
下阁我们用显式的值 2 去调用 INCW(CALL INCW(2川重做这个试验,看看会发生什么
变化T
main PROC F AR
push bp
mov bp ,sp
sub .p ,4 3分配局部空间
,CALL INCW(2)
mov" WORD PTR[bp-4J;2
lea PTR[bp…4J
push ax
ca[[ FAR PTR INCW
自lOV ax ,。
mov
pop bp
ret
m刷n ENDP
lNCW PROC F AR
push bp
盯lOV bp ,sp
mov bx , WORD PTR[b~十 6J
tnC WORD PTR[bxJ
pop bp
ret 2
INCW ENDP
$将值 2 存放在局部交盘中
$取局部变寰的地址
现察上丽的n:编代码可以发现,不论是用变量作参数还是用表达式作参数,真最终表现都是一
样的.即都通过引用传递参数。
下lIìî是一个通过债来传递参数的试验 φ
C
C MAIN
C
INTERFACE TO SUBROUTINE INCW(Nl
INTEGER 幡 2 N[VALUEJ
END
PROGRAM
INTEGER 俊 2N
N=2
CALL INCWCN)"
67
1996年9月 火控雷达技术 第25卷
END
SUBROUTINE INCW (N)
INTEGER*2 N[VALUE]
N—N+1
RET【】RN
END
上述程序中关键字[VALUE]指明参数是 值参的形式传递.这次等价的汇编语言代码
会如下所示:
BSS SEGMENT
N DW ?
BSS ENDS
bp
bp,sp
sp,2
WORD PTR N,2
8x,2
; 直接将值2压入堆栈
FAR PTRINoW
ax,0
sp,bp
bp
—
main ENDP
INCW PROC FAR
push bp
mov bp,sp
inc WORD PTR[bp+6]; 值增1
pop bp
ret 2
INCW ENDP
这次虽然编译器也为变量N分配了空间,也将其赋值为2,但在调用INCW时,压入堆栈
的不是变量N的地址,而是N的值2。相应地,在INCW子程序中,虽然INCW指令也将传递
过来的值 2增1,但当子程序返回时,RET 2指令已将该值清除了。因此;INCW对其参数的
任何修改不会影响到主程序,即N的值在调用INCW 后仍为2 (未完待续)
ii Ⅲ
, , ( O
R
P
1
.札
m
维普资讯
1996 年 9 月 义4盘宵达技术
END
SUBROUTINE INCW( N}
lNTEGE民钟 2 N[VALUEJ
!\I =N十 1
RETtTRN
EN fJ
i在 25 卷
上述于荒原巾类憾字[VALUEJ指明苦苦数是以假参的形式传诵.这次举价的 ìC!:肩 i苦?可代码
会如下所m:
BSS SEGMENT
N DW ?
BSS ENDS
口1atn PROC FAR
push bp
自IOV
sub
盯IOV WORD PTR
mov
push ax , I室接将值 2 压入幢楼
call FAR PTR IN~W
mov ax. 。
mov
pop bp
ret
maLn ENDP
INCW PROC FAR
push bp
mov
tnC WORD PTR[bp十 6J , 值增 1
pOp bp
lNCW ENDP
这次虽然编译器也为变量 N 分配了空间,也将其赋值为 2.但在词用 lNCW 时,应入堆械
的不是变量 N 的地址,而婚 N 的傻 2. 相应地,在 INCW 子程序叶飞虽然 INCW 指令1!!.将传递
过来的傻 2 增 1 .但当子程序返阴时 .RET 2 指令已将该傻清除了。因此 .INCW 对其参数的
任何修改不会影响到:ì程序,即 N 的值在调用 INCW 后仍为 2. 未完待续}
的8