4

Normally, the SAVE attribute is used in Fortran type declarations so that the variable retains its value at the end of a subprogram, such as described by the answers to the SO question here. However, I recently gave an example at another question for how a Fortran function can be written that returns only the C address of an allocatable character string constant to a C calling program, with the C_LOC intrinsic and other ISO_C_BINDING features of F2003. Should the SAVE attribute be used on the Fortran allocatable string constant to avoid potential issues?

Although I didn't use SAVE, the function worked as intended: the C program uses a char* to point at the address returned by the Fortran function, which could then be used as normal (e.g., for printing and with strlen()). No warnings/errors were generated. Also, this seems consistent with how I have seen C_F_POINTER used in the compiler docs and the examples at a related SO question (that is, the target values did not have the SAVE attr).

Particularly since the Fortran function was called from C, it's not clear to me whether this process exhibits the expected behavior, or if it could/should have failed, or if this is a vendor-specific implementation detail. The Intel Fortran 17 documentation for SAVE (here) seems to indicate that a string constant (allocatable or not?) is saved by default, but I am not sure if I'm reading this right, or how/if this info holds up in the current context. Am I performing this process in a portable, correct manner?


Although I linked to the code already, here are the important bits:

! f_string.f90: returns the C address to an allocatable string constant:
function get_string() bind(c, name='get_string')
    use, intrinsic :: iso_c_binding
    implicit none
    type(C_PTR) :: get_string
    character(len=:), allocatable, target :: fortstring  ! <- Include SAVE?

    fortstring = "Hello StackOverflow" // C_NULL_CHAR    ! <- NULL-terminated string constant
    get_string = C_LOC(fortstring)
end function get_string

// c_string.c: uses a char* to point at the address returned by 'get_string'
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
char *get_string(void);                                  // <- Fortran fcn signature
int main(){
    char *mycharptr; 

    mycharptr = get_string();
    printf("String from Fortran: %s\n", mycharptr);
    printf("len = %d\n", strlen(mycharptr));
    return 0;
}

Compiled as ifort /c f_string.f90, icl c_string /link f_string.obj.

Matt P
  • 2,287
  • 1
  • 11
  • 26

1 Answers1

3

That is correct. Save is necessary.

All allocatable entities are deallocated when the procedure exits unless they are save.

You can use pointer instead of allocatable and the target will not be automatically deallocated. I find it better than save because of possible multiple calls to the procedure.

Your program probably "worked" because although the memory block was deallocated, it was not overwritten. So the C procedure still found meaningful data, but at address it was not allowed to access. But this access is not always checked by the OS memory protection.

  • Ok, understood. Same as @francescalus suggested yesterday. People like you two are why I come back to SO. BTW, with `pointer` would I then need to use `c_f_pointer`? Still learning... – Matt P Jun 08 '17 at 17:43
  • After experimenting: if we choose to avoid `save` it seems that the function can just return with `get_string = C_LOC(ptr_to_fortstring)`. How does that look? – Matt P Jun 08 '17 at 18:08
  • If `ptr_to_fortrstring` was allocated, not just pointed by `=>`, it should work. – Vladimir F Героям слава Jun 08 '17 at 18:53