前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Fortran 与 C 数组传递的三种方式

Fortran 与 C 数组传递的三种方式

作者头像
自学气象人
发布2023-01-11 18:50:02
1.4K0
发布2023-01-11 18:50:02
举报
文章被收录于专栏:自学气象人

本文由知乎答主清风徐来提供,点击https://zhuanlan.zhihu.com/p/519709168即可跳转阅读。

01 背景

在群里闲聊的时候,有群友提出(:)不能作为 Fortran 接口传递数组给 C,于是基于经验进行了以下的尝试和解析(可能不对,欢迎指正)。

02 Fortran 数组

在高级编程语言初期,Fortran 数组设计与 C 是一致的,只要拿到数组第一个元素的地址即可,相匹配上;但随着 Fortran 在科学计算领域的发展,其没有实现链表、哈希等内置数据结构,却在数组这种适用于科学计算(矩阵线性代数)上花了不少设计,导致 F77 array(*) 与 F90 array(:) 这两种风格不同,前者与 C 兼容,实际上是地址引用(指针),后者则是 Fortran 语言的特有内置数据结构!

03 Fortran 传递数组给 C

从 02 可以推断,如果需要将 Fortran 数组传递给 C,还得是指针(地址),直接传内置数据结构(结构体)是不行的。以下给出三种传递方式,并开放在 Gitee 上:

  • Fortran 与 C 数组传递的三种方式 (gitee.com)
  • (https://gitee.com/zoziha/fortran-array-to-c)

C语言代码:

代码语言:javascript
复制
// 获取两者最大值
int max(int *two_int)
{
    int result;
    if (two_int[0] > two_int[1])
        result = two_int[0];
    else
        result = two_int[1];
        
    // 检查是否传递到 C 的数组值是对的
    printf("%d,%d", two_int[0], two_int[1]);
    return result;
}

Fortran 语言代码:

代码语言:javascript
复制
!> author: 左志华
!> date: 2022-05-25
program main

    use, intrinsic :: iso_c_binding

    !> 三种接口
    interface
        !> 接口 1:(*)
        integer function max_1(two_int) bind(c, name="max")
            integer, intent(in) :: two_int(*)
        end function max_1
        !> 接口 2(不推荐):(1)
        integer function max_2(two_int) bind(c, name="max")
            integer, intent(in) :: two_int(1)
        end function max_2
        !> 接口 3:c_ptr
        integer function max_3(two_int) bind(c, name="max")
            import
            type(c_ptr), intent(in), value :: two_int
        end function max_3
    end interface

    print *, max_1([1, 2])
    print *, max_2([1, 2])
    block
        integer, target :: i(2) = [1, 2]
        print *, max_3(c_loc(i))
    end block

end program main

!>> fpm run
! 1,2           2
! 1,2           2
! 1,2           2

一个可行的简单示例胜过千言万语,到此就结束了!

04 评论

Fortran 内置数组数据结构,优劣并存,只是希望 Fortran 与其他语言相融相通,越来越好;这一小段代码,相比 C,发现Fortran 写代码还是有点繁琐的,intent(in)valuetargetfunction语句写起来都很长,效率挺低的,字符串能力弱是刻在基因里的。

当然了,Fortran 与 C 函数可以通过指针(地址)传递数组,Fortran 与 Fortran 函数传递的方式,肯定也包括以上三种,以及新范式(:)的传递方式。

05 番外:在 Fortran 中访问 C 的本地数组变量

本贴原来主要关注在函数接口中传递数组(即访问函数堆栈中的数组变量),但有些人对在 Fortran 中访问 C 的本地数组变量感兴趣。

  • 从 Fortran 中访问 C 的本地数组变量 (gitee.com)
  • (https://gitee.com/zoziha/c2f-demo)

这时候一般分为两种情况,数组和数组指针。先讨论数组:

C 语言代码:

代码语言:javascript
复制
double x[3]; // 数组

void init()
{
    x[0] = 1;
    x[1] = 2;
    x[2] = 3;
}

void prt()
{
    printf(" 在 C 中打印:%f ", x[0]);
    printf("%f ", x[1]);
    printf("%f\n", x[2]);
}

Fortran 语言代码:

代码语言:javascript
复制
module demo

    implicit none
    real(8), bind(c) :: x(3)

    interface
        subroutine init() bind(c)
        end subroutine init
    end interface

    interface
        subroutine prt() bind(c)
        end subroutine prt
    end interface

end module demo

!> author: 左志华
!> date: 2022-10-06
program main
    use demo

    print *, "从 Fortran 读取 C 本地数组的方式 1:"
    call init()                                 ! 赋初值 1,2,3
    print *, '值:', x                          ! 从 Fortran 中访问 bind(c) 数组
    call prt()                                  ! 从 C 例程中访问数组

end program main

! 从 Fortran 读取 C 本地数组的方式 1:
! 值:1.0000000000000000        2.0000000000000000        3.0000000000000000     
! 在 C 中打印:1.000000 2.000000 3.000000

这里在 Fortran 中绑定 C 中的同名数组,从而直接访问 C 数组。

接下来,讨论数组指针,这略微有点不同:

C 语言代码:

代码语言:javascript
复制
double *y; // 数组指针

void init2()
{
    y = (double *)malloc(3 * sizeof(double));
    y[0] = 4;
    y[1] = 5;
    y[2] = 6;
}

void prt2()
{
    printf(" 在 C 中打印:%f ", y[0]);
    printf("%f ", y[1]);
    printf("%f\n", y[2]);
}

Fortran 语言代码:

代码语言:javascript
复制
module demo2

    use iso_c_binding
    implicit none
    real(8), pointer :: x2(:)
    type(c_ptr), bind(c, name='y') :: x_in_demo2

    interface
        subroutine init2() bind(c)
        end subroutine init2
    end interface

    interface
        subroutine prt2() bind(c)
        end subroutine prt2
    end interface

end module demo2
!> author: 左志华
!> date: 2022-10-06
program main
    use iso_c_binding
    use demo2

    print *, "从 Fortran 读取 C 本地数组的方式 2:"
    call init2()                                ! 赋初值 4,5,6
    call c_f_pointer(x_in_demo2, x2, shape=[3]) ! 从 C 中访问 bind(c) 数组
    print *, '地址:', x_in_demo2               ! 数组地址,即指针
    print *, '值:', x2                         ! 将 Fortran 数组指针绑定到 C 数组地址
    call prt2()                                 ! 从 C 例程中访问数组

end program main
! 从 Fortran 读取 C 本地数组的方式 2:
! 地址:2205703485936
! 值:4.0000000000000000        5.0000000000000000        6.0000000000000000
! 在 C 中打印:4.000000 5.000000 6.000000

因为 C 中是数组指针,所以 Fortran 也是数组指针,多一个c_f_pointer绑定指针的操作。

PS. 还是回到我的编程哲学,编程只是一种语言表达性的体现,不断地描述问题,所以代码冗长,但代码背后逻辑却十分简单。

其他链接

  • FAQ之 三种数组传递方式 - Fortran教程 - Fortran Coder 程序员聚集地 (fcode.cn)

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2023-01-09,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 自学气象人 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 01 背景
  • 02 Fortran 数组
  • 03 Fortran 传递数组给 C
  • 04 评论
  • 其他链接
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档