2012-12-13 67 views
4

我想從C傳遞一個字符串數組到Fortran子程序以及從Fortran到同一個Fortran子例程。我成功地從C和Fortran傳遞了單個字符串(即1D字符數組)。但是,我遇到了字符串數組的麻煩。我在Fortran方面使用ISO C綁定,理想情況下,我希望在呼叫方儘可能無縫。如何將C和Fortran中的字符串數組傳遞給Fortran?

我看過一些相關的問題和答案。有些(即thisthis)僅僅是「使用ISO C」而沒有進一步的細節,這沒有多大幫助。 This answer非常有幫助(類似於a different question的答案),但僅適用於單個字符串,其中看起來c_null_char在單個Fortran字符串中被識別。如果沒有兩個單獨的例程,我無法弄清楚爲數組情況做些什麼。

我現在已經是一個C程序,我想傳遞一個字符串從數組(string):

#include <iostream> 

extern "C" void print_hi_array(char input_string[][255]); 

using namespace std; 

int main() { 

    char string[3][255] = {"asdf","ghji","zxcv"}; 
    print_hi_array(string); 

    return 0; 
} 

而且,類似的Fortran例程:

program main 
    implicit none 
    call print_hi_array((/"asdf", "ghji", "zxcv"/)) 
end program 

至此,這是我的接收端:

subroutine print_hi_array(input_string) bind(C) 
    use iso_c_binding, only: C_CHAR, c_null_char 

    implicit none 

    character (kind=c_char, len=1), dimension (3,255), intent (in) :: input_string 
    character (len=255), dimension (3) :: regular_string 
    character (len=255) :: dummy_string 
    integer :: i,j,k 

    write (*,*) input_string 

    do j = 1 , 3 
    dummy_string(:) = c_null_char 
    k = 1 
    do i = 1 + (j-1)*255, j*255,1 
     if (input_string(i) .ne. c_null_char) then 
     write (*,*) "i ",i,j, input_string(i) 
     dummy_string(k:k) = input_string(i) 
     endif 
    k = k +1 
    enddo 
    regular_string(j) = dummy_string 
    enddo 

    write (*,*) regular_string 

end subroutine print_hi_array 

這適用於C函數;我得到這樣的輸出:

asdfghjizxcv 
j=   1 
i   1   1 a 
i   2   1 s 
i   3   1 d 
i   4   1 f 
j=   2 
i   256   2 g 
i   257   2 h 
i   258   2 j 
i   259   2 i 
j=   3 
i   511   3 z 
i   512   3 x 
i   513   3 c 
i   514   3 v 
asdf ghji zxcv 

然而,當它通過Fortran語言做了,我得到了廢話:

[email protected],B�@(P,B�]B]6(P,B�@ ....... 

似乎有在這個方法沒有c_null_char

所以,我怎麼能寫一個Fortran子程序採取從兩個C和Fortran字符串數組?

回答

3

Fortran語言使用空格填滿,如果它被聲明比其存儲的文本長字符串的其餘部分。它不是零分隔的,聲明的長度存儲在一個隱藏的變量中。它不包含c空字符,因此你正在閱讀一些垃圾(緩衝區溢出)。當tlit打印帶\ 000的字符串時,Fortran應該打印什麼內容,而不是由標準定義,並取決於實施。

特別是,你也傳遞一個字符(4)陣列維度3給需要更多的數據的子程序(255個字符,但我不舒爾有關索引順序)。只有指針被傳遞,所以我認爲它不能被檢查。

它可以這樣定義數組構造字符串的長度:

[character(255) :: "a","ab","abc"] 
2

我居然看到兩種方法可以做到這一點。或者,你在C中編寫一個循環,並將這些字符串逐個傳遞給Fortran,就像你之前已經做過的那樣。另外,如果你想通過要處理的Fortran和使用相同的過程的C數組的整個數組,你必須讓你的C-字符串數組的適當副本。下文一個工作,但沒有太多的測試,例如:

extern "C" void print_array_c(int nstring, char input_string[][255]); 

using namespace std; 

int main() { 

    char string[3][255] = {"asdf","ghji","zxcv"};  
    print_array_c(3, string); 

    return 0; 
} 

請注意,我還通過串的數量,這樣的例子能夠處理各種大小的陣列。 (但是,字符串的長度假定爲255個字符。)在Fortran大小上,需要一個例程來將其轉換爲Fortran字符串。一個可能的可視化可能是:

module arrayprint_module 
    use, intrinsic :: iso_c_binding 
    implicit none 

    integer, parameter :: STRLEN = 255 

contains 

    !> The printing routine, works with Fortran character arrays only. 
    subroutine print_array(strings) 
    character(len=STRLEN), intent(in) :: strings(:) 

    integer :: ii 

    do ii = 1, size(strings) 
     write(*,*) ii, strings(ii) 
    end do 

    end subroutine print_array 


    !> Converts C string array to Fortran string array and invokes print_array. 
    subroutine print_array_c(nstring, cptr) bind(C) 
    integer(c_int), value :: nstring 
    type(c_ptr), intent(in), value :: cptr 

    character(kind=c_char), pointer :: fptr(:,:) 
    character(STRLEN), allocatable :: fstrings(:) 
    integer :: ii, lenstr 

    call c_f_pointer(cptr, fptr, [ STRLEN, nstring ]) 
    allocate(fstrings(nstring)) 
    do ii = 1, nstring 
     lenstr = cstrlen(fptr(:,ii)) 
     fstrings(ii) = transfer(fptr(1:lenstr,ii), fstrings(ii)) 
    end do 
    call print_array(fstrings) 

    end subroutine print_array_c 


    !> Calculates the length of a C string. 
    function cstrlen(carray) result(res) 
    character(kind=c_char), intent(in) :: carray(:) 
    integer :: res 

    integer :: ii 

    do ii = 1, size(carray) 
     if (carray(ii) == c_null_char) then 
     res = ii - 1 
     return 
     end if 
    end do 
    res = ii 

    end function cstrlen 


end module arrayprint_module 

請注意,您從C傳遞的數組必須contigous這個工作,我認爲字符(KIND = c_char)與FORTRAN字符類型,兼容這通常應該是。這是我想出來的

+0

這種方法的問題是,它需要兩個獨立的功能。我希望能從C和Fortran中獲得同樣好的功能(想象一個應該獨立於代碼的庫)。 –

1

一種方法是修改調用Fortran程序來使用ISO C結合:

program main 

    use iso_c_binding, only: C_CHAR 
    implicit none 
    character (kind=c_char, len=255), dimension (3) :: input_string 

    input_string = (/ "asdf", "ghji", "zxcv" /) 

    call print_hi_array(input_string) 

end program 
+0

您應該小心,因爲這些字符串在其末尾仍然會丟失c_null_char(它們仍然是Fortran類型字符串,只是使用與C兼容的字符類型)。如果在你的例程中,你依靠c_null_char終止的字符串,那將無法安全工作。 –

+0

這是真的。他們充滿空間。所以它會用空格填充整個虛擬字符串,這很好。 –

相關問題