网 上说要分c为主程序和fortran为主程序两种情况讨论,其实我觉得不用,只要你了解生成可执行文件的机制。这个机制就是:不论是单一语言模块之间的 链接还是不同语言之间的混合链接,本质目的都是要链接器能找到定义于其他模块中的符号,如果全部找到,则链接成功,生成可执行的二进制文件。

下面的内容比较基础,看烦了就跳过。

比如简单的一个c程序:

/* main.c */

void FOO (); /* 其实按c语言的规定,调用程序之前不需声明,

* c编译器会猜测函数的原型。需要在调用之前

* 定义或者声明那是c++的风格

*/

int main ()

{

FOO ();

return 0;

}

/* foo.c */

#include <stdio.h>

void FOO ()

{

printf (“hello world\n”);

}

这两个文件都可以编译通过

gcc -c main.c

gcc -c foo.c

这里gcc只用来编译并不链接,因为单独链接其中一个文件都是不完整的。必须将两个目标文件(上面会生成main.o和foo.o)链接在一起。

gcc -osample main.o foo.o

这里gcc 因为看到文件后缀名为.o所以直接进行链接而不再进行编译。(gcc在编译时对文件的识别主要靠起后缀名。如果是gcc -osample main.c foo.c那么因为后缀名为.c所以会先编译,又因为没有-c选项所以会再链接。多说一点如果是gcc -osample
main.c foo.o那么只编译main.c文件,再将编译后的临时目标文件与foo.o链接。gcc还会因为不同的后缀名采用不同的编译器进行编译具体参见man 或者info,因为他是compiler collection嘛:)

不好意思,绕了一大圈。上面说到这两个文件都能独立编译(因为语法没错嘛),并且将编译出的目标文件放在一起链接就可以了。这正是因为链接器(链接器其实 是ld,gcc调用了它)在foo.o中找到了main.o中需要的foo的定义,并且在main.o中找到了main的定义。

这里还牵扯到一个问题,那就是c是很纯朴的语言,c的函数在文本文件中是什么名字,那么编译出来在目标文件(一般是.o文件)中相应的那个函数还是那个名字(不会像c++一样为了重载在函数名前后加一大堆区分符)。

比如用nm查看main.o和foo.o

nm main.o

U FOO

00000000 T main

U表示在main.o中符号foo是未定义的,需要从外部链接进来。T表示该符号存在于这个目标文件对应的文本文件中,说白了就是有定义的符号。

nm foo.o

00000000 T FOO

U printf

这样就可以看出,编译出的foo.o与原foo.c中的函数名都是FOO。main.c中调用了函数FOO,那么他编译出的这个函数的符号也还是FOO, 这样对函数FOO的供与求才能对的上,链接器能找到对得上的符号才能链接成功。有人说foo.o里还有一个未定义符号printf,这个到哪里去
找?gcc总是会有很多默认链接的库和链接选项,这其中包括c的标准库,而printf就在c标准库中。加上-v选项就可以看出来,gcc在编译和链接时 到底做了哪些事。

又多说一点,如果一个函数有定义或者被调用,那么编译后在目标文件中就会有其相应的符号,因为要告诉链接器有这个供给,或者有这个需求嘛。如果一个函数仅仅有什么声明,那么是不会编译出它的符号的,因为它既不会给别人用,也不会用别人。

那么,说了这么多其实还是为了明确一点:要让链接器找到在一个文件中需要的符号定义,那么链接就能成功,就能生成可执行文件了。这也是混编的关键!

现在开始真真儿的了。

将主程序main.c换成fortran的

c main.f

program test

external FOO

call FOO ()

end

还是原来的foo.c,也就是说由fortran调用c,仍旧是:

gcc -c foo.c

gcc -c main.f

注意这里用的是gcc编译main.f(fortran程序),这是完全可以的。前面说了gcc是compiler collection,它会根据文件后缀来判断是什么语言写成的,从而调用相应的编译器来编译。.f的文件它自然会用g77或者f77之类的来编译。与 g77
-c main.f完全一样。为了链接成功,先来看看foo.o和main.o中都有什么符号

nm main.o

U foo_

00000000 T MAIN__

U s_stop

nm foo.o

00000000 T FOO

U printf

可以看出,main.o里需要用到符号名为foo_但foo.o里提供的是FOO——不匹配。一个办法就是依据上面说的c的纯朴性——写的什么名儿,编译 出就是什么名儿,从而直接改变foo.c中的函数名,改为void foo_ ()即可。这样链接时,main.o需要的foo_符号就能在foo.o中找到。

但是把c的函数名改成这个样子,感觉总是别扭。应该看到是什么(.c中看到foo_)就用什么(而.f中用的是FOO)这样才人性化。再说如果 fortran需要用到一个c库,这个库里的函数不一定都是小写并且最后还带下划线。就像c++要用c库,也需要在声明这个库中的函数时使用extern
“C”,使c++编译器在编译这个函数时生成的符号名是C风格而不是C++风格。所以我们也需要类似c++的做法改变fortran程序编译出来的符号 名。

我不知道fortran是否有extern “C”之类的东东。但是编译fortran程序是有选项可选的。比如:

gcc -fno-underscoring -fcase-preserve -c main.f

这里加了两个选项,顾名思义前者用来去掉下划线,后者用来保持大小写。这下再

nm main.o

U FOO

00000000 T MAIN__

U s_stop

这样就解决了c函数被fortran调用的问题了。

但是因为main.o中还有一个未定义符号s_stop,而gcc默认只链接和c相关的库,所以这时使用gcc -osample main.o foo.o会报错,大概就是说s_stop未定义(unreferenced 或者 undefined)。一个简单的解决办法——使用g77链接main.o和foo.o。就好像gcc默认会链接c库一样,g77默认会链接
fortran的一些基本的,标准的库;另一个办法就是查明g77会链接哪些基本的,标准的fortran库,这也很简单在编译链接fortran程序时 加上-v选项。我看到的g77的比gcc多了这几个选项 -lfrtbegin -lg2c -lm,那么就是说g77链接了libfrtbegin,libg2c,libm,最后一个是数学库,前两个应该就是g77专用的了。所以

gcc -lfrtbegin -lg2c -osample main.o foo.o

(同样g++使用了-lstdc++,也就是libstdc++。这也就是为什么时常有人问gcc main.cc会出错的问题了,如果main.cc用到了c++库中的函数,那么当然要使用gcc -lstdc++ main.cc才行了)

如果我们保持main.c不变,而将foo.c变为foo.f。也就是c调用fortran

c foo.f

SUBROUTINE FOO()

print *,"hello world"

END

编译foo.f和main.c

gcc -fno-underscoring -fcase-preserve -c foo.f

gcc -c main.c

链接

gcc -lfrtbegin -lg2c main.o foo.o -osample

(use gfortran main.o foo.o -osample)

成了。(其实,当fortran不为主程序时,可以不用链接libfrtbegin,起码这个小程序不用)

这里讨论了混编的基本原理,就是让链接器找到符号所在。从这点出发,一些混编问题都应该有了解决的思路。至于代参数的函数我没有涉及到,但我想都得从这个 基本出发吧。还有些程序会使用动态链接库.so,那么应该使用nm的-D选项查看这些动态符号。(objdum的功能比nm更强大)

有很多东西很基础我还罗嗦了很久,让大家见笑了:)

还有一件事,那就是我这里链接采用了gcc -l的方式,更基本的是ld的方式,只要你知道链接哪些库,链接的顺序如何即可。但是为了简单安全方便,还是建议直接用相应的编译器完成链接工作(比如 fortran就用g77),因为它们的链接顺序已经理好了(并且它们除了链接自己的库还链接c库,而gcc只链接c库,所以用它们不用担心链不到c库,
而用gcc会担心链不到专有的库)。像上面的例子最后的链接的使用g77最好,因为我的例子很简单,而你的有可能很复杂。

还想起来一件事,关于网上使用的例子有__stdcall的,那都是关于win api的。

我 对fortran一无所知,只知道其优势在于科学计算还比较方便,再有就是某领域的早期程序大多由fortran编写。出于重用方便的考虑,我们 现在要把fortran写的代码编译成动态链接库,然后通过C来调用。Windows下动态链接库是很常见的东西,linux下也有,换了一个名字,叫
standard object,大多形如lib*.so。SO文件可以通过编译器的-shared选项得到。

比如,我们有一个fortran程序名为subf1.f,如下:

subroutine sUbF1()

print*,'hello world.'

return

end

如果有一个C程序希望调用sUbF1(),就可以将 subf1.f 编译成为动态链接库。

gcc -shared -o libf1.so subf1.f

这个命令将产生libf1.so这个文件,此文件即是一个linux下的动态链接库。gcc会根据文件的扩展名来调用相应的编译器,不用你操心。此例中事实上实际的编译器是f77,我机器上没有f90。

在main.c里面调用sUbF1(),如下:

int main(){

subf1_();

return 0;

}

注意到我们调用的时候,名字变成了“ subf1_ ”。这是编译器(f77)的一个命名规则,没有为什么,它就是把你在fortran中的函数名字全转换成小写,然后在最后加一个下划线。 我昨天搜了很多版本,头昏脑胀,怎么调都说找不到,也没有想到要自己看看。今天一早突然想到用hex编辑器看一下就是了,于是一看,里面果然有真正的函数
名。后来看program版kb也给了正确的解答,很钦佩;伟大的康神还教导我抛弃hex编辑器,用nm,热泪盈眶……

找到正确的函数名,直接调用就可以,好像你已经在你的C文件里实现了这个函数一样,不需要include任何东西,只需要在编译时告诉编译器你用了哪个动态链接库就可以了,如下:

gcc -o out main.c libf1.so

这时候编译器有可能会报告如下错误:

libf1.so: undefined reference to 'do_lio'

libf1.so: undefined reference to 'e_wsle'

libf1.so: undefined reference to 's_wsle'

这时在后面加上-lg2c -B108的选项应该就可以了,也就是

gcc -o out main.c libf1.so -lg2c -B108

这时会得到out的可执行文件。运行out,屏幕会输出hello world。关于这两个选项,我也着实搜了一阵,不是很好搜。当时看了眼原因,可能是有关编译器版本和字符方面的

注意:

用C++和fortran混合编程需要用一下命令连接.o文件

g++ svm-train.o svm.o maintrain.o -oout1 -lgfortran

否则编译会有问题

c和fortran混编(基于GNU/Linux,转自 linzch)的更多相关文章

  1. iOS 静态类库 打包 C,C++文件及和OC混编

    iOS 静态类库 编译 C,C++ 我们都知道,OC 原生支持C, 在 创建的 OC类的 .m 里面,可以直接编写C的代码: 同样 Xcode 也支持 OC ,C++的混编,此时,我们通常把OC创建的 ...

  2. 在GNU/Linux下将CD音乐转为mp3

    以前我欣赏古典音乐都是听的CD,因而珍藏了不少光盘以及下载到电脑上的ape与flac格式的音乐文件.随着手机硬件性能(如电池续航能力.处理器速度.音质.存储容量等)和软件功能(音乐播放器对于曲目的管理 ...

  3. 完全用 GNU/Linux 工作(转)

    转自:http://www.chinaunix.net/old_jh/4/16102.html 看到一半,实在太长,但已觉得很好,转来分享一下. 完全用 GNU/Linux 工作 - 摈弃 Windo ...

  4. UNIX发展史(BSD,GNU,linux)

    先前的一個理想 UNIX 系统自 1969 年 Ken Thompson 与 Dennis Ritchie 在美国贝尔电话实验室(Bell Telephone Laboratories)发展出雏形至今 ...

  5. UNIX发展史(BSD,GNU,linux)(转)

    转自 UNIX发展史(BSD,GNU,linux) 这篇文章写的非常好,在这里转一下. 先前的一個理想 UNIX 系统自 1969 年 Ken ThompsonKen Thompson 与 Denni ...

  6. 【历史】- UNIX发展史(BSD,GNU,linux)

    先前的一個理想 UNIX 系统自 1969 年 Ken Thompson 与 Dennis Ritchie 在美国贝尔电话实验室(Bell Telephone Laboratories)发展出雏形至今 ...

  7. [转自王垠]完全用GNU/Linux工作,摈弃Windows低效率的工作方式

    ZT (a qinghua student's article) 我已经半年没有使用 Windows 的方式工作了.Linux 高效的完成了我所有的工作. GNU/Linux 不是每个人都想用的.如果 ...

  8. (转)完全用GNU/Linux工作 by 王珢

    完全用GNU/Linux工作 王珢      (看完这篇博文,非常喜欢王珢的这篇博客,也我坚定了学gnu/linux的决心,并努力去按照国外的计算机思维模式去学习编程提高自己.看完这篇文章令我热血沸腾 ...

  9. 从C过渡到C++(1)——GNU/Linux

    从C过渡到C++(1)--GNU/Linux 目录 从C过渡到C++(1)--GNU/Linux 大名鼎鼎的GNU/Linux GNU GNU的组成 一点补充 MinGW 运行时库 额外的内容 Min ...

随机推荐

  1. BufferedWriter

    package JBJADV003;import java.io.*;public class BufferedWriterTest { public static void main(String[ ...

  2. Symbol() 的使用方法

    简介:ES5的对象属性名都是字符串,这就很容易造成属性名的冲突,比如一个项目很庞大,又不是一个人开发 的,就有可能会造成变量名冲突,如果有一个独一无二的名字就好了,这样就能从根本上防止属性名冲突.这就 ...

  3. 通用JSONHelp 的通用的封装

    1. 最近项目已经上线了 ,闲暇了几天 想将JSON  的序列化 以及反序列化进行重新的封装一下本人定义为JSONHelp,虽然Microsoft 已经做的很好了.但是我想封装一套为自己开发的项目使用 ...

  4. Servlet 中为多项选择题判分---String类的indexOf()方法妙用

    首先来看一下String类的indexOf()方法的用法: public class FirstDemo1 { /** *API中String的常用方法 */ // 查找指定字符串是否存在 publi ...

  5. Spring3 MVC 类型转换

    1. Spring在进行类型转化都是基于java.beans.PropertyEditor接口. 2. 可以使用@InitBinder来进行对单个controller的类型进行操作,比如添加Date类 ...

  6. 30分钟学会使用Spring Web Services基础开发

    时隔一年终于又推出了一篇30分钟系列,上一篇<30分钟学会反向Ajax>是2016年7月的事情了.时光荏苒,岁月穿梭.虽然一直还在从事Java方面的开发工作,但是私下其实更喜欢使用C++. ...

  7. 基于.NET CORE微服务框架 -谈谈surging的服务容错降级

    一.前言 对于不久开源的surging受到不少.net同学的青睐,也受到.net core学习小组的关注,邀请加入.NET China Foundation以方便国内.net core开源项目的推广, ...

  8. 中国(北方)大学生程序设计训练赛(第二周) (A B D G)

    比赛链接 A题是KMP,先把A拼接到B的后面,然后利用next数组的意义(包括其具体含义,以及失配时的应用),得到ans #include<bits/stdc++.h> using nam ...

  9. 原生JSdom节点相关(非原创)

    节点属性 Node.nodeName //返回节点名称,只读 Node.nodeType //返回节点类型的常数值,只读 Node.nodeValue //返回Text或Comment节点的文本值,只 ...

  10. 支付宝LR集群压测报告

    支付宝压力测试报告 时间:2016-03-23                                             测试人员:XXX 目录 支付宝压力测试报告 1 目录 1 一   ...