Возвращение трехмерного массива из файла изображения в C программировании - PullRequest
0 голосов
/ 16 февраля 2020

В настоящее время я пытаюсь преобразовать файл растрового изображения в 3D-массив. Программа зависает после 3 попыток после запуска = 1. Я не знаю, что случилось.

uint8_t*** loadImage(const int size[3], const char url[]){
uint8_t*** output_array;
output_array = malloc((size[0] * size[1]) * 3 * sizeof(uint8_t));
uint8_t byte, RGB = 0, start = 0; int x = 0, y = 0; uint64_t i;
FILE * fp = fopen(url, "rb");
if(fp == NULL){
    perror("Unable to open file!");
    exit(1);
} else {
    for(i = 0; y < size[1]; i++) {
        fread(&byte, sizeof(uint8_t), 1, fp);
        if(start == 1){
            //it ends here in the 3rd rotation after start
            if(RGB < 2){
                output_array[x][y][RGB] = byte;
                RGB++;
            }
            else if (RGB >= 2 && x <= size[0]) {
                x++;
                output_array[x][y][0] = byte;
                RGB = 1;
            }
            else if(RGB >= 2 && x > size[0]){
                y++;
                x = 0;
                RGB = 1;
                output_array[0][y][0] = byte;      
            }
        }
        else if(byte == 255 && start == 0){
        //trying to find the starting position, which is always white(255)
            start = 1;
        }
}}
fclose(fp);
return output_array;
}

1 Ответ

1 голос
/ 17 февраля 2020

Выделение трехмерного массива

Оператор 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 ++.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...