Ошибка в `./a.out ': free (): неверный следующий размер (нормальный) при освобождении динамически размещаемого 2D-массива структуры - PullRequest
1 голос
/ 15 марта 2019

По сути, я создаю 2D массив структур с помощью calloc ().Затем я использую этот массив и освобождаю выделенное пространство, а освобождая его, я получаю «двойное освобождение или повреждение (! Prev)».Код написан на языке C.


Код:

#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include <fcntl.h>
#include <malloc.h>
#include <unistd.h>
#include <string.h>
#include <float.h>

typedef struct  complex_num{
    double real;
    double imag;
}comp;

void transpose(comp *arr, int height, int width);
void change(comp *arr, int width);

void main(){    
        int width = 64, height = 64;

        int len = width;
        comp *fft[len];
        for(int i=0; i<len; i++){
                fft[i] = (comp *)calloc(len, sizeof(comp));
        }


        for (int scan=0;scan<height;scan++){
                for (int pix=0;pix<width;pix++){
                        fft[scan][pix].real = 1.0;
                        fft[scan][pix].imag = 0.0;
                }
                change(&fft[scan][0], len);
        }
        transpose(&fft[0][0], len, len);
        for(int i=0;i<len;i++){
                change(&fft[i][0], len);
        }
        transpose(&fft[0][0], len, len); 
        for(int i=0;i<len;i++){
                free(fft[i]);
        }
}
void transpose(comp *arr, int height, int width){
        comp var;
        for(int i=0;i<height;i++){
                for(int j=0;j<width;j++){
                        if(j>i){
                                var = *((arr + (i*width)) + j);
                                *((arr + i*width) + j) = *((arr + j*width) + i);
                                *((arr + j*width) + i) = var;
                        }
                }
        }
}

void change(comp *arr, int width){
        for(int i=0; i<width; i++){
                (arr + i)->real = 5.0;
                (arr + i)->imag = 6.9;
        }
}

Сообщение об ошибке:

    *** Error in `./a.out': double free or corruption (!prev): 0x0000000002095010 ***
======= Backtrace: =========
/lib/x86_64-linux-gnu/libc.so.6(+0x777e5)[0x7f05108a77e5]
/lib/x86_64-linux-gnu/libc.so.6(+0x8037a)[0x7f05108b037a]
/lib/x86_64-linux-gnu/libc.so.6(cfree+0x4c)[0x7f05108b453c]
./a.out[0x400880]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf0)[0x7f0510850830]
./a.out[0x400509]
======= Memory map: ========
00400000-00401000 r-xp 00000000 00:00 467935                     /path/to/a.out
00600000-00601000 r--p 00000000 00:00 467935                     /path/to/a.out
00601000-00602000 rw-p 00001000 00:00 467935                     /path/to/a.out
02095000-020b6000 rw-p 00000000 00:00 0                          [heap]
7f050c000000-7f050c021000 rw-p 00000000 00:00 0
7f050c021000-7f0510000000 ---p 00000000 00:00 0
7f0510610000-7f0510626000 r-xp 00000000 00:00 360788             /lib/x86_64-linux-gnu/libgcc_s.so.1
7f0510626000-7f0510825000 ---p 00000016 00:00 360788             /lib/x86_64-linux-gnu/libgcc_s.so.1
7f0510825000-7f0510826000 rw-p 00015000 00:00 360788             /lib/x86_64-linux-gnu/libgcc_s.so.1
7f0510830000-7f05109f0000 r-xp 00000000 00:00 360750             /lib/x86_64-linux-gnu/libc-2.23.so
7f05109f0000-7f05109f9000 ---p 001c0000 00:00 360750             /lib/x86_64-linux-gnu/libc-2.23.so
7f05109f9000-7f0510bf0000 ---p 000001c9 00:00 360750             /lib/x86_64-linux-gnu/libc-2.23.so
7f0510bf0000-7f0510bf4000 r--p 001c0000 00:00 360750             /lib/x86_64-linux-gnu/libc-2.23.so
7f0510bf4000-7f0510bf6000 rw-p 001c4000 00:00 360750             /lib/x86_64-linux-gnu/libc-2.23.so
7f0510bf6000-7f0510bfa000 rw-p 00000000 00:00 0
7f0510c00000-7f0510c25000 r-xp 00000000 00:00 360690             /lib/x86_64-linux-gnu/ld-2.23.so
7f0510c25000-7f0510c26000 r-xp 00025000 00:00 360690             /lib/x86_64-linux-gnu/ld-2.23.so
7f0510e25000-7f0510e26000 r--p 00025000 00:00 360690             /lib/x86_64-linux-gnu/ld-2.23.so
7f0510e26000-7f0510e27000 rw-p 00026000 00:00 360690             /lib/x86_64-linux-gnu/ld-2.23.so
7f0510e27000-7f0510e28000 rw-p 00000000 00:00 0
7f0510e30000-7f0510e31000 rw-p 00000000 00:00 0
7f0510e40000-7f0510e41000 rw-p 00000000 00:00 0
7f0510e50000-7f0510e51000 rw-p 00000000 00:00 0
7f0510e60000-7f0510e61000 rw-p 00000000 00:00 0
7fffc47c7000-7fffc4fc7000 rw-p 00000000 00:00 0                  [stack]
7fffc5242000-7fffc5243000 r-xp 00000000 00:00 0                  [vdso]

Прервано (ядро сброшено)

Я компилирую свой код с помощью GCC версии 5.4.0.Я не понимаю сообщение об ошибке и не знаю, как его отладить.Что я должен сделать, чтобы освободить массив указателей?

1 Ответ

3 голосов
/ 16 марта 2019

transpose явно обращается к элементам за пределами массива.

fft в main - это массив указателей.Каждый указатель инициализируется, чтобы указывать на блок динамически выделяемой памяти (через calloc).

В памяти это выглядит так:

                                   0            1                  63
fft:  0 [ 0x0000a000 ] ----> [ real; imag | real; imag | ... | real; imag ]
      1 [ 0x0000ff08 ] ----> [ real; imag | real; imag | ... | real; imag ]
      .
      .
      .
     63 [ 0x0001041c ] ----> [ real; imag | real; imag | ... | real; imag ]

fft имеет 64 элемента, каждыйиз которых указатель.В этом примере fft[0] имеет значение 0x0000a000, по адресу которого есть другой массив из 64 элементов (созданный calloc), в котором хранятся значения типа comp (который является структурой из 2 элементов).

Таким образом, *fft[0] является первой comp структурой (по адресу 0x0000a000), *fft[1] является второй comp структурой (по адресу 0x0000a010), *fft[2] является третьей comp struct (по адресу 0x0000a020) и т. Д. Каждая comp struct занимает 16 байтов, поэтому адреса увеличиваются на 16 (0x10).Последний элемент fft[0], fft[0][63] живет по адресу 0x0000a3f0.

fft[1] - это второй указатель, указывающий на второй (не связанный) блок памяти (также созданный calloc),В этом примере экземпляры comp живут по адресам 0x0000ff08, 0x0000ff18, 0x0000ff28 и т. Д.

То же самое происходит для всех элементов fft, вплоть до fft[63].В этом примере comp экземпляры fft[63][0], fft[63][1], fft[63][2] и т. Д. Живут по адресам 0x0001041c, 0x0001042c, 0x0001043c и т. Д.

Теперь рассмотрим, что transpose делаетОн называется так:

transpose(&fft[0][0], len, len);

Он обращается к памяти следующим образом:

*((arr + (i*width)) + j)

Здесь arr - первый параметр;его значение равно &fft[0][0], что совпадает с fft[0], который в нашем примере равен 0x0000a000.

width равно 64. i и j находятся где-то между 0 и 63в зависимости от того, в какой итерации цикла мы находимся. Предположим, что они на 63.

Тогда i*width - это 63*64 - это 4032, а arr + 4032 - указатель на 4033-й элементмассив.Но ждать!Там нет такого элемента;arr имеет только 64 элемента.

Сейчас мы находимся по адресу памяти 0x00019c00, который находится далеко за пределами fft[0] (напомним, что его элементы идут только по адресу 0x000a3f0).

Но мы еще не закончили: к этому указателю мы добавляем j (63), давая 0x00019ff0.И этот указатель - то, к чему мы обращаемся с *.

Если бы мы написали эту операцию с использованием записи массива, она выглядела бы как

arr[i*width + j]

, что более очевидно показывает, что мы обращаемсяэлемент 4095 из 64-элементного массива.

transpose даже пишет по этому адресу:

*((arr + i*width) + j) = ...

Это изменяет память, которой не владеет ваша программа, тем самым повреждая внутренние структуры данныхиспользуется malloc / calloc / free.Вот что означает сообщение об ошибке double free or corruption: ваш код содержит поврежденные данные, которые были необходимы для free, что может произойти, если дважды освободить один и тот же указатель («double free») или просто записать в память после конца ваших массивов (как в вашем коде).


Чтобы исправить свой код, измените transpose на

void transpose(comp **arr, int height, int width) {
    for (int i = 0 ; i < height; i++) {
        for (int j=0; j < width; j++) {
            if (j > i) {
                comp var = arr[i][j];
                arr[i][j] = arr[j][i];
                arr[j][i] = var;
            }
        }
    }
}

и назовите его как

transpose(fft, len, len);

Таким образом, выне просто передать адрес первого подмассива, а адрес массива промежуточных указателей (который, в свою очередь, позволяет получить доступ к любому из 64 подмассивов).

...