Computing: DOS, OS/2 & Windows Programming

Mixing Fortran and C programming with GCC.

There are lots of articles on the Internet that deal with mixed programming using C and Fortran. I past a rather long time to study them and to try to use the code examples in my own programs. In fact, there are two problems with these articles (at least for me). First, the way to proceed depends on the compilers used, and a major part of them deals with Intel Fortran rather than with GFortran. Second, I find that most of these articles are confusing, difficult to understand, and I'm even not sure if all of the code published really works correctly.

The following text is not really a tutorial about mixed C and Fortran programming. It's primarily a series of simple examples, that show how a C function can be called by a Fortran program, resp. how a Fortran subroutine or function can be called by a C program. The way to proceed, described here, is probably not best practice, but it has the advantage to be very easy to implement, without having to consider complicated structures like interfaces. It's important to note that the article applies to GCC and GFortran on Windows. The programming environment used is the MSYS2 toolchain of the MinGW-w64 Project; the MSYS2 subsystem actually used being UCRT64. The installation and usage of this environment on Windows 10 is described in my tutorial Using Sublime Text as IDE for GCC/GFortran development. All samples published here have been successfully tested on Windows 10 (I'm quiet sure that they also work correctly on Windows 11) using the UCRT64 subsystem of MSYS2 2024-01-13 (Sublime Text is not used here; all builds have been done in the UCRT64 shell).

Calling a C function from GFortran.

Calling GCC from GFortran is not a big deal if you remember these three points:

  1. GFortran external references are case-dependent. In fact, the compiler uses all lowercase function names. Thus, name your C functions, using lowercase letters only.
  2. The GFortran compiler adds by default a terminal underscore to the name of external references. Thus, name your C functions, using an underscore as last character.
  3. Fortran passes function arguments by reference. Thus, in the declaration of the C function, use pointers instead of "ordinary" variables.

Example 1.

In our first example the Fortran program fortran_c_test.f90 passes an array of 10 integers to the C routine write_array.c, that prints out the array elements. The C function should have two arguments: the array to be printed, and the length (= number of elements) of this array.

Here is the code of write_array.c.

    #include <stdio.h>
    void write_array_(int array[], int *array_len) {
        int i;
        for (i = 0; i < *array_len; i++) {
            printf("%d ", array[i]);
        }
    }

Concerning the function declaration, note the following:

The rest of the code is standard C...

Here is the code of the calling GFortran program fortran_c_test.f90.

    program fortran_c_test
        integer :: arrlen, arr(10)
        arrlen = 10
        arr = (/10, 20, 30, 40, 50, 60, 70, 80, 90, 100/)
        call write_array(arr, arrlen)
    end program fortran_c_test

The code is all standard Fortran code. I suppose that the external function should be declared (?), but this is not mandatory. The compiler doesn't care if there are some unresolved references, and when linking the whole together, the linker resolves the reference to the C function. A C void function corresponds to a Fortran subroutine, so we have to use a call instruction. Note, that in the GFortran program the function name must not have a terminal underscore (this one will be added by the compiler)!

There are 3 steps to build the executable fortran_c_test.exe:

  1. Compiling the C routine using gcc (creating an object file). Important to note that this has to be done using the -fno-leading-underscore command line parameter.
  2. Compiling the Fortran program using gfortran (creating another object file).
  3. Building the executable with gfortran using the 2 object files as input.

In our case:
    gcc -c -fno-leading-underscore write_array.c
    gfortran -c fortran_c_test.f90
    gfortran fortran_c_test.o write_array.o -o fortran_c_test.exe

The screenshot shows the build and the program execution. Remember that in UCRT64, you'll have to prefix the executable with "./", as the current directory is not automatically part of the executables path, as is normally the case on Windows.

Mixed GCC and GFortran programming on Windows: Passing an array from Fortran to C

Example 2.

In this example the Fortran program fortran_c_test2.f90 passes a string (array of characters) to the C routine word_count.c, that counts and prints out the number of words in that string.

Passing a string from Fortran to C is more complicated than passing other data types. That's obvious, as they are differently implemented in these two programming languages. In Fortran, a string is an array of characters with a defined array length; in C a string is a dynamic array of characters, the string being delimited by a null character. As a consequence, the Fortran string has to be adapted before we can pass it to a C routine. This can be done by the instruction trim(<string-name>)//char(0)

But, there is another particularity when passing a string from Fortran to C: the string is passed together with its length, what is automatically done, so that in the Fortran program, the C function is called with one argument (the string), whereas in the C module, the function declaration must have two arguments: the string (an array pointer), and the string length (an integer). Important to note that this string length is passed by value, thus in the C routine must be declared as an integer (and not as an integer pointer, as is the case when passing "normal" arguments).

Here is the code of word_count.c.

    #include <stdio.h>
    void word_count_(char *str, int str_len) {
        int count, i;
        count = 0;
        for (i = 0; i < str_len; i++) {
            if (str[i] == ' ') {
                count++;
            }
        }
        count++;
        printf("The sentence '%s' is composed of %u words", str, count);
    }

Note the extra integer argument "str_len" in the function definition. As this variable contains the declared length of the string, we can only use it in the element print-out for loop, if it actually is the effective length of the string (what is the case here). Otherwise, we would have to consider the null terminator character and leave the loop when it is encountered.

Note: It's obvious that the word counting code is elementary. In fact, the routine counts the number of spaces and supposes that the number of words is one more than there are spaces...

Here is the code of the calling GFortran program fortran_c_test2.f90.

    program fortran_c_test2
        character(len=50) :: sentence
        sentence = "Calling a C function from Fortran is really easy!"
        call word_count(trim(sentence)//char(0))
    end program fortran_c_test2

The fixed length Fortran array of characters is transformed to a "C-like" array of characters, before passing it to the C routine. Note, that the function in the Fortran program must have one single argument (the string); the length of the string passed to C argument is automatically added by the compiler (similarly as the underscore of the function name).

The screenshot shows the build and the program execution.

Mixed GCC and GFortran programming on Windows: Passing a string from Fortran to C

Example 3.

This example does the same as the preceding one, but this time, the C routine is implemented as a C function (fword_count.c), that returns the word count to the Fortran program fortran_c_test3.f90, that displays it. As there is a direct correspondence between the C data type int and the Fortran data type integer, both the C and the Fortran code are just as they would be with a single language program.

Here is the code of fword_count.c.

    #include <stdio.h>
    int fword_count_(char *str, int str_len) {
        int count, i;
        count = 0;
        for (i = 0; i < str_len; i++) {
            if (str[i] == ' ') {
                count++;
            }
        }
        count++;
        return(count);
    }

And the code of the calling GFortran program fortran_c_test3.f90.

    program fortran_c_test3
        character(len=50) :: sentence
        integer :: wordcount
        integer :: fword_count
        sentence = "Calling a C function from Fortran is really easy!"
        wordcount = fword_count(trim(sentence)//char(0))
        write (*, "(A, A49, A, I2, A)") "The sentence '", sentence, "' is composed of ", wordcount, " words"
    end program fortran_c_test3

As you would do if the function was written in Fortran, "fword_count" has to be declared as being of type integer...

Example 4.

In this example, the Fortran program fortran_c_test4.f90 reads a name from the keyboard and passes it to the C routine hello_user.c, that uses it to construct a greeting message that is supposed to be displayed by the Fortran program. Typically, this would be implemented using a C function returning a string value. I did not succeed in doing this. Perhaps because I didn't try really hard. In fact, why make life complicated, if you can have it easy? Just implement the C routine as a void function with two string arguments: the name (passed from Fortran to C), and the greeting message (filled in by the C routine).

There is however a problem here. The greeting string has been defined with fixed length in the Fortran program, thus if we print it out, it will be printed using this length. With the result that on the display, the greeting message will be followed by several "garbage characters". We could avoid this by setting the not used characters of the greeting message to spaces in the C routine. But, there is a more elegant way. Fortran allows the usage of character substrings, defined as any contiguous section of a character variable (a part of an array of characters, from a starting to an ending position, so to say). This means that all that the Fortran program has to know to be able to display the greeting message properly, is the effective string length. This length is known by the C routine, that constructs the greeting. So, all we have to do is to add a third argument to our hello_user function: the effective length of the greeting message (filled in by the C routine).

Here is the code of hello_user.c.

    #include <stdio.h>
    #include <string.h>
    void hello_user_(char *user, char *greeting, int *greeting_len, int ulen, int glen) {
        strcpy(greeting, "Hello, ");
        strcat(greeting, user);
        strcat(greeting, "!");
        *greeting_len = strlen(greeting);
    }

Looking at the arguments of the function: "*user" is the user name, passed by the Fortran program; "*greeting" is the greeting message, constructed by the C routine (and displayed by the Fortran program after the return from the C routine); "*greeting_len" is the real length of the greeting message, determined by the C routine (and used by the Fortran program to properly display the greeting after the return from the C routine); "ulen" and "glen" are the lengths of the two character arrays, passed automatically by the GFortran compiler (note that these extra arguments are always past at the end of the arguments list).

Here is the code of the calling GFortran program fortran_c_test4.f90.

    program fortran_c_test4
        character(len=50) :: user
        character (len=57) :: greeting
        integer :: glen
        write(*, "(A, $)") "Enter your name? "
        read(*, *) user
        user = trim(user)//char(0)
        call hello_user(user, greeting, glen)
        write(*, "(A)") greeting(1:glen)
    end program fortran_c_test4

Note the usage of a character substring to print out only the valid portion of the greeting message, i.e. the characters from the beginning of the string, over a number of characters equal to its effective length: "greeting(1:glen)"

Note: As the length of the name is limited to 50 characters, there is no risk that the greeting message constructed here could exceed the length of this variable (as declared in the Fortran program). A more realistic way to proceed would be to test this using the variable "glen", that contains the declared length of the Fortran string.

The screenshot shows the build and the program execution.

Mixed GCC and GFortran programming on Windows: Value assignment to a Fortran string by a C function

Calling a GFortran function from C.

Some general guidelines to call a GFortran subroutine/function from a C program:

  1. Include the Fortran routine(s) in a module and add a use iso_c_binding for this module.
  2. In the Fortran subroutine/function, bind all arguments to C, using "C-like" variables, as for example integer (C_INT), character (C_CHAR). In the case of a function, bind this function to C, when declaring it.
  3. The Fortran subroutine/function has to be declared as external in the "calling" C program.
  4. Fortran awaits the arguments passed by reference, thus pass the address of the variable instead of the variable itself.
  5. The return from a Fortran function is a pointer, not the return value itself. Thus, in the C program, declare the Fortran function as being of a pointer type.

Example 5.

This sample program does the same as our very first example (printing out the elements of an array), but this time, the array is defined in a C main program, that calls a Fortran subroutine to do the print-out. The subroutine requires 2 arguments: the array length, and the array to be printed.

Here is the code of the GFortran module fortran_module.f90.

    module myModule
        use iso_c_binding
        contains
        subroutine write_array(arrlen, arr) bind(C)
            integer (C_INT), bind(C) :: arrlen, arr(arrlen)
            integer :: i
            do i = 1, arrlen
                write (*, "(2X, I2, $)") arr(i)
            enddo
        end subroutine
    end module

Note the declarations of the subroutine's arguments: the array length as an integer of type C_INT, the array itself as an array of integers of type C_INT and of fixed length, as given by the first subroutine argument. The rest of the code is standard Fortran...

Here is the code of the "calling" C program c_fortran_test.c.

    #include <stdio.h>
    int arraylen = 10;
    int array[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
    extern void write_array();
    int main() {
        write_array(&arraylen, array);
        return 0;
    }

Note the declaration of the Fortran subroutine as extern void. The call of the subroutine is a void function with two arguments: the array length has to be given as the address of the corresponding variable: "&arraylen". The array itself can be passed as such, as C arrays are always pointers. The return 0 is optional in C; just declare the main routine as void main() if you omit it.

There are 3 steps to build the executable c_fortran_test.exe:

  1. Compiling the Fortran module using gfortran (creating an object file).
  2. Compiling the C program using gcc (creating another object file).
  3. Building the executable with gcc using the 2 object files as input. Important to note, that you also have to link in the gfortran library!

In our case:
    gfortran -c fortran_module.f90
    gcc -c c_fortran_test.c
    gcc c_fortran_test.o fortran_module.o -lgfortran -o c_fortran_test.exe

The screenshot shows the build and the program execution.

Mixed GCC and GFortran programming on Windows: Passing an array from C to Fortran

Example 6.

In this example, the Fortran module contains two integer functions, that calculate the minimum, resp. the maximum value in an array. The "calling" C program passes the array length and the array to the Fortran functions, and retrieves the array minimum/maximum as function return value.

Here is the code of the GFortran module fortran_module2.f90.

    module myModule
        use iso_c_binding
        contains
        integer function array_min(arrlen, arr) bind(C)
            integer (C_INT), bind(C) :: arrlen, arr(arrlen)
            integer :: arrmin, i
            arrmax = arr(1)
            do i = 2, arrlen
                if (arr(i).lt.arrmin) arrmin = arr(i)
            enddo
            array_min = arrmin
        end function
        integer function array_max(arrlen, arr) bind(C)
            integer (C_INT), bind(C) :: arrlen, arr(arrlen)
            integer :: arrmax, i
            arrmax = arr(1)
            do i = 2, arrlen
                if (arr(i).gt.arrmax) arrmax = arr(i)
            enddo
            array_max = arrmax
        end function
    end module

The array length and the array itself are passed as arguments just as in the preceding program sample. Note, the declarations of the the two functions with the bind(C) attribute.

Here is the code of the "calling" C program c_fortran_test2.c.

    #include <stdio.h>
    int arraylen = 10;
    int array[10] = { 6, 2, 5, 10, 8, 1, 7, 3, 4, 9 };
    int *min, *max;
    extern int *array_min();
    extern int *array_max();
    int main() {
        min = array_min(&arraylen, array);
        max = array_max(&arraylen, array);
        printf("Minimum = %2i\n", min);
        printf("Maximum = %2i\n", max);
        return 0;
    }

Concerning the two functions: First, they have to be declared as extern; second, they have to be declared as integer pointer functions (and not as integer functions): extern int *array_min() and extern int *array_max(). The variables, which the function result is assigned to, must of course also be pointers: int *min, *max. The passage of the two arguments is similar to the case described in the preceding example.

The screenshot shows the build and the program execution.

Mixed GCC and GFortran programming on Windows: Returning an integer from Fortran to C

Example 7.

This is an example of passing a string from a C program to a Fortran subroutine. The C program asks for a name and passes it to Fortran, that generates and displays a greeting message using this name, if it is not empty. The subroutine requires 2 arguments: the string and its length.

Here is the code of the GFortran module fortran_module3.f90.

    module myModule
        use iso_c_binding
        contains
        subroutine hello(sname, namelen) bind(C)
            character (C_CHAR), bind(C) :: sname(*)
            integer (C_INT), bind(C) :: namelen
            if (namelen .eq. 0) then
                write (*, *) "Hello, World!"
            else
                write (*, *) "Hello, ", sname(1:namelen), "!"
            endif
        end subroutine
    end module

The Fortran character array of type C_CHAR is bind to C and by this must not include a fixed length in its declaration. A proper output of the string is obtained by using a character substring (just the way we did in program sample 4).

Here is the code of the "calling" C program c_fortran_test3.c.

    #include <stdio.h>
    #include "strings.h"
    int namelen;
    char name[26];
    extern void hello();
    int main() {
        printf("Please, enter your name? ");
        fgets(name, 26, stdin);
        if (strlen(name) >= 25) {
            namelen = strlen(name);
        }
        else {
            namelen = strlen(name) - 1;
        }
        hello(name, &namelen);
        return 0;
    }

Concerning the call to the Fortran subroutine "hello" (declared as extern void): The string (an array of characters) is passed as such, as C arrays are always pointers. The string length, on the other hand, has to be passed as the address of the corresponding integer variable.

What are these calculations of the string length to be passed to the Fortran routine for, you may ask. They are in relationship with the fgets function, that when returning the string entered by the user includes an end-of-line character. Thus, the string length that has (normally) to be passed to the Fortran routine is the length of the input string minus 1. However, if the length of the input string equals the maximum of characters permitted (25 in our case), subtracting 1 from the input string length would result in a name display with the last character missing. I think that the reason for this is that there is no end-of-line character in this case, and thus the full string length has to be passed to the Fortran subroutine.

The screenshot shows the build and the program execution.

Mixed GCC and GFortran programming on Windows: Passing a string from C to Fortran

Example 8.

In this example, the C program passes a string to a Fortran routine that changes all letters of the string to uppercase. The display of the original string and the converted uppercase string is done by the C program. This would normally be an example of a Fortran function returning a string. But, in order to avoid all complications, I chose to do it the same way as in example 4. Instead of implementing a function, I implemented a subroutine with 3 arguments: the first one to pass the original string, the second one to retrieve the uppercase string, the third one to pass the string length (identical for both string arguments).

Here is the code of the GFortran module fortran_module4.f90.

    module myModule
        use iso_c_binding
        contains
        subroutine upper_case(str, str_upper, str_len) bind(C)
            character (C_CHAR), bind(C) :: str(*), str_upper(*)
            integer (C_INT), bind(C) :: str_len
            integer, parameter :: asc = ichar('A') - ichar('a')
            character :: ch
            integer :: i
            do i = 1, str_len
                ch = str(i)
                if (ch >= 'a' .and. ch <= 'z') ch = char(ichar(ch) + asc)
                str_upper(i) = ch
            enddo
        end subroutine
    end module

Nothing special to say about this code. Except for the C bindings, it's standard Fortran...

Here is the code of the "calling" C program c_fortran_test4.c.

    #include <stdio.h>
    int len = 31;
    char text[32] = "Hello world from C and FORTRAN!";
    char text_upper[32];
    extern void upper_case();
    int main() {
        upper_case(text, text_upper, &len);
        printf("%s\n", text);
        printf("%s\n", text_upper);
        return 0;
    }

Here, too, nothing that we haven't already seen in previous examples. Just note that the length of the literal is 31 characters (length to be passed to the Fortran subroutine), and that the corresponding character array has 32 elements, one being needed for the null terminator automatically added by the C compiler.

The screenshot shows the build and the program execution.

Mixed GCC and GFortran programming on Windows: Modification of a C string by Fortran

Example 9.

This last example implements a C program that declares an array of floating point numbers, displays this array, then passes it to a GFortran bubble sort subroutine. At the return from the subroutine, the original values of the array are replaced by the sorted one; the sorted array is printed out by the C program.

Here is the code of the GFortran module fortran_module5.f90.

    module myModule
        use iso_c_binding
        contains
        subroutine sort(arrlen, array) bind(C)
            integer (C_INT), bind(C) :: arrlen
            real (C_FLOAT), bind(C) :: array(arrlen)
            real :: temp
            integer :: i, j
            logical :: swapped
            do i = 1, arrlen - 1
                swapped = .false.
                do j = i + 1, arrlen
                    if (array(i) > array(j)) then
                        temp = array(i)
                        array(i) = array(j)
                        array(j) = temp
                        swapped = .true.
                    endif
                enddo
                if (.not. swapped) exit
            enddo
        end subroutine
    end module

As in other examples, the subroutine has two arguments: the array length and the array itself. Using the data type float in the C program, the data type of the Fortran array has to be real (C_FLOAT). The length of the Fortran array is given by "arrlen", that is the array length passed by the "calling" C program.

Just as Fortran integer is compatible with C int, Fortran real is compatible with C float. Thus, the array elements may be assigned without problems to the real variable "temp".

Here is the code of the "calling" C program c_fortran_test5.c.

    #include <stdio.h>
    int arraylen = 18;
    float array[18] = { 6.6, 9.9, 4.4, -4.4, -6.6, 1.1, -8.8, 8.8, -2.2, 2.2, -9.9, 3.3, -7.7, 7.7, -3.3, 5.5, -5.5, -1.1 };
    int i;
    extern void sort();
    int main() {
        printf("\n");
        for (i = 0; i < arraylen; i++) {
            printf("%.1f ", array[i]);
        }
        printf("\n");
        sort(&arraylen, array);
        for (i = 0; i < arraylen; i++) {
            printf("%.1f ", array[i]);
        }
        printf("\n\n");
        return 0;
    }

Nothing not already seen in this piece of code...

The screenshot shows the program output.

Mixed GCC and GFortran programming on Windows: Fortran sorting an array passed by C

Custom build batch files.

To be able to build the C and Fortran mixed programs using one simple command, I created the batch files fcbuild.bat, to build a main Fortran program calling a C function, and cfbuild.bat, to build a main C program calling a Fortran subroutine/function. Both scripts have three command line parameters, the first one (-r) being optional; this parameter, when stated, runs the program if the build has been successful. Here how to use the scripts:
    fcbuild [-r] <fortran-source-name> <c-source-name>
    cfbuild [-r] <c-source-name> <fortran-source-name>
Filenames have to be specified without file extension (.f90 and .c are assumed). The old object files and the old executable are deleted before the new build is done. If an error occurs at any stage of the build, the scripts exit at that point.

Here is the code of my fcbuild.bat:

    @echo off
    if "%1"=="-r" set run=true
    if "%1"=="-R" set run=true
    if "%run%"=="true" shift
    if "%1"=="" goto NoFSource
    if "%2"=="" goto NoCSource
    if not exist %1.f90 goto ErrorFSource
    if not exist %2.c goto ErrorCSource
    if exist %1.o del %1.o
    if exist %2.o del %2.o
    if exist %1.exe del %1.exe
    gcc -c -fno-leading-underscore %2.c
    if not exist %2.o goto Exit
    gfortran -c %1.f90
    if not exist %1.o goto Exit
    gfortran %1.o %2.o -o %1.exe
    if not exist %1.exe goto Exit
    if "%run%"=="" goto Exit
    %1.exe
    goto Exit
    :NoFSource
    echo Fortran source file argument missing!
    goto Exit
    :NoCSource
    echo C source file argument missing!
    goto Exit
    :ErrorFSource
    echo Fortran source file %1.f90 not found!
    goto Exit
    :ErrorCSource
    echo C source file %2.c not found!
    :Exit
    set run=

And the code of my cfbuild.bat:

    @echo off
    if "%1"=="-r" set run=true
    if "%1"=="-R" set run=true
    if "%run%"=="true" shift
    if "%1"=="" goto NoCSource
    if "%2"=="" goto NoFSource
    if not exist %1.c goto ErrorCSource
    if not exist %2.f90 goto ErrorFSource
    if exist %1.o del %1.o
    if exist %2.o del %2.o
    if exist %1.exe del %1.exe
    gfortran -c %2.f90
    if not exist %2.o goto Exit
    gcc -c %1.c
    if not exist %1.o goto Exit
    gcc %1.o %2.o -lgfortran -o %1.exe
    if not exist %1.exe goto Exit
    if "%run%"=="" goto Exit
    %1.exe
    goto Exit
    :NoCSource
    echo C source file argument missing!
    goto Exit
    :NoFSource
    echo Fortran source file argument missing!
    goto Exit
    :ErrorCSource
    echo C source file %1.c not found!
    goto Exit
    :ErrorFSource
    echo Fortran source file %2.f90 not found!
    :Exit
    set run=

If you place these files somewhere in your executables path (I placed them in C:\Programs\msys64\ucrt64\bin, the ucrt64\bin folder of my MSYS2 installation directory), you can use the scripts in the UCRT64 shell to build the tutorial examples and similar mixed programs.

And to terminate, here is the link to download all tutorial files (all C and Fortran sources, and the two batch files).


If you find this text helpful, please, support me and this website by signing my guestbook.