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 подмассивов).