# Difference in BIND(C) behavior for assumed-size character arrays

**URL:** <https://fortran-lang.discourse.group/t/difference-in-bind-c-behavior-for-assumed-size-character-arrays/9151>\
**Category:** Help\
**Created:** [January 30, 2025, 8:32pm UTC](https://fortran-lang.discourse.group/t/difference-in-bind-c-behavior-for-assumed-size-character-arrays/9151 "2025-01-30T20:32:49Z")\
**Posts on this page:** 13\
**Page:** 2

<div class="post-metadata">

**Author:** ![septc](https://yyz2.discourse-cdn.com/free1/user_avatar/fortran-lang.discourse.group/septc/32/77_2.png) [@septc](https://fortran-lang.discourse.group/u/septc)\
**Post date:** [January 31, 2025, 5:04pm UTC](https://fortran-lang.discourse.group/t/difference-in-bind-c-behavior-for-assumed-size-character-arrays/9151/21 "2025-01-31T17:04:03Z")

</div>

> [@wspector](#):
>
> One nuance with the hidden string length argument is whether it should be declared on the C side as an int, long, long long, or size\_t. Compilers differ…

I think it should be possible to write an interface in Fortran for C routines that do not care about Fortran, because, for example, one may want to interoperate with an existing shared library that contains the following C function (with no “hidden” argument)…

```c
void check_argument(char name[]);

```

---

<div class="post-metadata">

**Author:** ![wspector](https://avatars.discourse-cdn.com/v4/letter/w/47e85d/32.png) [@wspector](https://fortran-lang.discourse.group/u/wspector)\
**Post date:** [January 31, 2025, 5:09pm UTC](https://fortran-lang.discourse.group/t/difference-in-bind-c-behavior-for-assumed-size-character-arrays/9151/22 "2025-01-31T17:09:27Z")

</div>

> [@septc](#):
>
> I think it should be possible to write an interface in Fortran for C routines that do not care about Fortran, because, for example, one may want to interoperate with an existing shared library that contains the following C function (with no “hidden” argument)…
> 
> ```auto
> void check_argument(char name[]);
> 
> ```

On the C side, it can simply ignore the fact that the Fortran compiler is adding the second, “hidden”, length argument. Remember that C supports varargs. So the number of arguments potentially passed into any given C function isn’t written in stone.

---

<div class="post-metadata">

**Author:** ![RonShepard](https://avatars.discourse-cdn.com/v4/letter/r/a3d4f5/32.png) [@RonShepard](https://fortran-lang.discourse.group/u/RonShepard)\
**Post date:** [January 31, 2025, 5:33pm UTC](https://fortran-lang.discourse.group/t/difference-in-bind-c-behavior-for-assumed-size-character-arrays/9151/23 "2025-01-31T17:33:01Z")

</div>

> [@wspector](#):
>
> Remember that C supports varargs. […]

NOTE 2 in section 18.3.7 of the fortran standard says, “The C language allows specification of a C function that can take a variable number of arguments (ISO/IEC 9899:2018, 7.16). This document does not provide a mechanism for Fortran procedures to interoperate with such C functions.” So that feature is not supported by fortran. On the other hand, optional arguments in fortran are supported within C interoperability by adding null arguments as appropriate to fill out the argument list.

---

<div class="post-metadata">

**Author:** ![wspector](https://avatars.discourse-cdn.com/v4/letter/w/47e85d/32.png) [@wspector](https://fortran-lang.discourse.group/u/wspector)\
**Post date:** [January 31, 2025, 5:42pm UTC](https://fortran-lang.discourse.group/t/difference-in-bind-c-behavior-for-assumed-size-character-arrays/9151/24 "2025-01-31T17:42:14Z")

</div>

> [@RonShepard](#):
>
> NOTE 2 in section 18.3.7 of the fortran standard says, “The C language allows specification of a C function that can take a variable number of arguments (ISO/IEC 9899:2018, 7.16). This document does not provide a mechanism for Fortran procedures to interoperate with such C functions.” So that feature is not supported by fortran. On the other hand, optional arguments in fortran are supported within C interoperability by adding null arguments as appropriate to fill out the argument list.

True. But my point is that on the C side, it doesn’t necessarily know that a Fortran procedure is calling it.

---

<div class="post-metadata">

**Author:** ![septc](https://yyz2.discourse-cdn.com/free1/user_avatar/fortran-lang.discourse.group/septc/32/77_2.png) [@septc](https://fortran-lang.discourse.group/u/septc)\
**Post date:** [January 31, 2025, 5:42pm UTC](https://fortran-lang.discourse.group/t/difference-in-bind-c-behavior-for-assumed-size-character-arrays/9151/25 "2025-01-31T17:42:43Z")

</div>

In the following code, I’ve tried to test the behavior of different compilers for hidden argument passing, though I am not very sure whether this is a valid code (I don’t know how to compile two source files with CompilerExplorer, so tried to include everything in one source.) And, the result is that (1) gfortran, ifort, and ifx seem to pass no hidden variable, (2) nvfortran passes a hidden length variable, (3) LLVM-flang complains about my code 😅, and (4) Lfortran also complains about my code (in a more complicated way 😆). I guess it would be better to use different source files + real C codes (rather than mimicking it with Fortran single source)…

> **[Compiler Explorer - Fortran](https://godbolt.org/z/bTedPeK7f)**
>
> module test\_m
> use :: iso\_c\_binding, only: c\_char, c\_long
> implicit none
> 
> interface
> subroutine sub( arr ) bind(C,name="fsub\_")
> import; implicit none
> character(kind=c\_char), dimension(\*) :: arr
> end
> end...

```fortran
module test_m
    use :: iso_c_binding, only: c_char, c_long
    implicit none

    interface
    subroutine sub( arr ) bind(C,name="fsub_")
        import; implicit none
        character(kind=c_char), dimension(*) :: arr
    end
    end interface
end module

subroutine fsub( arr, n )
    use iso_c_binding
    implicit none
    character(kind=c_char), dimension(*) :: arr
    integer(c_long), value :: n
    print *, "arr = ", arr(1:5)
    print *, "n = ", n
end

program main
    use test_m
    implicit none

    call sub( "hello" )
end

```

Result:

```auto
>>> gfortran14.2
arr = hello
n = 140732106695248

>>> ifort 2021.11.0
arr = hello
n = -1906913433109000205

>>> ifx 2024.0.0
arr = hello
n = -1906913433109000205

>>> nvfortran 25.1
arr = hello
n = 5

```

EDIT: I’ve updated the code such that it always prints the first 5 characters of `arr` to check whether the address of the 1st argument is passed. All compilers pass the latter (as expected), while the behavior is different for hidden length variable. To be C interoperable, I think the information on the length of the character string should be passed to C either via null char at the end of the string, or via an explicit integer argument, so as to respect the signature of the C function (which may be compiled independently of Fortran, even by using a companion C compiler).

---

<div class="post-metadata">

**Author:** ![jwmwalrus](https://yyz2.discourse-cdn.com/free1/user_avatar/fortran-lang.discourse.group/jwmwalrus/32/4483_2.png) [@jwmwalrus](https://fortran-lang.discourse.group/u/jwmwalrus)\
**Post date:** [January 31, 2025, 6:09pm UTC](https://fortran-lang.discourse.group/t/difference-in-bind-c-behavior-for-assumed-size-character-arrays/9151/26 "2025-01-31T18:09:51Z")

</div>

I tested the following locally (not through Compiler Explorer).

This C code is a slight variation of the one provided by the OP through a link (I just added a new `printf` and modified the exiting one):

```c
#include <stdio.h>

void check_argument(char name[]);

void check_argument(char name[]) {
    printf("arg: %s\n", name);
    if (name[0] != 'x')
        printf(" oops!\n");
}

```

This would be the code on the Fortran side:

```fortran
use ISO_C_BINDING

implicit none

interface
    subroutine c_check_argument_1(name) bind(C, name="check_argument")
        import
        character(kind = c_char), dimension(*) :: name
    end subroutine
end interface

call c_check_argument_1('x'//C_NULL_CHAR)
call c_check_argument_1('xx'//C_NULL_CHAR)
call c_check_argument_1('c'//C_NULL_CHAR)
end

```

And these are the results of compiling with gfortran and flang-new, and their respective “companion processors”:

```bash
$ clang -c llvm_maybe_bug_c.c && flang-new -c llvm_maybe_bug_f.f90 && flang-new -o llvm_maybe_bug llvm_maybe_bug_c.o llvm_maybe_bug_f.o

$ ./llvm_maybe_bug
arg: x
arg: xx
arg: c
  oops!

$ gcc -c llvm_maybe_bug_c.c && gfortran -c llvm_maybe_bug_f.f90 && gfortran -o llvm_maybe_bug llvm_maybe_bug_c.o llvm_maybe_bug_f.o

$ ./llvm_maybe_bug
arg: x
arg: xx
arg: c
  oops!

```

So, the interoperability produces the expected results. The code generated by `flang-new` might be half-a-microsecond slower (as per the IR diff provided elsewhere), but not wrong.

My gcc/gfortran version is 14.2.0, and my clang/flang-new version is 19.0.6.

---

<div class="post-metadata">

**Author:** ![pjfasano](https://yyz2.discourse-cdn.com/free1/user_avatar/fortran-lang.discourse.group/pjfasano/32/3091_2.png) [@pjfasano](https://fortran-lang.discourse.group/u/pjfasano)\
**Post date:** [January 31, 2025, 6:18pm UTC](https://fortran-lang.discourse.group/t/difference-in-bind-c-behavior-for-assumed-size-character-arrays/9151/27 "2025-01-31T18:18:24Z")

</div>

@septc You’re correct, and if you cajole Compiler Explorer with a bit more CMake magic, you can see that indeed `flang-trunk` and `nvfortran` pass a hidden parameter, while `gfortran`, `ifort`, and `ifx` seem not to:

> **[Compiler Explorer - Fortran](https://godbolt.org/z/W81e8v5eP)**
>
> cmake\_minimum\_required(VERSION 3.10)
> project(example Fortran C)
> add\_executable(main example.c example.f90)

@jwmwalrus Okay, so I think I see that this is exploiting the fact that the C calling convention allows for additional (ignored) arguments to functions, so once this gets lowered to ASM it is interoperable. However, if you look at the LLVM IR, there is not that flexibility in calling conventions, and thus the generated IR is not interoperable. This means that if you are doing, e.g., whole-program optimization as an LLVM pass, you’ll run into a call mismatch. So yes, this is potentially a bug in `flang` but it’s entirely internal (to LLVM) in terms of how `flang` compiles to LLVM IR.

---

<div class="post-metadata">

**Author:** ![septc](https://yyz2.discourse-cdn.com/free1/user_avatar/fortran-lang.discourse.group/septc/32/77_2.png) [@septc](https://fortran-lang.discourse.group/u/septc)\
**Post date:** [January 31, 2025, 6:31pm UTC](https://fortran-lang.discourse.group/t/difference-in-bind-c-behavior-for-assumed-size-character-arrays/9151/28 "2025-01-31T18:31:30Z")

</div>

It seems that C ignore the implicitly passed length variable (in the case of LLVM-flang), so generating a seemingly correct behavior. But my concern is whether such an implicit passed extra variable can cause possible issues (when those variables are not defined in the C function with no “…”). I would like to learn more about this point (including the treatment of variadic arguments on the C side). FWIW, the result of Perplexity was something like this, though I am not sure to what extent the contents are correct…

- [Result from Perplexity](https://www.perplexity.ai/search/in-c-language-is-it-legitimate-OlqQ5Gt5RWqHHiFsc090BQ)

> (my prompt) In C language, is it legitimate to pass an extra argument to a function that is not declared in the function definition?

> (response) In C, passing extra arguments to a function that does not declare them is generally not legitimate in terms of compiler checks, but it may not always result in a compile-time error. Here’s why:
> 
> 1. **Function Prototypes and Definitions** : If a function is defined in the same file where it is called, the compiler will typically check the number and types of arguments against the function definition and throw an error if they do not match. However, if the function is declared in a header file or elsewhere without a prototype, the compiler may not enforce this check at compile time.
> 
> 2. **Variable Argument Lists** : C supports variable argument lists using the `...` syntax in function declarations. Functions like `printf` use this feature, allowing them to accept a variable number of arguments. However, this is explicitly declared in the function definition and is not applicable to functions without such a declaration.
> 
> 3. **ABI and Calling Conventions** : The behavior of passing extra arguments can depend on the Application Binary Interface (ABI) and calling conventions used by the compiler. In many cases, extra arguments are simply ignored if they are not accessed within the function. However, this can lead to undefined behavior if the function attempts to access memory beyond its declared parameters.
> 
> 4. **Compile-Time vs. Runtime Checks** : C compilers typically do not perform runtime checks for argument counts unless explicitly programmed to do so. Therefore, while passing extra arguments might not cause a compile-time error, it can lead to runtime issues if the function accesses memory incorrectly.
> 
> In summary, while passing extra arguments to a C function might not always result in a compile-time error, it is generally not recommended and can lead to undefined behavior. It is best to ensure that the number and types of arguments passed to a function match its declaration.
> 
> Citations:  
> [1] [Setting properties of a function's arguments — The Rocq Prover 9.1+alpha documentation](https://coq.inria.fr/doc/master/refman/language/extensions/arguments-command.html)  
> [2] [[Tech Story] The NeverEnding Story of writing a rule for argument passing in C++ - Sonar Updates - Sonar Community](https://community.sonarsource.com/t/tech-story-the-neverending-story-of-writing-a-rule-for-argument-passing-in-c/9762)  
> [3] [Functions with Variable Argument Lists (C++) | Microsoft Learn](https://learn.microsoft.com/en-us/cpp/cpp/functions-with-variable-argument-lists-cpp?view=msvc-170)  
> [4] [https://www.mathworks.com/help/matlab/matlab\_prog/function-argument-validation-1.html](https://www.mathworks.com/help/matlab/matlab_prog/function-argument-validation-1.html)  
> [5] [language design - Why does C++ allow extra arguments to printf? - Programming Language Design and Implementation Stack Exchange](https://langdev.stackexchange.com/questions/3341/why-does-c-allow-extra-arguments-to-printf)  
> [6] [Effective Go - The Go Programming Language](https://go.dev/doc/effective_go)  
> [7] [Reddit - Dive into anything](https://www.reddit.com/r/C_Programming/comments/t22ycw/what_happens_if_i_forcibly_pass_extra_arguments/)  
> [8] [gcc - Why does passing extra arguments to a C function does not result in a compile-time error? - Stack Overflow](https://stackoverflow.com/questions/27324010/why-does-passing-extra-arguments-to-a-c-function-does-not-result-in-a-compile-ti)

---

<div class="post-metadata">

**Author:** ![pjfasano](https://yyz2.discourse-cdn.com/free1/user_avatar/fortran-lang.discourse.group/pjfasano/32/3091_2.png) [@pjfasano](https://fortran-lang.discourse.group/u/pjfasano)\
**Post date:** [January 31, 2025, 7:45pm UTC](https://fortran-lang.discourse.group/t/difference-in-bind-c-behavior-for-assumed-size-character-arrays/9151/29 "2025-01-31T19:45:30Z")

</div>

@septc also interesting: if you don’t declare `sub` as `bind(C)`, `gfortran`/`ifort`/`ifx` all _do_ pass an extra parameter (i.e. in “Fortran calling convention”).

> **[Compiler Explorer - Fortran](https://godbolt.org/z/YEWP7zj3q)**
>
> cmake\_minimum\_required(VERSION 3.10)
> project(example Fortran C)
> add\_executable(main example.c example.f90)

Or, to turn it around the other way, **for `flang` and `nvfortran` adding `bind(C)` to the interface declaration does nothing** whereas for `gfortran`/`ifort`/`ifx` adding `bind(C)` removes the hidden parameter.

---

<div class="post-metadata">

**Author:** ![jwmwalrus](https://yyz2.discourse-cdn.com/free1/user_avatar/fortran-lang.discourse.group/jwmwalrus/32/4483_2.png) [@jwmwalrus](https://fortran-lang.discourse.group/u/jwmwalrus)\
**Post date:** [January 31, 2025, 9:57pm UTC](https://fortran-lang.discourse.group/t/difference-in-bind-c-behavior-for-assumed-size-character-arrays/9151/30 "2025-01-31T21:57:39Z")

</div>

> [@pjfasano](#):
>
> Or, to turn it around the other way, **for `flang` and `nvfortran` adding `bind(C)` to the interface declaration does nothing** whereas for `gfortran`/`ifort`/`ifx` adding `bind(C)` removes the hidden parameter.

Hmm… Maybe the behavior was introduced by `pgfortran` and kept in `flang-new` and `nvfortran` for compatibility?

(That reminds me of that [old Lotus bug that MS Excel implemented](https://en.wikipedia.org/wiki/Leap_year_problem#Occurrences) on purpose 😆)

---

<div class="post-metadata">

**Author:** ![PierU](https://yyz2.discourse-cdn.com/free1/user_avatar/fortran-lang.discourse.group/pieru/32/1848_2.png) [@PierU](https://fortran-lang.discourse.group/u/PierU)\
**Post date:** [February 1, 2025, 7:41pm UTC](https://fortran-lang.discourse.group/t/difference-in-bind-c-behavior-for-assumed-size-character-arrays/9151/31 "2025-02-01T19:41:49Z")

</div>

> [@jwmwalrus](#):
>
> So, the interoperability produces the expected results.

And what if the character array is not the _last_ argument?

---

<div class="post-metadata">

**Author:** ![msz59](https://avatars.discourse-cdn.com/v4/letter/m/3d9bf3/32.png) [@msz59](https://fortran-lang.discourse.group/u/msz59)\
**Post date:** [February 1, 2025, 8:09pm UTC](https://fortran-lang.discourse.group/t/difference-in-bind-c-behavior-for-assumed-size-character-arrays/9151/32 "2025-02-01T20:09:57Z")

</div>

My understanding is that the additional _length_ parameters are always at the end of the list, regardless of the position of the _parent_ argument.

---

<div class="post-metadata">

**Author:** ![PierU](https://yyz2.discourse-cdn.com/free1/user_avatar/fortran-lang.discourse.group/pieru/32/1848_2.png) [@PierU](https://fortran-lang.discourse.group/u/PierU)\
**Post date:** [February 2, 2025, 11:42am UTC](https://fortran-lang.discourse.group/t/difference-in-bind-c-behavior-for-assumed-size-character-arrays/9151/33 "2025-02-02T11:42:32Z")

</div>

Ok. In this case the extra length argument doesn’t matter at all, as it is simply ignored on the C side. Attempting to use it on the C side would be possible, but it would not be portable.

[Previous page](https://fortran-lang.discourse.group/t/difference-in-bind-c-behavior-for-assumed-size-character-arrays/9151.md?page=1)
