EXC_BAD_ACCESS относится к выравниванию структуры? - PullRequest
5 голосов
/ 13 февраля 2012

В приложении для iOS у меня есть структура, которая выглядит следующим образом

typedef struct _Pixel {
  signed char r;
  signed char g;
  signed char b;
} Pixel;

В моем коде я выделяю их массив с помощью calloc:

Pixel* buff = calloc(width * height, sizeof(Pixel));

Теперь, это прекрасно работает в симуляторе, но на устройстве, если я пытаюсь получить доступ к buff[width * height - 1] (т.е. последний элемент в buff), я получаю EXC_BAD_ACCESS.

Это не имело для меня смысла, поэтому после нескольких часов отладки я подумал, не является ли это какой-то проблемой выравнивания, поэтому по какой-то причине я попытался:

typedef struct _Pixel {
  signed char r;
  signed char g;
  signed char b;
  signed char padding;
} Pixel;

делает размер пикселя степенью двойки.

Это исправляет EXC_BAD_ACCESS, но это ужасно странно. У кого-нибудь есть понимание того, что здесь происходит? Могу ли я просто замаскировать основную проблему, заполнив структуру, или выравнивание действительно может привести к неправильному доступу (я думал, что выравнивание влияло только на производительность, а не на корректность).

Ответы [ 2 ]

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

EXC_BAD_ACCESS относится к выравниванию. В отличие от x86, ARM требует доступа к памяти, выровненного по определенной границе.

Для управления выравниванием используйте #pragma push, #pragma pack(n) и #pragma pop вокруг.

См. http://tedlogan.com/techblog2.html

2 голосов
/ 17 марта 2013

Это проблема выравнивания. Минимальный размер выравнивания структуры составляет 4 байта, и он будет варьироваться в зависимости от объявления типа данных в структуре (например, double). Вот если вы напечатаете размер одного блока, он напечатает 3 вместо 4. Но если вы напечатаете размер ваша структура, он напечатает 4 из-за минимального размера выравнивания.

Предположим, что если у вас также есть элемент int в структуре, то и размер отдельного блока, и структура будут равны 8. Это из-за того, что компилятор принудительно распределил байт заполнения между символами и типом int. Например

typedef struct {

signed char r;
signed char g;
signed char b;
}MyType;

MyType *type = (MyType *)calloc(20, sizeof(MyType));
printf("size: %ld", sizeof(MyType));
printf("size: %ld", sizeof(type[0]));

Первый оператор printf напечатает 4, а второй напечатает 3. Поскольку размер выравнивания структуры по умолчанию составляет 4 байта, а фактическое распределение составляет 3 байта. Теперь просто добавьте один тип int к той же структуре.

typedef struct {

signed char r;
signed char g;
signed char b;

int i;           // New int element added here
}MyType;

MyType *type = (MyType *)calloc(20, sizeof(MyType));
printf("size: %ld", sizeof(MyType));
printf("size: %ld", sizeof(type[0]));

Здесь оба оператора printf будут печатать 8. Потому что компилятор вынужден размещать байт между char и int, чтобы просто сохранить выравнивание кратным четырем. Тогда структура будет выглядеть так, как указано ниже,

typedef struct {

signed char r;
signed char g;
signed char b;

char padding;    // Padding byte allocated to keep alignment.

int i;
}MyType;

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

Размер размещения структуры также будет варьироваться в зависимости от положения различных объявлений типов данных внутри структуры.

...