简而言之:我有一个fortran子例程,它接受一个c_ptr
,并使用c_associated
检查它是否是null
,除非我给予指针赋予VALUE
属性,否则检查将触发segfault。
我已经找到了解决这个问题的方法,但是我对导致这种行为的原因很感兴趣。在我看来,这似乎与Fortran隐式地通过引用传递有关。对于我的主库(未显示),这种行为只有在我使用优化编译时才会出现(使用-O0 -g
时不会),但是我无法创建一个最小的示例来重现这种行为。
示例:
在main.cpp中
#include <iostream>
extern "C" {
void test_ptr(double* ptr);
}
int main(){
double* ptr{nullptr};
std::cout << "In C++, pointer is " << ptr << std::endl;
test_ptr(ptr);
std::cout << "Fortran exited without fault." << std::endl;
return 0;
}
在test.f90
中
module test
use, intrinsic :: iso_c_binding
public test_ptr
contains
subroutine test_ptr(ptr) BIND(C)
use, intrinsic :: iso_c_binding
type(c_ptr), target, intent(in) :: ptr
print*, "In Fortran, pointer is", ptr
if (c_associated(ptr)) then
print*, 'Pointer is not null'
else
print*, 'Pointer is null'
endif
end subroutine test_ptr
end module test
如果我编译并将其与
gfortran -c test.f90 test.o -O3
g++ -c main.cpp main.o -O3
g++ -o main main.o test.o -lgfortran
运行./main
得到
In C++, pointer is 0
In Fortran, pointer is 0
Segmentation fault: 11
我发现了两种解决方法:
- 或者 * 通过将
ptr
声明为
type(c_ptr), target, intent(in), value :: ptr
- 或 *,方法是将C++端更改为传递
&ptr
,as。
extern "C" {
void test_ptr(double** ptr);
}
int main(){
double* ptr = nullptr;
std::cout << "In C++, pointer is " << ptr << std::endl;
test_ptr(&ptr);
std::cout << "Fortran exited without fault." << std::endl;
return 0;
}
这两项更改(单独)都将产生预期输出
In C++, pointer is 0
In Fortran, pointer is 0
Pointer is null
Fortran exited without fault.
在我看来,c_associated
似乎在某个时候试图解引用ptr
,而没有首先检查它是否是null
,这是预期的行为吗?有没有其他(首选)方法来检查c_ptr
是否是null
?
1条答案
按热度按时间bkhjykvo1#
value
属性是Fortran 2003的一项功能(一些编译器在此之前支持它作为扩展)。没有它,它假定指针ptr
是 * 按地址传递 *(在C中),尽管由intent(in)
定义了恒定性,这相当于引用C++中的指针。而且您提供了一个空指针作为该地址,而Fortran在任何使用中都会隐式地取消对它的引用,例如ptr = something
相当于C代码*ptr = something
。void test_ptr(double** ptr);
是没有value
属性的函数的正确等效签名。如果您使用的是传统的Fortran 90编译器,可能会有一些与现代标准行为不匹配的怪癖或不同行为,因为
value
不在标准中