2013-03-18 2 views
1

32 비트 시스템에서 작동하는 C 호출로 약간 오래된 Fortran 프로그램을 살펴 보았지만 64 비트 컴파일러에서 경고 및 문제가 발생합니다. 이 프로그램은 동적으로 할당 된 메모리에 대한 C 포인터의 주소를 int으로 저장하며 포트란 측에서는 INTEGER으로 공유됩니다. 내 관심은 64 비트 정수 시스템에서 C 포인터의 캐스트가 int/INTEGER으로 저장할 수있는 것보다 클 수 있다는 것입니다. 두 파일을 사용하여 기존 프로그램을이 예제로 단순화했습니다.Fortran/C 프로그램을 32 비트에서 다중 아키텍처로 업그레이드

포트란 : 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 

C : 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; 
} 

컴파일

프로그램은 32 비트 -m32와 GNU 컴파일러로 컴파일 될 수

(아무 문제 여기) 64 비트의 경우 -m64 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 

질문

내가 볼 수 있지만 calloc 할 수있는 주소를 반환 4 바이트 정수의 데이터 한계에 맞으면, 더 큰 정수로 주소를 반환하는 calloc의 위험이 있습니까? (intptr_t)으로 캐스팅하면 컴파일 경고가 사라지지만 포인터가 잘린 주소로 캐스팅하려고하면 "세그멘테이션 오류"와 상위 비트는 자릅니다. 이 올바른지?

어떻게해야합니까? 수정 프로그램이 포트란 코드를 사용해야합니까?

답변

5

예, 잠재적 인 비트 문제가 있습니다. 컴파일러와 플랫폼이 바뀌어도 코드가 강력 해지기를 원하면 Fortran 2003의 C 상호 운용성 기능에 의존하는 많은 작업을해야합니다. 이러한 언어 기능은 최근 gfortran 가장 적극적으로 Fortran 컴파일러를 유지 관리합니다.

Fortran 코드가 실제로 data 구조체에 대한 포인터의 값을 정수로 알아야하는지 여부는 명확하지 않습니다 (이 값을 인쇄하는 예에서는 디버깅 용으로 생각됩니다). Fortran 코드가 포인터를 불투명 한 핸들로 간주하면 ISO_C_BINDING 내장 모듈의 C_PTR 유형이 C 포인터와 동등합니다. 어떤 이유로 Fortran 코드가 포인터의 값을 정수로 알아야하는 경우 ISO_C_BINDING 내장 모듈의 C_INTPTR_T 종류의 정수가 적합합니다. 더 나아가 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; 
} 
+0

덕분에, 그 좋은 예입니다! ISO_C_BINDING이 앞으로 나아갈 길입니다. –

관련 문제