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:
- GFortran external references are case-dependent. In fact, the compiler uses all lowercase function names. Thus, name your C functions, using lowercase letters only.
- 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.
- 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 name of the function has to end with an underscore: write_array_
- The function arguments have to be pointers. No problem for the array itself, as arrays are always pointers to their first element in C. On the other hand, the array length must not be declared as a simple integer, but as an integer pointer: int *array_len
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:
- 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.
- Compiling the Fortran program using gfortran (creating another object file).
- 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.
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.
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.
Calling a GFortran function from C.
Some general guidelines to call a GFortran subroutine/function from a C program:
- Include the Fortran routine(s) in a module and add a use iso_c_binding for this module.
- 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.
- The Fortran subroutine/function has to be declared as external in the "calling" C program.
- Fortran awaits the arguments passed by reference, thus pass the address of the variable instead of the variable itself.
- 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:
- Compiling the Fortran module using gfortran (creating an object file).
- Compiling the C program using gcc (creating another object file).
- 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.
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.
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.
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.
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.
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.