Выделение трехмерного массива
Оператор output_array = malloc((size[0] * size[1]) * 3 * sizeof(uint8_t));
выделяет пространство для size[0] * size[1] * 3
объектов типа uint8_t
. Но output_array
не указывает на uint8_t
. Его тип uint8_t ***
, что означает, что он указывает на uint8_t **
. Следовательно, для объектов типа uint8_t **
.
может потребоваться место. Кто-то, возможно, сказал вам, что массивы - это указатели, и поэтому uint8_t ***
- это трехмерный массив. Это неверно. uint8_t ***
- указатель на указатель на указатель на uint8_t
(или, если они указывают на первый из нескольких объектов, то указатель на указатели на указатели на несколько uint8_t
). Из вашего кода похоже, что вам нужен многомерный массив, а не куча указателей.
«Самый простой» способ сделать malloc
правильно - это объявить указатель на то, что вы хотите. Если вам нужен массив size[0]
массивов size[1]
массивов из 3 массивов uint8_t
, тогда он будет объявлен с помощью uint8_t SomeName [size[0]] [size[1]] [3];
1 , а указатель на него будет объявлен с помощью uint8_t (*Pointer) [size[0]] [size[1]] [3];
. Затем вы можете выделить пространство с помощью 2 :
uint8_t (*Pointer) [size[0]] [size[1]] [3] = malloc(sizeof *Pointer);
Использование вещи, на которую указывает указатель, требует использования оператора *
, поэтому вы будете обращаться к элементам массива с помощью (*Pointer)[i][j][k]
. Однако вместо этого вы можете сделать указатель на первый элемент массива с помощью:
uint8_t (*Pointer) [size[0]] [size[1]] [3] = malloc(sizeof *Pointer);
uint8_t (*output_array) [size[1]] [3] = *Pointer;
Это сначала устанавливает Pointer
для указания на один трехмерный массив. Затем *Pointer
обозначает первый массив, но он автоматически преобразуется в указатель на первый элемент этого массива. (Этот первый элемент является двумерным массивом.) Этот указатель используется для инициализации output_array
, поэтому output_array
указывает на первый из нескольких двумерных массивов. Теперь вы можете использовать output_array
так же, как если бы это был массив: вы можете ссылаться на элемент как output_array[i][j][k]
и возвращать этот указатель с помощью return output_array
.
Однако большинство людей не используют две строки как я показал им выше. Я использовал это для иллюстрации, чтобы показать один объект, который мы хотим выделить (трехмерный массив) и как sizeof
вычисляет его размер для вас. Большинство людей объединяют эти строки в одну строку, которая выделяет несколько содержащихся массивов, с одинаковым эффектом:
uint8_t (*output_array) [size[1]] [3] = malloc(size[0] * sizeof *output_array);
Возвращение массива
Массив, определенный с помощью uint8_t [size[0]] [size[1]] [3]
, равен массив переменной длины , потому что его размеры не являются целочисленными константами, как определено стандартом C. Такой тип называется переменно измененным типом , как и указатель на него. Функции не могут возвращать переменно измененные типы, поэтому эта функция не может возвращать указатель на массив, который она создала, или даже указатель на свой первый элемент, поскольку подмассив также изменяемым образом.
Обходной путь - преобразовать указатель на void *
и верните это. Сначала измените объявление массива так, чтобы оно возвращало void *
:
void *loadImage(const int size[3], const char url[])
Затем в вызывающей программе поместите возвращаемое значение в указатель нужного типа:
uint8_t (*output_array) [size[1]] [3] = loadImage(size, url);
Другой вариант чтобы вызывающий передавал адрес указателя, который он хотел бы установить, чтобы он указывал на новый массив (по его первому элементу). Функция изменится на:
void loadImage(const int size[3], const char url[],
uint8_t (**returned_array) [size[1]] [3])
{
…
*returned_array = output_array;
}
, а вызывающая сторона изменится на:
uint8_t (*output_array) [size[1]] [3];
loadImage(size, url, &output_array);
Сноски
1 Пробелы между скобками только для удобства чтения, так как здесь так много скобок. Вы можете использовать этот интервал в исходном коде или нет по желанию.
2 Это требует, чтобы ваша реализация C поддерживала массивы переменной длины. В настоящее время они являются необязательными в стандарте C, но поддерживаются общими компиляторами C, хотя они могут отсутствовать при компиляции для C ++.