我正在尝试替换一组fortran程序中的一些图形代码(而不是我自己的代码)。我得到了一个更简单的('psvdraw')工作得很好,用调用Cairo库的C调用替换了fortran postscript生成代码(graphic_output.c)。我已经能够成功地完成跨语言调用而不会有太多麻烦。
然而,当试图让第二个更大的程序('pssect')工作,调用相同的C代码时,我得到分段错误,或者在某些情况下,程序流程返回到fortran例程'错误' (我不会在我的C代码中调用此函数或任何fortran例程。)
在尝试诊断此问题时,我将一堆来自pssect的fortran代码链接到psvdraw('biglib.f'),并得到了相同的错误。请注意,实际上并未调用此添加的代码!此外,错误发生在从fortan到c代码的第一次调用中。所以:psvdraw与biglib.f链接失败,但没有它的psvdraw成功。
以下是makefile的相关位:
COMP77 = gfortran
FFLAGS = -C -v -pedantic -Wunused -fno-underscoring
CC = gcc-4
CFLAGS = -v -pedantic -Wunused
CAIRO_INCLUDE = /sw/include/cairo
CAIRO_LIB = /sw/lib
# PSVDRAW Make setup that works:
psvdraw: psvdraw.o graphic_output.o tlib.o pscom.o
$(COMP77) $(FFLAGS) $@.o graphic_output.o tlib.o pscom.o -L$(CAIRO_LIB) -lcairo -o $@
# PSVDRAW Make setup with errors:
#psvdraw: psvdraw.o graphic_output.o tlib.o pscom.o biglib.o
# $(COMP77) $(FFLAGS) $@.o graphic_output.o pscom.o tlib.o biglib.o -L$(CAIRO_LIB) -lcairo -o $@
pssect: pssect.o graphic_output.o pscom.o tlib.o biglib.o
$(COMP77) $(FFLAGS) $@.o graphic_output.o pscom.o tlib.o biglib.o -L$(CAIRO_LIB) -lcairo -o $@
pssect.o: pssect.f
$(COMP77) $(FFLAGS) -c pssect.f
psvdraw.o: psvdraw.f
$(COMP77) $(FFLAGS) -c psvdraw.f
pscom.o: pscom.f
$(COMP77) $(FFLAGS) -c pscom.f
tlib.o: tlib.f
$(COMP77) $(FFLAGS) -c tlib.f
biglib.o: biglib.f
$(COMP77) $(FFLAGS) -c biglib.f
graphic_output.o: graphic_output.c
$(CC) $(CFLAGS) $(INCL) -c -I$(CAIRO_INCLUDE) graphic_output.c
.c.o:
$(CC) $(CFLAGS) $(INCL) -c $<
.f.o:
$(FC) $(FFLAGS) $(INCL) -c $<
以下是令人讨厌的fortran代码:请注意,问题恰好发生在程序的开头:
PROGRAM PSSECT
implicit none
include 'perplex_parameters.h'
integer jop0, ier99
logical vertex, output, first
character*100 fname, yes*1
integer iop0
logical debug
common / basic /iop0, debug
integer isec,icopt,ifull,imsg,io3p
common/ cst103 /isec,icopt,ifull,imsg,io3p
c----------------------------------------------------------------------
c Look for the "debug_yes" file to turn on debugging messages
PRINT *,'Pre-PSOPEN1'
call psopen ('plot2')
PRINT *,'Post-PSOPEN1'
这是被调用的c代码的一部分并产生错误:
char dmh_debug = 0;
#define DEBUGPRINT(x) if (dmh_debug) {printf x;};
void psopen(char *fname, int fnamelen) {
printf("Debug opened\n");
char *outFileName;
char outputType[255];
char pageWidthString[255];
char pageHeightString[255];
/* Set debug status based upon presence of file named 'debug_yes' in directory */
FILE *debugFile = fopen("debug_yes", "r");
if (debugFile == NULL) {
dmh_debug = 0;
} else {
dmh_debug = 1;
}
fclose(debugFile);
dmh_debug = 1;
DEBUGPRINT(("Debug closed\n"));
fname[fnamelen]='\0';
fname = trim(fname);
outFileName = malloc((strlen(fname) + 50) * sizeof(char));
strcpy(outFileName, fname);
DEBUGPRINT(("Found file name:%s of length: %lu\n", fname, strlen(fname)));
[...]
pnr-rethington:source dave$ ./pssect
Pre-PSOPEN1
Debug opened
Segmentation fault
答案 0 :(得分:2)
如果在未使用的代码中链接会触发问题,那么这往往会表明某些地方(在Fortran代码或C代码中)覆盖了您不应该覆盖的内存。尝试在Valgrind下运行已编译的程序 - 它应该有助于确定这种情况发生的位置。
答案 1 :(得分:1)
我仍然怀疑你在Fortran和C之间的调用有问题,导致不一致。你是怎么打电话的?我认为最可靠的方法是使用ISO C Binding向Fortran指定如何调用C例程。
或者,您可以考虑具有Fortran界面或绑定的图形包。然后你不必在接口上工作,因为Fortran调用或Fortran接口已经存在。我一直在使用DISLIN。另一种可能性是PLplot。
使用当前的方法,我建议检查C例程入口处的参数,以确保它们是正确的。
答案 2 :(得分:1)
您使用单个参数从Fortran调用psopen,C例程需要两个参数。也许你的Fortran编译器会在每个字符串后面添加字符串的长度作为尾随参数。或者也许你第一次尝试就很幸运,它碰巧使用C例程在堆栈中找到的“随机”值。在第二种情况下,在另一种情况下,可能会发生特殊的崩溃。充其量,这是一种连接Fortran和C的非可移植方式。您可以尝试在Fortran调用中添加一个整数长度,看看会发生什么。或者打印或使用调试器在C例程的入口处查看第二个参数的值。
ISO C绑定效果更好。它受到众多编译器的支持(例如,gfortran&gt; = 4.3,ifort),并提供了一种连接Fortran和C的定义和可移植方式。在Fortran代码中指定一个“接口”,以便Fortran编译器生成正确的指令C-call。
尽管如此,使用已经提供Fortran接口的绘图包可能更容易。
答案 3 :(得分:1)
我从你的make文件中注意到你正在使用gfortran。 gfortran(&gt; = 4.3)&amp;的组合gcc支持ISO C绑定。 (Fortran 2003的一部分。)如果你包含接口示例并使用两个参数调用psopen,它应该可以工作。接口进入Fortran程序的声明,并且是C例程psopen的Fortran描述。由于我还没有测试过,因此无法保证...字符串是一种特殊情况 - 您将Fortran程序中的缩放器字符串与接口中的字符串数组匹配,因为C参数是字符数组或指向字符。
interface To_psopen
subroutine psopen ( fname, fnamelen ) bind (C, name="psopen")
use iso_c_binding
implicit none
character (kind=c_char, len=1), dimension (100), intent (inout) :: fname
integer (c_int), value, intent (in) :: fnamelen
end subroutine psopen
end interface To_psopen
答案 4 :(得分:0)
在调试器(gdb
)下运行应告诉您segfault发生的位置。使用-O0 -g
编译所有代码以获取准确信息。