2015-04-19 3 views
1

Я работаю над программой Fortran 95, которая пытается угадать, о чем вы думаете. Он принимает файл со строками вида id label question yes no:Работа с деревьями и указателями в Фортране

9 
1 . Is_it_a_living_thing? 2 7 
2 . Can_it_walk? 3 6 
3 . Does_it_meow? 4 5 
4 a_cat . 0 0 
5 David_Mitchell . 0 0 
6 a_bacteria . 0 0 
7 . Is_it_electrical? 8 9 
8 a_toaster . 0 0 
9 hair_gel . 0 0 

Причины подчеркивания является реализацией C этой программы, которую я сделал, который с удовольствием работает с форматированием языка Fortran при чтении. Код ниже:

module types 
    implicit none 

    type node 
     character (len = 32) :: label 
     character (len = 128) :: question 
     type(node), pointer :: yes, no 
    end type node 
end module types 

program pangolins 
    use types 
    implicit none 

    !type(node), allocatable :: nodes(:) 
    type(node), pointer :: head, current 

    ! Program 

    head => parseFile() 
    nullify(current) 

    call freeAll(head) 

    stop 
contains 
    function parseFile() result(head) 
     implicit none 

     type(node), pointer :: nodes(:) 
     type(node), pointer :: head 
     integer :: i, n, thisN, thisYes, thisNo 
     character (len = 32) :: thisLabel 
     character (len = 128) :: thisQuestion 

     open(10, file = './file1') 

     read(10, *) n 

     write(*, *) 'Nodes: ', n 

     allocate(nodes(n)) 

     do i = 1, n 
     read(10, *) thisN, thisLabel, thisQuestion, thisYes, thisNo 

     write (*,'(a24,a64,i4,i4)') thisLabel, thisQuestion, thisYes, thisNo 

     nodes(i)%label = thisLabel 
     nodes(i)%question = thisQuestion 

     if (thisYes .eq. 0) then 
      nullify(nodes(i)%yes) 
     else 
      nodes(i)%yes => nodes(thisYes) 
     end if 

     if (thisNo .eq. 0) then 
      nullify(nodes(i)%no) 
     else 
      nodes(i)%no => nodes(thisNo) 
     end if 
     end do 

     head => nodes(1) 
    end function parseFile 

    recursive subroutine freeAll(head) 
     implicit none 

     type(node), pointer :: head 

     if (associated(head%yes)) then 
     call freeAll(head%yes) 
     end if 

     if (associated(head%no)) then 
     call freeAll(head%no) 
     end if 

     write (*,'(a24,a64)') head%label, head%question 
     deallocate(head) 
    end subroutine freeAll 
end program pangolins 

В настоящее время код только инициализирует массив, а затем пытается очистить его снова.

Проблема связана с указателями на массивы и элементы массива. Моя функция parseFile упрощает организацию дерева, сначала анализируя узлы из файла в массив указателей и указывая указатели да и не указатели по индексу в массиве, а затем возвращая первый элемент, который всегда является главой дерева. Это интуитивно понятно в C, откуда я и иду.

Когда я запускаю этот код, второй вызов deallocate() в freeAll() вызывает двойной свободный segfault.

Я подозреваю, что я запутал это в версии C, в которой используется массив указателей, поэтому, хотя я инициализировал начальные узлы в качестве массива, я мог освобождать каждый указатель по одному, используя обход в порядке, как дерево может увеличиться с момента инициализации, а затем, наконец, освободить массив. Вот функция, которую я пытался выполнить с порта C:

... 
node_t* readFile(FILE* inFile) 
{ 
    int noOfNodes; 

    fscanf(inFile, "%d", &noOfNodes); 

    node_t** nodes = (node_t**) malloc(sizeof(node_t*) * noOfNodes); 

    for (int i = 0; i < noOfNodes; i++) 
     nodes[i] = (node_t*) malloc(sizeof(node_t)); 

    char* nodeLabel = (char*) malloc(sizeof(char) * MAX_LABEL_SIZE); 
    char* nodeQuestion = (char*) malloc(sizeof(char) * MAX_QUESTION_SIZE); 
... 

Что мне не хватает? Обратный ход ниже:

$ gfortran -pedantic -Wall -ggdb -fbacktrace -fcheck=all -o pangolins pangolins.f95 
pangolins.f95:65.6: 

     head => nodes(1) 
     1 
Warning: Pointer at (1) in pointer assignment might outlive the pointer target 
$ ./pangolins 
Nodes:   9 
.      Is_it_a_living_thing?            2 7 
.      Can_it_walk?              3 6 
.      Does_it_meow?              4 5 
a_cat     .                 0 0 
David_Mitchell   .                 0 0 
a_bacteria    .                 0 0 
.      Is_it_electrical?             8 9 
a_toaster    .                 0 0 
hair_gel    .                 0 0 
a_cat     .                
*** Error in `./pangolins': double free or corruption (out): 0x0000000000858700 *** 
======= Backtrace: ========= 
/lib64/libc.so.6[0x3055875a4f] 
/lib64/libc.so.6[0x305587cd78] 
./pangolins[0x400d7d] 
./pangolins[0x400c70] 
./pangolins[0x400c70] 
./pangolins[0x400c70] 
./pangolins[0x400ddf] 
./pangolins[0x4018b6] 
/lib64/libc.so.6(__libc_start_main+0xf5)[0x3055821d65] 
./pangolins[0x400b69] 
======= Memory map: ======== 
00400000-00402000 r-xp 00000000 08:03 5636403       /home/adam/utils/fortran/pangolins 
00602000-00603000 r--p 00002000 08:03 5636403       /home/adam/utils/fortran/pangolins 
00603000-00604000 rw-p 00003000 08:03 5636403       /home/adam/utils/fortran/pangolins 
00853000-00874000 rw-p 00000000 00:00 0         [heap] 
3055400000-3055420000 r-xp 00000000 08:03 4459030      /usr/lib64/ld-2.18.so 
305561f000-3055620000 r--p 0001f000 08:03 4459030      /usr/lib64/ld-2.18.so 
3055620000-3055621000 rw-p 00020000 08:03 4459030      /usr/lib64/ld-2.18.so 
3055621000-3055622000 rw-p 00000000 00:00 0 
3055800000-30559b4000 r-xp 00000000 08:03 4499543      /usr/lib64/libc-2.18.so 
30559b4000-3055bb3000 ---p 001b4000 08:03 4499543      /usr/lib64/libc-2.18.so 
3055bb3000-3055bb7000 r--p 001b3000 08:03 4499543      /usr/lib64/libc-2.18.so 
3055bb7000-3055bb9000 rw-p 001b7000 08:03 4499543      /usr/lib64/libc-2.18.so 
3055bb9000-3055bbe000 rw-p 00000000 00:00 0 
3056800000-3056905000 r-xp 00000000 08:03 4460722      /usr/lib64/libm-2.18.so 
3056905000-3056b05000 ---p 00105000 08:03 4460722      /usr/lib64/libm-2.18.so 
3056b05000-3056b06000 r--p 00105000 08:03 4460722      /usr/lib64/libm-2.18.so 
3056b06000-3056b07000 rw-p 00106000 08:03 4460722      /usr/lib64/libm-2.18.so 
3057400000-3057415000 r-xp 00000000 08:03 4499572      /usr/lib64/libgcc_s-4.8.3-20140911.so.1 
3057415000-3057614000 ---p 00015000 08:03 4499572      /usr/lib64/libgcc_s-4.8.3-20140911.so.1 
3057614000-3057615000 r--p 00014000 08:03 4499572      /usr/lib64/libgcc_s-4.8.3-20140911.so.1 
3057615000-3057616000 rw-p 00015000 08:03 4499572      /usr/lib64/libgcc_s-4.8.3-20140911.so.1 
7fcb37dc5000-7fcb37dc9000 rw-p 00000000 00:00 0 
7fcb37dc9000-7fcb37e04000 r-xp 00000000 08:03 4471039     /usr/lib64/libquadmath.so.0.0.0 
7fcb37e04000-7fcb38003000 ---p 0003b000 08:03 4471039     /usr/lib64/libquadmath.so.0.0.0 
7fcb38003000-7fcb38004000 r--p 0003a000 08:03 4471039     /usr/lib64/libquadmath.so.0.0.0 
7fcb38004000-7fcb38005000 rw-p 0003b000 08:03 4471039     /usr/lib64/libquadmath.so.0.0.0 
7fcb38005000-7fcb38006000 rw-p 00000000 00:00 0 
7fcb38006000-7fcb38125000 r-xp 00000000 08:03 4470960     /usr/lib64/libgfortran.so.3.0.0 
7fcb38125000-7fcb38325000 ---p 0011f000 08:03 4470960     /usr/lib64/libgfortran.so.3.0.0 
7fcb38325000-7fcb38326000 r--p 0011f000 08:03 4470960     /usr/lib64/libgfortran.so.3.0.0 
7fcb38326000-7fcb38328000 rw-p 00120000 08:03 4470960     /usr/lib64/libgfortran.so.3.0.0 
7fcb3834b000-7fcb3834d000 rw-p 00000000 00:00 0 
7ffdefd3b000-7ffdefd5c000 rw-p 00000000 00:00 0       [stack] 
7ffdefd8e000-7ffdefd90000 r--p 00000000 00:00 0       [vvar] 
7ffdefd90000-7ffdefd92000 r-xp 00000000 00:00 0       [vdso] 
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0     [vsyscall] 

Program received signal SIGABRT: Process abort signal. 

Backtrace for this error: 
#0 0x7FCB3801F497 
#1 0x7FCB3801FADE 
#2 0x30558358EF 
#3 0x3055835877 
#4 0x3055836F67 
#5 0x3055875A53 
#6 0x305587CD77 
#7 0x400D7C in freeall at pangolins.f95:82 (discriminator 2) 
#8 0x400C6F in freeall at pangolins.f95:74 
#9 0x400C6F in freeall at pangolins.f95:74 
#10 0x400C6F in freeall at pangolins.f95:74 
#11 0x400DDE in pangolins at pangolins.f95:23 
Aborted (core dumped) 

Первый освободитель работает, однако последующий вызывает проблему. Обратите внимание на вторую распечатку a_cat, напечатанную write чуть выше deallocate().

+0

Извинения, я пропустил это по вопросу.Это не причина проблемы. Я добавлю обратную линию. – Doddy

ответ

3

Результат функции head внутри функции parseFile связан с элементом массива. Хотя массив, частью которого является элемент, является назначенная цель указателя, сам элемент не является.

Указатель, являющийся результатом функции, в конечном итоге заканчивается как аргумент функции freeAll. В freeAll вы затем освободите вещь, на которую ссылается этот указатель - это вы освобождаете то, что не является выделенной вещью. Это ошибка программирования.

Если вы хотите освободить цель указателя, связанного с массивом nodes, в функции parseFile, вам необходимо освободить массив. Возможно, результатом функции и аргумента подпрограммы должен быть массив.

(В C, указатель на первый элемент массива может представлять весь массив. Это не тот случай в Fortran, в стороне от таких вещей, как последовательность ассоциаций.)

(Стиль/безопасный вопрос программирования - вам следует использовать подпрограмму для parseFile вместо функции - функции обычно предназначены для использования в выражениях (которые не применяются к этой функции, которая делает IO), и легко ошибиться с функциями, возвращающими результаты указателя, поэтому их следует использовать только по необходимости по другим причинам.)

Смежные вопросы