You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何用c_f_pointer在Fortran访问C++ Eigen Tensor会触发段错误?

问题描述

我有一个作为C程序组成部分的Fortran模块,该模块包含一个全局三维数组,希望将其指向C中定义的三维Eigen Tensor。

代码文件

test.cpp

#include <Eigen/Dense>
#include <unsupported/Eigen/CXX11/Tensor>

extern "C" {
  void f_link_global_variable(double* data, int* dims, int* strides);
}
int main(){
  Eigen::Tensor<double, 3> tensor(1,2,3);
  tensor.setZero();
  tensor(0,0,0) = 1;
  std::array<int, 3> dims = {tensor.dimension(2), tensor.dimension(1), tensor.dimension(0)};
  int n_dims = 3;
  std::cout << "tensor before passing: " << tensor << std::endl; // prints 1 0 0 0 0 0

  f_link_global_variable(tensor.data(), dims.data(), &n_dims);
  std::cout << "tensor after passing: " << tensor << std::endl;
}

stub_fortran_interface.f90

module fortran_testmodule
  use iso_c_binding
  implicit none
  double precision, pointer :: my_global_ndarray(:,:,:)

contains

subroutine link_global_variable(c_pointer, dims, n_dims) bind(C, name="f_link_global_variable")
  type(c_ptr), intent(in) :: c_pointer
  integer(c_int), intent(in) :: dims(*)
  integer(c_int), intent(in) :: n_dims
  integer, allocatable :: shape_(:)
  integer :: i
  
  allocate(shape_(n_dims))
  do i = 1, n_dims
    shape_(i) = dims(i)
  end do

  call c_f_pointer(c_pointer, my_global_ndarray, shape_)
  ! generates segfault:
  print *, "array in fortran:", my_global_ndarray
  my_global_ndarray(1,1,2) = 3
end subroutine link_global_variable

end module fortran_testmodule

编译链接方式

enable_language(Fortran)
add_library(stub_fortran_interface SHARED stub_fortran_interface.f90)
target_compile_options(stub_fortran_interface PUBLIC "-fvisibility=default")

add_executable(test test.cpp)
target_link_libraries(test_cpp_fortran_interface
                      PUBLIC
                      stub_fortran_interface)

问题

当我尝试在Fortran中打印新绑定的全局数组时触发了段错误,已知C的Eigen库默认采用与Fortran相同的列主序格式,因此问题与数组值的对齐无关,请问为何无法访问C代码中定义的值?


问题分析与解决方案

核心原因:维度顺序不匹配

你在C++中传递的维度顺序完全搞反了。Eigen Tensor和Fortran数组都采用列主序(第一个索引变化最快),但你构造dims数组时,把Eigen的维度顺序反转成了{tensor.dimension(2), tensor.dimension(1), tensor.dimension(0)},导致Fortran拿到的数组形状与实际内存布局完全不匹配,访问时直接越界触发段错误。

举个具体例子:你的Eigen Tensor是(1,2,3),总元素数为6,内存中元素按(0,0,0) → (0,1,0) → (0,0,1) → (0,1,1) → (0,0,2) → (0,1,2)的顺序排列。但你传给Fortran的形状是(3,2,1),Fortran会认为这是一个3×2×1的数组,当你访问my_global_ndarray(1,1,2)时,计算出的内存位置会超出实际的6个元素范围,直接触发段错误。

修正步骤

  1. 调整C++中的维度传递顺序
    将dims数组改为与Eigen Tensor的维度顺序一致:

    std::array<int, 3> dims = {tensor.dimension(0), tensor.dimension(1), tensor.dimension(2)};
    

    这样Fortran拿到的形状是(1,2,3),和实际内存布局完全匹配。

  2. 修正CMake中的目标名错误
    你的CMake代码中,target_link_libraries的第一个参数写错了,应该对应可执行文件test,而不是test_cpp_fortran_interface:

    target_link_libraries(test
                          PUBLIC
                          stub_fortran_interface)
    
  3. 验证索引对应关系
    修正后,Fortran中的my_global_ndarray(1,1,1)对应Eigen的tensor(0,0,0),my_global_ndarray(1,2,1)对应tensor(0,1,0),my_global_ndarray(1,1,2)对应tensor(0,0,1),以此类推,所有访问都不会越界。

修正后的效果

修改完成后,Fortran可以正常打印数组内容,修改my_global_ndarray的值也会同步到C的Eigen Tensor中,C端能正确输出修改后的结果。

内容的提问来源于stack exchange,提问作者Yes

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.20 16:49:56