1

I am trying to pass a string and a value from fortran to c and it is giving me weird results and I cannot figure out what is happening.

fortran      
      integer rmat

      rmat = 4
      call cprint("rmat ", rmat)

      END

cprint_.c

#include <stdio.h>

void cprint_( char *string, int *wtf, int *var){
    printf("string: %s wtf: %s var: %d\n", string, wtf, var);
}

output:

string: rmat convertedtest.f wtf: var: 5

Ben
  • 117
  • 11
  • `wtf` is `int*` and should not be printed with `%s` – Eugene Sh. Aug 06 '19 at 21:08
  • You are treating `int *wtf` as if it is a string with `%s`. – Weather Vane Aug 06 '19 at 21:09
  • Also `var` is incorrect type – Eugene Sh. Aug 06 '19 at 21:09
  • 1
    I think you want something like `printf("string: %s wtf: %d var: %d\n", string, *wtf, *var);` No comments about Fortran side(as I don't know it), but it looks suspicious that `cprint` is being called with two parameters only. – Eugene Sh. Aug 06 '19 at 21:10
  • This might help https://stackoverflow.com/questions/17845931/calling-c-function-subroutine-in-fortran-code – Renat Aug 06 '19 at 21:14
  • 2
    As you aren't using the standardized interoperability features of Fortran 2003, please say which compilers (including versions) you are using. Many compilers for example pass an additional length argunent when using a C function with a character argument. – francescalus Aug 06 '19 at 21:44

3 Answers3

2

Fortran strings are equivalent to fixed-length character arrays. Semantically, they are not null-terminated. Instead, it is up to the implementation to track their length. Historically, implementations track string length through function calls by various different means, but the most common is probably to pass the length as an additional, implicit parameter. That's surely what you're seeing.

In some implementations, the extra argument immediately follows the string, and in others all the string lengths come at the end of the argument list. Typically, though, the length parameters are integers, not pointers, unlike the parameters that correspond directly to objects.

Your test, though technically malformed, strongly suggests that your string length is being passed as the third argument (and indeed as an int, not an int *). Thus, a first cut at an appropriate cprint_ function might look like this:

void cprint_(char *string, int *integer, int string_len) {
    printf("string: '%.*s'\ninteger: %d\n", string, string_len, *integer);
}

If you're going to print the string as received from Fortran, without modification, then it is appropriate to specify a suitable precision, as shown, because the string cannot be assumed to be terminated. In practice, you may want to print fewer characters of the string than its actual length, however, because there is no explicit distinction between the size of the array and the number of meaningful characters in it. Fortran strings are right-padded with blanks (' ') when values smaller than their sizes are assigned to them, and it is common for Fortran programs to assume that the real data extend only to the last non-blank character.

Be careful, though: if you modify the string, by inserting a C string terminator, for example, then you are changing the data on the Fortran side, too. Make a copy if you must, but often you can avoid doing so, as the above example does.

John Bollinger
  • 160,171
  • 8
  • 81
  • 157
1

The answer provided by @John Bollinger is correct. Fortran treats strings entirely differently from C. Here is an example code that I wrote a few days ago, which passes a char array from C to Fortran, then Fortran makes it lower-case and returns the result to C,

module String_mod
    implicit none
contains
    subroutine getLowerCase(StrVec,StrVecLowerCase,lenStrVec) bind(C, name="getLowerCase")
        use, intrinsic :: iso_c_binding, only: c_char, c_null_char, c_size_t
        use, intrinsic :: iso_fortran_env, only: IK => int32
        integer(c_size_t), intent(in), value        :: lenStrVec
        character(len=1,kind=C_char), intent(in)    :: StrVec(lenStrVec)
        character(len=1,kind=C_char), intent(inout) :: StrVecLowerCase(lenStrVec)
        integer(IK), parameter                      :: duc = ichar('A') - ichar('a')
        character                                   :: ch
        integer(IK)                                 :: i
        write(*,"(*(g0))") "From Inside Fortran@getLowerCase(): StrVec = ", (StrVec(i),i=1,lenStrVec)
        write(*,"(*(g0))") "From Inside Fortran@getLowerCase(): lenStrVec = ", lenStrVec
        do i = 1, lenStrVec
            ch = StrVec(i)
            if (ch>='A' .and. ch<='Z') ch = char(ichar(ch)-duc)
            StrVecLowerCase(i) = ch
        end do
    end subroutine getLowerCase
end module String_mod

and here is the C code calling Fortran,

#include <stdio.h>

// Fortran function's prototype
extern void getLowerCase(char [], char [], size_t );

int main(void)
{
    char StrVec[] = "HELLO FORTRAN! YOU ROCK!";
    size_t lenStrVec = sizeof(StrVec) / sizeof(StrVec[0]);
    char StrVecLowerCase[ lenStrVec ];

    getLowerCase( StrVec
                , StrVecLowerCase
                , lenStrVec
                );
    printf("From Inside C: %s\n", StrVecLowerCase);
    return 0;
}

Compiling with Intel Fortran/C compiler gives,

$ ifort -c String_mod.f90
$ icl -c main.c
$ icl String_mod.obj main.obj -o a.exe 
$ a.exe

From Inside Fortran@getLowerCase(): StrVec = HELLO FORTRAN! YOU ROCK!
From Inside Fortran@getLowerCase(): lenStrVec = 25
From Inside C: hello fortran! you rock!

The simplest Fortran-2003 approach to string interoperation is to declare the Fortran strings as char arrays and pass the lengths of the arrays explicitly from C to Fortran or vice versa, as done in the example above. Note that all of the allocations happen on the C side. Fortran 2018 provides more convenient ways to pass strings (including allocatable strings) between Fortran and C, some of which require no change to the Fortran code and minimal work on the C code via ISO_Fortran_binding.h. See, for example, this page.

Scientist
  • 1,767
  • 2
  • 12
  • 20
0

You are trying to print an int * with the %s formatter, which is string-reserved.
You have to use %d, and don't forget that wtf in a pointer, so you have to dereference it with *wtf

cocool97
  • 1,201
  • 1
  • 10
  • 22