2

32 ビット システムでは機能するが、64 ビット コンパイラでは警告と懸念が発生する、C 呼び出しを含む少し古い Fortran プログラムを見ています。このプログラムは、動的に割り当てられたメモリへの C ポインタのアドレスを として格納します。このメモリintは、Fortran 側で として共有されINTEGERます。int私の懸念は、64 ビット整数システムでは、C ポインターからのキャストが/として格納できるものよりも大きくなる可能性があることINTEGERです。既存のプログラムを 2 つのファイルでこの例に簡略化しました。

フォートラン: this.f

        program this
        integer,pointer :: iptr
        allocate(iptr)
        call that_allocate(iptr)
        write(*,'(A, Z12)') 'Fortran: iptr address', iptr
        call that_assemble(iptr)
        call that_free(iptr)
        end program this

子:that.c

#include <stdlib.h>
#include <stdio.h>

typedef struct data {
    int a;
    float b;
} data;

void that_allocate_(int* iptr)
{
    data *pData = calloc(1, sizeof(data));
    *iptr = (int)pData;
    printf("C: Allocated address %p (or %d)\n", pData, pData);
    return;
}

void that_assemble_(int* iptr)
{
    data *pData = (data *) *iptr;
    pData->a = 42;
    pData->b = 3.1415926;
    return;
}

void that_free_(int* iptr)
{
    data *pData = (data *) *iptr;
    printf("C: Freeing data %d and %g at %p\n", pData->a, pData->b, pData);
    free(pData);
    return;
}

コンパイル

-m32このプログラムは、32 ビット (ここでは問題ありません) および-m6464ビット用の GNU コンパイラでコンパイルできます。C コードをコンパイルすると、いくつかの警告が発生します。

$ gcc -m64 -c that.c
that.c: In function ‘that_allocate_’:
that.c:12: warning: cast from pointer to integer of different size
that.c: In function ‘that_assemble_’:
that.c:19: warning: cast to pointer from integer of different size
that.c: In function ‘that_free_’:
that.c:27: warning: cast to pointer from integer of different size

残りのコンパイルとリンクは問題なく、プログラムは動作します。

$ gfortran -m64 -o prog this.f that.o
$ ./prog 
C: Allocated address 0x1130b40 (or 18025280)
Fortran: iptr address     1130B40
C: Freeing data 42 and 3.14159 at 0x1130b40

質問

calloc4 バイト整数のデータ制限内に収まるアドレスが返されたように見えcallocますが、より大きな整数のアドレスを返すリスクはありますか? でキャストする(intptr_t)と、コンパイルの警告が表示されなくなりますが、ポインターが切り捨てられたアドレスにキャストしようとすると、上位ビットが切り捨てられ、「セグメンテーション違反」が発生すると思われます。これは正しいです?

私は何をすべきか?Fortran コードに修正を加える必要がありますか?

4

1 に答える 1

6

はい、潜在的なビットの問題があります。コンパイラとプラットフォームの変更に直面してもコードを堅牢にしたい場合は、多くのことを行う必要がありますが、そのほとんどは Fortran 2003 の C 相互運用性機能に依存しています。これらの言語機能は、最近の gfortran でサポートされています。最も活発に維持されている Fortran コンパイラ。

dataあなたの例からは、Fortran コードが構造体へのポインターの値を整数として本当に知る必要があるかどうかは明らかではありません(あなたの例では、この値を出力しますが、デバッグのためだけだと思います)。Fortran コードがポインターを不透明なハンドルと見なすだけでよい場合、ISO_C_BINDING 組み込みモジュールの C_PTR 型は、C ポインターと適切に同等です。何らかの理由で Fortran コードがポインターの値を整数として認識する必要がある場合は、種類が C_INTPTR_T の整数 (これも ISO_C_BINDING 組み込みモジュールから) が適切です。さらに進んで、Fortran コードで実際の構造自体を操作できるようにしたい場合は、BIND(C) 派生型を定義して、さまざまな方法で使用できます。

さらに、C コードは、Fortran コンパイラーが特定の呼び出し規則を使用していることを前提としています。これには、リンカー名を形成するためにプロシージャー名をマングルする方法が含まれます。Fortran 側のインターフェース・ブロックで BIND(C,NAME='xxx') 属性を使用して、Fortran コンパイラーがそのコンパニオン C コンパイラーと互換性のある呼び出し規約を使用することを指定し、プロシージャーの C 名を指定することができます。 .

残りの例を考えると、整数の POINTER 宣言とその後の割り当ては関係ないことに注意してください。

以上 (自由形式では、固定形式が適切だったのは久しぶりです):

PROGRAM this
  USE, INTRINSIC :: ISO_C_BINDING, ONLY: C_PTR, C_INTPTR_T
  IMPLICIT NONE
  ! Interfaces required due to BIND(C).  Also allows the Fortran 
  ! compiler to do better error checking.  Note that the default 
  ! binding label is a lowercase variant of the Fortran name, but
  ! we specify it explicitly here anyway for clarity.
  INTERFACE
    SUBROUTINE that_allocate(the_c_ptr)  &
        BIND(C,NAME='that_allocate')
      USE, INTRINSIC :: ISO_C_BINDING, ONLY: C_PTR
      IMPLICIT NONE
      ! Note passing a pointer by reference.
      TYPE(C_PTR), INTENT(OUT) :: the_c_ptr
    END SUBROUTINE that_allocate

    SUBROUTINE that_assemble(the_c_ptr)  &
        BIND(C,NAME='that_assemble')
      USE, INTRINSIC :: ISO_C_BINDING, ONLY: C_PTR
      IMPLICIT NONE
      ! Note passing a pointer by value.
      TYPE(C_PTR), INTENT(IN), VALUE :: the_c_ptr
    END SUBROUTINE that_assemble

    SUBROUTINE that_free(the_c_ptr)  &
        BIND(C,NAME='that_free')
      USE, INTRINSIC :: ISO_C_BINDING, ONLY: C_PTR
      IMPLICIT NONE
      ! Note passing a pointer by value.
      TYPE(C_PTR), INTENT(IN), VALUE :: the_c_ptr
    END SUBROUTINE that_free
  END INTERFACE

  TYPE(C_PTR) :: the_c_ptr
  CALL that_allocate(the_c_ptr)
  ! Use transfer to convert the C address to an integer value.
  PRINT "('Fortran: ptr address',Z12)",  &
      TRANSFER(the_c_ptr, 0_C_INTPTR_T)
  CALL that_assemble(the_c_ptr)
  CALL that_free(the_c_ptr)
END PROGRAM this

そしてC側で単純化します:

#include <stdlib.h>
#include <stdio.h>

typedef struct data {
  int a;
  float b;
} data;

void that_allocate(data** pData)
{
    *pData = (data*) calloc(1, sizeof(data));
    printf("C: Allocated address %p\n", *pData);
    return;
}

void that_assemble(data* pData)
{
    pData->a = 42;
    pData->b = 3.1415926;
    return;
}

void that_free(data* pData)
{
    printf("C: Freeing data %d and %g at %p\n", pData->a, pData->b, pData);
    free(pData);
    return;
}
于 2013-03-18T10:11:03.997 に答える