如何正确link gfortran和gcc?

How to properly link gfortran and gcc?

我正在尝试编写一个调用一段 Fortran 代码的 C 函数。我认为直接尝试 link fortran 代码比尝试将 fortran 重写为 C 更容易。我在直接下载的 mac 上使用 gcc/g++/gfortran二进制文件并安装它们。

Fortran 代码位于 https://www.ngdc.noaa.gov/IAGA/vmod/igrf12.f。我要调用的具体子程序是subroutine igrf12syn (isv,date,itype,alt,colat,elong,x,y,z,f)

我写的wrapper如下:

 #include <stdio.h>

 extern void igrf12syn( int *isv, double *year, double *itype, double *alt,
 double *colat, double *elong, double* x,double* y, double* z, double* f );

int main(){
    double B[4], BabsDervs[3], BrDervs[3], BthDervs[3], BphDervs[3];
    double r, th, ph,year,alt2,lat,colat,elong,x,y,z,f;
    int isv;

    // igrf12syn (isv,date,itype,alt,colat,elong,x,y,z,f)
    r = 6371e3;
    th = 0.57;
    ph = 0;

    year = 2015;
    alt2 = 200.0;
    lat = 33.25;
    colat = 90.0-lat;
    elong = 0.0;
    isv =1;
    igrf12syn_(&isv,&year,&isv,&r, &th, &ph,x,y,z,f);

    printf("%0.4e",f);
 return 0;
 }

我用来编译和调用它的命令是:

/usr/local/bin/gcc -lgfortran -c testigrf.c igrf12.f
/usr/local/bin/gcc -lgfortran -o b.out testigrf.o igrf12.o 

我得到的错误是:

duplicate symbol _main in:
testigrf.o
igrf12.o
ld: 1 duplicate symbol for architecture x86_64
collect2: error: ld returned 1 exit status
make: *** [igrf] Error 1

我明白这个错误是什么意思,我可以为显示两个 main 函数的 .o 文件生成 nm 文件。但是,我不确定如何更改 Fortran 代码以消除此错误。

所以我的问题如下: 1. 如何更改 Fortran 或 C 代码来解决此问题?特别是,由于 fortran 代码以 PROGRAM IGRF

开头
  1. 我是否正确调用了 igrf12syn 例程?我担心我没有正确调用它。我也可能在传递变量方面做得不好,这必须通过引用来完成。

感谢您的帮助,如果我在调用例程时做了一些非常愚蠢的事情,请告诉我。

很久没调用 Fortran 例程了,但你的工作看起来很合理。

现在编写一个简短的 fortran 子程序,将其编译到一个独立的文件中,通过引用子程序参数测试您的调用。

      subroutine igrf12syn (isv,date,itype,alt,colat,elong,x,y,z,f)

当然,您可以尝试编译 fortran 文件,只需删除程序行,直至该子例程定义。在我看来它看起来是独立的,但编译器会更好地检查源代码。

首先,您的 C 程序 testigrf.c 有缺陷。事实上, 你有警告:

testigrf.c: In function ‘main’:
testigrf.c:23:5: warning: implicit declaration of function ‘igrf12syn_’ [-Wimplicit-function-declaration]
     igrf12syn_(&isv,&year,&isv,&r, &th, &ph,x,y,z,f);
     ^

这是因为您声明了一个名为 igrf12syn 的函数并调用了 一个叫做 igrf12syn_,编译器对此一无所知。

igrf12syn_改成igrf12syn,重新编译,出现的错误是:

testigrf.c: In function ‘main’:
testigrf.c:23:26: warning: passing argument 3 of ‘igrf12syn’ from incompatible pointer type [-Wincompatible-pointer-types]
     igrf12syn(&isv,&year,&isv,&r, &th, &ph,x,y,z,f);
                          ^
testigrf.c:3:14: note: expected ‘double *’ but argument is of type ‘int *’
  extern void igrf12syn( int *isv, double *year, double *itype, double *alt,
              ^
testigrf.c:23:44: error: incompatible type for argument 7 of ‘igrf12syn’
     igrf12syn(&isv,&year,&isv,&r, &th, &ph,x,y,z,f);
                                            ^
testigrf.c:3:14: note: expected ‘double *’ but argument is of type ‘double’
  extern void igrf12syn( int *isv, double *year, double *itype, double *alt,
              ^
testigrf.c:23:46: error: incompatible type for argument 8 of ‘igrf12syn’
     igrf12syn(&isv,&year,&isv,&r, &th, &ph,x,y,z,f);
                                              ^
testigrf.c:3:14: note: expected ‘double *’ but argument is of type ‘double’
  extern void igrf12syn( int *isv, double *year, double *itype, double *alt,
              ^
testigrf.c:23:48: error: incompatible type for argument 9 of ‘igrf12syn’
     igrf12syn(&isv,&year,&isv,&r, &th, &ph,x,y,z,f);
                                                ^
testigrf.c:3:14: note: expected ‘double *’ but argument is of type ‘double’
  extern void igrf12syn( int *isv, double *year, double *itype, double *alt,
              ^
testigrf.c:23:50: error: incompatible type for argument 10 of ‘igrf12syn’
     igrf12syn(&isv,&year,&isv,&r, &th, &ph,x,y,z,f);
                                                  ^
testigrf.c:3:14: note: expected ‘double *’ but argument is of type ‘double’
  extern void igrf12syn( int *isv, double *year, double *itype, double *alt,
              ^

阅读诊断并修复错误,然后再继续。他们 可能会被修复,例如,通过更改:

double r, th, ph,year,alt2,lat,colat,elong,x,y,z,f;

double r, th, ph,year,alt2,lat,colat,elong,x,y,z,f,itype;

并改变:

igrf12syn(&isv,&year,&isv,&r, &th, &ph,x,y,z,f);

至:

igrf12syn(&isv,&year,&itype,&r, &th, &ph,&x,&y,&z,&f);

这会让编译器满意,虽然它可能不会表达你的意图, 我不清楚。

您明白您不能 link 两个 程序 成为一个程序, 因为 linker 会发现重复的 main 函数。你需要 link C 主程序仅包含 Fortran 子例程 igrf12syn.

接下来,下载并保存文件 https://www.ngdc.noaa.gov/IAGA/vmod/igrf12.f。 在 text/programming 编辑器中打开它。 Select 所有行 - 完整 行,包括前导 space - 组成子例程 igrf12syn,来自:

  subroutine igrf12syn (isv,date,itype,alt,colat,elong,x,y,z,f)

至:

 end

在新文件 igrf12syn.f 中完全按照复制的方式保存此选择 与 testigrf.c.

相同的目录

然后,在该目录中打开控制台并执行以下命令:

$ gcc -c -o testigrf.o testigrf.c
$ gfortran -fno-underscoring -c -o igrf12syn.o igrf12syn.f
$ gfortran -o testigrf testigrf.o igrf12syn.o

这些将编译和 link 您的程序作为 testigrf 在同一目录中。

最后,重复前两个命令,每个命令都有附加选项 -Wall, 查看您尚未注意到的警告。考虑修复程序 删除警告,因为它们很可能意味着程序不会 表现如你所愿。始终在 gcc/g++/gfortran 编译器中包含 -Wall 编译对您重要的代码时的选项。