100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
Programming

Interfacing Fortran with C

Learn to safely call C functions from Fortran and expose Fortran routines to C using the standardized iso_c_binding module.

Practical FortranIntermediate11 min readJul 10, 2026
Analogies

Introduction to the ISO_C_BINDING Module

Modern Fortran defines a standardized way to interoperate with C through the intrinsic module iso_c_binding, introduced in Fortran 2003. It supplies portable kind parameters such as c_int, c_double, and c_float that guarantee a Fortran variable has exactly the same binary representation as the corresponding C type on the target platform, replacing the old, non-portable practice of guessing that a Fortran integer happened to match a C int. Without iso_c_binding, calling a C library from Fortran (or vice versa) relies on compiler-specific conventions that can silently break when you switch compilers or platforms.

🏏

Cricket analogy: Like agreeing on ICC's official pitch and ball specifications before an international match, so a bowler trained in Australia and a batter trained in India both know exactly what to expect, rather than each team guessing the other's local ground rules.

Calling C Functions from Fortran

To call a C function from Fortran, you write an explicit interface block declaring the function with the bind(c, name='c_function_name') attribute, matching each C parameter's type to its iso_c_binding equivalent (for example a C double* typically corresponds to a Fortran real(c_double), value or a pointer target depending on whether C expects a value or an address). The value attribute on a dummy argument is essential when the C function expects to receive an argument by value rather than by reference, since Fortran otherwise passes everything by reference by default, which would hand the C function a pointer where it expected a plain number.

🏏

Cricket analogy: Like an overseas fast bowler joining a domestic franchise: the coaching staff writes an explicit dossier translating his exact grip, run-up length, and release point into terms the local team understands, rather than assuming his technique needs no translation.

fortran
! Fortran side: calling C's sqrt() from libm via an explicit interface
module c_math
  use, intrinsic :: iso_c_binding
  implicit none
  interface
     function c_sqrt(x) bind(c, name='sqrt') result(res)
       import :: c_double
       real(c_double), value :: x
       real(c_double) :: res
     end function c_sqrt
  end interface
end module c_math

program call_c
  use c_math
  implicit none
  real(c_double) :: y
  y = c_sqrt(2.0_c_double)
  print *, 'sqrt(2) via C =', y
end program call_c

Exposing Fortran to C: the bind(c) Attribute

The interoperability is symmetric: a Fortran subroutine or function can be made callable from C by adding bind(c, name='...') to its own declaration, which fixes its external symbol name to match exactly what the C linker expects (Fortran compilers otherwise mangle names with case-folding and compiler-specific decoration). Derived types intended to cross the boundary must be declared with the bind(c) attribute too, so their member layout matches a C struct field-for-field with no hidden padding or descriptor overhead that a plain Fortran derived type (which may carry array descriptors) would otherwise introduce.

🏏

Cricket analogy: Like a domestic player being registered under one fixed, official name on the international team sheet so overseas commentators and scorers can identify him unambiguously, instead of each broadcaster using a different local nickname.

Mixing a plain (non-bind(c)) Fortran derived type into a C interface is a common and dangerous mistake: ordinary Fortran derived types can carry compiler-specific hidden metadata (for allocatable components, type-bound procedures, or array descriptors) that has no equivalent in a C struct, so the two languages will disagree about the memory layout and corrupt data silently. Always declare cross-language types with bind(c) and stick to interoperable component types.

Practical Workflow and Tooling

In practice, mixed Fortran/C projects are built by compiling each language's source with its own compiler and linking the resulting object files together (for example gfortran and gcc both produce standard object files that a single linker invocation can combine), often orchestrated by CMake's Fortran/C language support or a hand-written Makefile that tracks both toolchains. Because C has no notion of Fortran's array descriptors, arrays crossing the boundary are normally passed as a plain pointer to contiguous data (real(c_double), dimension(*) or type(c_ptr)) alongside an explicit integer size argument, since C has no built-in way to ask an incoming pointer how many elements it points to.

🏏

Cricket analogy: Like a franchise fielding both a local domestic scoring system and an international ICC scoring feed, with a dedicated liaison officer who reconciles the two into one official scoreboard, rather than assuming they merge automatically.

When passing a Fortran array to C, always pass its element count as a separate explicit integer(c_int) argument. Unlike a Fortran array (which the compiler tracks bounds for internally), a C pointer carries no built-in size information, so the receiving C function has no way to know how many elements are safely accessible unless told.

  • iso_c_binding provides portable kind parameters (c_int, c_double, etc.) guaranteeing exact binary compatibility with C types.
  • bind(c, name='...') on an interface block lets Fortran call an existing C function with a fixed, predictable symbol name.
  • The value attribute is required on dummy arguments when the C function expects an argument passed by value, not by reference.
  • bind(c) on a derived type declaration (or the Fortran side of a symbol) ensures the memory layout and name match exactly what C expects.
  • Mixing ordinary (non-interoperable) Fortran derived types into a C interface risks silent memory-layout mismatches.
  • Arrays crossing the Fortran/C boundary are passed as raw pointers plus an explicit size argument, since C has no array descriptors.
  • Mixed-language projects are typically built by compiling each language separately and linking the object files together, often via CMake.

Practice what you learned

Was this page helpful?

Topics covered

#Programming#FortranStudyNotes#InterfacingFortranWithC#Interfacing#Fortran#ISO#BINDING#StudyNotes#SkillVeris#ExamPrep