2017-02-09 78 views
9

Niedawno odkryliśmy, że wykonywaliśmy przypisanie do nieprzydzielonej tablicy w Fortranie. Kompilator GNU gfortran nie złapał błędu, a kod działa zarówno pod OS X jak i Linux. Jednak te same błędy segmentacji kodu na komputerze IBM Power PC.Automatyczne przydzielanie macierzy przy przydzielaniu w Fortranie

Moje pytanie brzmi, czy poniższy kod jest prawidłowy? Wygląda na to, że tablica przypisana do array automatycznie przydziela pamięć na niektórych architekturach, ale nie na inne. Czy istnieją tu konkretne szczegóły dotyczące wdrożenia?

Kod jest mieszany C/kod Fortran:

#include <stdlib.h> 

void assign_array_(double x[], int* n); 
void print_array_(); 

int main() 
{ 
    int n,i; 
    double *x; 

    n = 5; 
    x = (double*) malloc(sizeof(double)*n); 

    for (i = 0; i < n; i++) 
     x[i] = (double) i; 

    assign_array_(x,&n); 
    print_array_(); 

    return 0; 
} 

I kod Fortran:

MODULE test_mod 
    DOUBLE PRECISION, ALLOCATABLE, DIMENSION(:) :: array 
    integer :: nsize 
END MODULE test_mod 

SUBROUTINE assign_array(x,n) 
    USE test_mod 
    IMPLICIT NONE 

    INTEGER :: n 
    DOUBLE PRECISION :: x(n) 

    CALL test_allocated() 
    array = x 
    CALL test_allocated() 
    nsize = n 

END SUBROUTINE assign_array 


SUBROUTINE print_array() 
    USE test_mod, ONLY: nsize, array 
    IMPLICIT NONE 

    INTEGER :: i 

    DO i = 1,nsize 
    WRITE(6,'(F24.16)') array(i) 
    END DO 

END SUBROUTINE print_array 

SUBROUTINE test_allocated() 
    USE test_mod 
    IMPLICIT NONE 

    IF (ALLOCATED(array)) THEN 
    WRITE(6,*) 'Array is allocated' 
    WRITE(6,*) 'size is ', SIZE(array) 
    ELSE 
    WRITE(6,*) 'Array is NOT allocated' 
    END IF 
END SUBROUTINE test_allocated 

wyjściowych (jeśli działa) wynosi:

Array is NOT allocated 
Array is allocated 
size is   5 
    0.0000000000000000 
    1.0000000000000000 
    2.0000000000000000 
    3.0000000000000000 
    4.0000000000000000 

I tutaj jest wyjście na Power PC:

Array is NOT allocated 
Segmentation fault (core dumped) 

Podsumowując: Działa po skompilowaniu pod GNU (GNU Fortran (MacPorts gcc5 5.4.0_0) 5.4.0) gfortran na OSX (arch: x86_64h) i Linux (w wirtualnej maszynie hostowanej na OSX, GNU Fortran (Ubuntu 4.9.4-2ubuntu1 ~ 14.04.1) 4.9.4), ale nie uruchamia się po skompilowaniu na Power PC (arch: ppc64) skompilowanym przy użyciu GNU Fortran (GCC) 4.4.7 20120313 (Red Hat 4.4.7-17) . W naszym oryginalnym kodzie implementacja Power PC uległa dopiero późniejszej segregacji w kodzie, do którego odwołano się do wpisów z przypisaną tablicą, co sprawia, że ​​nasz "bug" (jeśli w rzeczywistości jest to błąd) jest naprawdę trudny do wyśledzenia.

Jakie jest prawidłowe zachowanie powyższego kodu?

+0

Nie sprawdzasz, czy malloc się powiódł. – stark

+0

Właśnie dodałem czek, a malloc odnosi sukcesy we wszystkich przypadkach. – Donna

+1

Jeśli tablica nie została przydzielona, ​​'tablica = x' jest nielegalna. – stark

Odpowiedz

2

Okazuje się, że tylko GNU gfortran 4.6 i wyżej pozwalają na automatyczną realokację tablic LHS w F90. Użycie flagi kompilatora -fno-realloc-lhs wyłącza tę funkcję i wyzwala blok. błędy we wszystkich przypadkach opisanych powyżej (OSX, Linux, PPC) Mystery rozwiązane! Dzięki tajemniczemu plakatowi, którego komentarz zniknął tajemniczo.

Zobacz GCC 4.6 Wiki

+1

Nie, nie pozwalają na to w F90, jeśli kompilujesz ze ścisłym Fortranem 95, nie powinieneś go używać. Jest to funkcja Fortran ** 2003 **, jak wyjaśnił francescalus. Jeśli masz na myśli pliki '.f90', to tak, ale to nie oznacza Fortran 90. –

+0

Tak - chodziło mi o pliki .f90. Używam kompilatora 'gfortran' (gcc5), który, jak zakładam, obejmuje najnowsze standardy F95, 2003 i nowsze. – Donna

+2

Rozróżnienie - jeśli kod nie jest zgodny z regułami określonego standardu, to standard nie określa, co się dzieje, co oznacza, że ​​pod względem "właściwego zachowania" - ** wszystko może się zdarzyć **. "Wszystko" może obejmować awarię programu lub program wykonujący jakąś magiczną realokację, lub program zapisujący fałszywy list rezygnacyjny do szefa, a następnie wymuszający ze współmałżonkiem. (Prawdziwie realistyczne jest inne pytanie.) – IanH

15

Ważność kodu podobnego

integer, allocatable :: array(:) 
array = (/1,2,3/) 
end 

zależy od standardu Fortran stosować je interpretować.

Fortran 2003 wprowadził pojęcie automatycznej alokacji na wewnętrzne przypisanie. Przed wersją Fortran 2003 tablica po lewej stronie takiej instrukcji przypisania musi zostać przydzielona i mieć taki sam kształt jak tablica po prawej stronie.

Od Fortran 2003 tylko potrzeby rangowe pasują. Jeśli występuje niezgodność kształtu, tablica zostanie najpierw zwolniona, a następnie ponownie przydzielona do poprawnego kształtu. Jeśli nie zostanie przydzielony na początku, zostanie przydzielony.

Tak, program nie jest ważna przede Fortran 90, ale jest ważny Fortran 2003.

Różnica w kodzie w świecie rzeczywistym, a następnie, z jakiego języka składnia wsparciu kompilatory.

Dla gfortran przypisanie Fortran 2003 do tablicy alokacyjnej było introduced in 4.6, 2011-01-28.

Jak skomentował również opcję wiersza poleceń -fno-realloc-lhs wyłącza tę automatyczną (ponownego) przyznawania, dzięki czemu kompilator nie Fortranu 2003+ zgodny.


Inne kompilatory mają podobny problem: dodanie wymaganych czek czy przesunięcie jest konieczne jest hitem wydajności, który jest zbędny w Fortran 90 Kod zgodny i może być funkcja nie używana przez wielu nawet w nowoczesnym kodzie . Na przykład dla wersji Intel's compiler w niektórych wersjach obsługujących F2003 domyślnie jest to ignorowane.

zawsze można stłumić (re-) kontrole alokacji/działania tablicy w nowoczesnym kodu przy użyciu sekcję tablicy

array(:) = (/1,2,3/) 

W tym przypadku array (jeśli allocatable) muszą być przydzielone rangi 1 i wielkości 3, aby oświadczenie cesji było ważne. Jest to zgodne z interpretacją przydziału Fortran 90 z całą tablicą array=(/1,2,3/).

Powodem tego jest to, że w sekcji tablicowej tego przypisu strona lewa nie jest alokowana, nawet jeśli sama tablica jest.