网 上说要分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. 今天真开心,终于知道怎么打包apk了

    1.函数上下文的产生,并不是函数定义时决定的,而是函数调用时产生的:来个栗子 function creep(){return this;} console.log(creep()) var sneak ...

  2. JavaSE中Collection集合框架学习笔记(3)——遍历对象的Iterator和收集对象后的排序

    前言:暑期应该开始了,因为小区对面的小学这两天早上都没有像以往那样一到七八点钟就人声喧闹.车水马龙. 前两篇文章介绍了Collection框架的主要接口和常用类,例如List.Set.Queue,和A ...

  3. string can not be resolved

    参考:http://jingyan.baidu.com/article/a17d5285339c828099c8f245.html

  4. 选择、冒泡排序,二分查找法以及一些for循环的灵活运用

    import java.util.Arrays;//冒泡排序 public class Test { public static void main(String[] args) { int[] ar ...

  5. java基础06 IO流

    IO用于在设备间进行数据传输的操作. Java IO流类图结构:   IO流分类 字节流: InputStream FileInputStream BufferedInputStream Output ...

  6. CentOS-7.2安装SQuirreL SQL Client连接Hive

    一,SQuirreL SQL Client干吗的? SQuirreL SQL Client是一款功能强大的服务器配置管理软件,该软件能够帮助用户快速.高效的配置服务器,且支持用户查看数剧库的结构并发出 ...

  7. Python基础之模块、数据类型及数据类型转换

    一.模块 1.标准库 不需要安装,直接调入使用的模块. import sys模块: import sys print(sys.path) #打印环境变量绝对路径 print(sys.argv) #打印 ...

  8. Samba远程代码执行-分析(CVE-2017-7494)

    经历了前一阵windows的EternalBlue之后,某天看见了360的 samba高危预警,这个号称linux端的EternalBlue(EternalRed),于是便研究了一波 概述(抄) Sa ...

  9. python之基本内容

    这里提供在使用python进行开发中常使用到的方法技巧,如有不对欢迎批评指正. 要点:开发中类.变量特性查询,类型就是类,断言的使用,深浅复制判断等 python脚本文件是使用UTF-8编码的,所以在 ...

  10. Nginx文档-初学者指南

    原文档: http://nginx.org/en/docs/beginners_guide.html 译者:Oopsguy 本指南旨在介绍nginx基本内容和一些在Nginx上可以完成的简单任务.这里 ...