Ошибка сегментации путем записи в 2D-массив - PullRequest
3 голосов
/ 03 февраля 2012

У меня небольшая проблема с доступом к памяти в моей программе, и я не нахожу ошибку, возможно, кто-то может мне помочь.

Я создал новый тип для хранения значений цвета RGB. Этот тип выглядит так:

typedef struct pixel {
    unsigned char r;
    unsigned char g;
    unsigned char b;
} pixel;

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

pixel **pixelvalue = (pixel **) calloc(imginformation.width, sizeof(pixel));
for (i = 0; i < imginformation.width; i++) {
    pixelvalue[i] = (pixel *) calloc(imginformation.height, sizeof(pixel));
}

После этого я вызываю свою функцию, которая считывает значения цвета и кто должен сохранять их в массиве. Функция получает в качестве параметра массив.

ReadFile(file, imginformation (Stuff like height and so one), pixelvalue (The calloc array));

В этой функции я пытаюсь записать значения с помощью

pixelvalue[i][j].r = (unsigned char)fgetc(in);

Здесь я получаю ошибку доступа к памяти, что я не так сделал?

Редактировать

Привет, прежде всего, извините за отсутствующий язык, вчера я немного устала:).

Для понимания я создал массив пикселей, а элементы указывают на другой массив пикселей? Что-то вроде [Point to another 1D array pixel]?

С пикселем **pixelvalue = calloc(imginformation.width, sizeof(pixel *)); Я создаю imginformation.width номеров указателей от типа pixel, и каждый указатель отображается в пикселях, верно?

Было бы замечательно, если бы вы могли объяснить это немного подробнее, если я ошибаюсь. Я бы очень хотел это понять.

@ carl-norum Что вы имеете в виду:

"вы не должны приводить возвращаемые значения calloc (). Это может сделать скрыть ошибки с #include, которые могут вернуться, чтобы укусить вас вниз дорога ".

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

Спасибо за вашу помощь!

Greetz

Ответы [ 4 ]

3 голосов
/ 03 февраля 2012

Вы на самом деле не создаете 2D-массив, вы создаете массив указателей, которые указывают на массивы пикселей. Это означает, что ваш первый calloc вызов должен выделить достаточно места для указателей, а не для пикселей:

pixel **pixelvalue = calloc(imginformation.width, sizeof(pixel *));

Вы не пометили свой вопрос языком, но предполагая, что это C (на основе вашего typedef, который не нужен в C ++), вам не следует приводить возвращаемые значения calloc(). Это может скрыть ошибки с #include, которые могут вернуться, чтобы укусить вас в будущем.

Edit:

Вы задали пару дополнительных вопросов. Я думаю, что на первый ответ довольно хорошо ответили несколько других ответов, но я постараюсь подвести итог. Как вы делаете распределение, вы сначала собираетесь выделить массив указателей - каждый из этих указателей будет указывать на одну строку вашего массива. Сами строки затем должны быть распределены - туда попадает место для каждого pixel объекта, а указатели на строки хранятся в первом массиве указателей.

Ваш второй вопрос о возвращаемом значении calloc() довольно прост. Если вы приведете возвращаемое значение, вы можете скрыть ошибки неявного объявления от себя. Поскольку тип возвращаемого значения calloc равен void *, если вы делаете что-то вроде:

my_ptr = calloc(1, 2);

Все работает хорошо. Теперь представьте, что вы не включили stdlib.h и, следовательно, не имели прототипа calloc() в своем блоке перевода. Это приведет к тому, что компилятор примет подпись calloc() равной int calloc(int, int), что не соответствует действительности. Та же строка кода, приведенная выше, выдаст предупреждение о том, что для этой функции используется сигнатура по умолчанию. Использование такой типовой трансляции, как у вас в коде, замаскирует это предупреждение, и вы никогда не узнаете, что пропустили эту строку #include.

1 голос
/ 03 февраля 2012

Пожалуйста, смотрите схему для объяснения enter image description here

Итак, вы сначала создаете массив pixel * с помощью calloc.Заполните этот массив, используя calloc с pixel.

0 голосов
/ 03 февраля 2012

Другие авторы правильно определили, что вы должны выделять свой первый блок памяти в единицах pixel* вместо единиц pixel.

Но почему эта проблема вызывает segfault?

На 32-битной машине ваша пиксельная структура, как определено выше, занимает 3 байта, а указатель занимает 32 бита (4 байта).

То есть

  • sizeof(pixel) == 3
  • Но sizeof(pixel*) == 4

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

(На некоторых 64-битных платформах проблема определенно только усугубляется. На некоторых 16-битных платформах вам, возможно, удастся сойти с рук, хотя это все равно будет небрежно)

0 голосов
/ 03 февраля 2012

Код

pixel **pixelvalue = (pixel **) calloc(imginformation.width, sizeof(pixel)); 

pixelvalue - это указатель на указатель на пиксель - ваш typedef.

Вам нужно написать

pixel **pixelvalue = calloc(imginformation.width, sizeof(pixel *)); 

...