Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

EXC_BAD_ACCESS related to struct alignment?

In an iOS application, I have a struct that looks like this

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

In my code, I allocate an array of these with calloc:

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

Now, this works perfectly in the simulator, but on the device, if I try to access buff[width * height - 1] (i.e. the last element in buff), I get an EXC_BAD_ACCESS.

This didn't make sense to me, so after a few hours of debugging, I wondered if it was some kind of alignment issue, so on a whim I tried:

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

making the size of Pixel a power of two.

This fixes the EXC_BAD_ACCESS, but it's awfully weird. Does anyone have any insight into what's going on here? Am I just masking the underlying problem by padding the struct or can alignment really cause a bad access (I thought alignment only had an effect on performance, not correctness).

like image 649
Bill Avatar asked Feb 13 '12 03:02

Bill


2 Answers

EXC_BAD_ACCESS is related to alignment. Unlike x86, ARM requires memory access aligned to certain boundary.

To control alignment, use #pragma push, #pragma pack(n) and #pragma pop around.

See http://tedlogan.com/techblog2.html

like image 199
ZhangChn Avatar answered Oct 11 '22 10:10

ZhangChn


It is the problem of alignment. The minimum structure alignment size is 4 bytes and it will vary according to the datatype declaration in structure (eg. double).Here if you print the size of single block, it will print 3 instead of 4. But if you print the size of your structure, it will print 4 because of the minimum alignment size.

Assume if you also have an 'int' element in the structure, then both the size of single block and structure will be 8. This is because of compiler forced allocate padding byte in between the chars and the int. For example

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]));

The first printf statement will print 4 and the second will print 3. Because the default structure alignment size is 4 bytes and actual allocation is 3 bytes. Now just add one int type in to the same structure.

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]));

Here both printf statements will print 8. Because compiler forced to allocate a byte in between char and the int to just keep the alignment in to multiples of four. Then the structure will look like as given below,

typedef struct {

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

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

int i;
}MyType;

So you have to add a padding byte in your structure to keep the alignment because of the actual allocation is 3 bytes.

The structure allocation size also will vary according to the position of different datatype declarations inside the structure.

like image 42
Augustine P A Avatar answered Oct 11 '22 09:10

Augustine P A