Alignment And Padding on ARM

ARM에서 struct의 사용시 그 크기를 구함에 있어 X86 과 매우 다름에 주의 해야 한다.
ARM에서는 RISC의 특성상 데이타로의 접근을 빠르게 하기 위해여 4의 배수 단위로 데이타를 정렬시킨다.
일단 예제를 통해 X86과 ARM 에서 어떻게 다른지 확인해 보자.

#include <stdio.h>

struct ac {
char    a;  
};

struct as {
short   a;  
};

struct ai {
int     a;  
};

struct amix {
struct ac   a;  
struct as   b;  
struct ai   c;  
};

int main(int argc, void *argv[])
{

printf("sizeof char  %d\n", sizeof(struct ac));
printf("sizeof short %d\n", sizeof(struct as));
printf("sizeof int   %d\n", sizeof(struct ai));
printf("sizeof mix   %d\n", sizeof(struct amix));

return 0;
}


X86 과 ARM의 실행 결과이다.

X86
sizeof char  1
sizeof short 2
sizeof int    4
sizeof mix   8
ARM
sizeof char  4
sizeof short 4
sizeof int    4
sizeof mix   12

각각 char, short, int 만을 멤버로 갖고 있는 자료구조들의 크기는
X86 에서는 그 크기만큼만 크기를 갖지만 ARM 에서는 정렬되지 않는 경우
데이타로의 접근이 느려지기 때문에 기본 데이타 크기인 4의 크기를 갖게 된다.
하지만 다른 것이 있다 바로 mix 자료형의 경우 X86에서조차 8의 크기를 갖는다.
char + short + int 를 하게 되면 7이 나와야 함에도 배열을 멤버로 갖는 자료형에 문제가 생기는 것이다.
이제 각각의 자료형이 어떤 정렬의 특서을 갖는지 살펴보자.
__alignof__ 함수는 자료형이 어떤 정렬의 특성을 갖는지 알려준다.
각각의 자료형에 아래와 같이 했을때의 결과를 살펴보자
printf("sizeof char  %d\n", __alignof__(struct ac));
printf("sizeof short %d\n", __alignof__(struct as));
printf("sizeof int   %d\n", __alignof__(struct ai));
printf("sizeof mix   %d\n", __alignof__(struct amix));
X86
sizeof char  1
sizeof short 2
sizeof int    4
sizeof mix   4
ARM
sizeof char  4
sizeof short 4
sizeof int    4
sizeof mix   4
보는 바와 같이 자료형의 정렬의 특성이 다름을 알려준다.
mix 자료형의 정렬은 보는 바와 같이 4의 크기이기 때문에 크기가  X86 에서도 8이 된다.
자 그럼 mix 자료형을 아래와 같이 바꾸면 크기가 얼마가 될까?
struct amix {
struct ac   a;
struct ai   c;
struct as   b;
};
인자들의 내용은 동일하다
하지만 char + short + int 형으로 배치되어 있던 것이
char + int + short 형으로 배치가 바뀌었다.
크기가 어떻게 바뀌었을까?
sizeof mix   12
X86 과 ARM 모두 동일하게 12의 크기를 갖게 된다.
그 이유는 내부 자료형에서 4의 정렬 틀성을 가지기 때문에 char 형 뒤에 int 가 올경우 4의 정렬을 맞추기 위해서 
각각의 인자의 크기가 4로 된 것이다.
정렬의 크기와 순서에 따라 크기가 달라지는 마법은 매우 혼란스럽다.
마지막으로 mix 된 데이타의 위치가 어디인지 한번 확인해 보자.
#define offsetof(s,m)   (size_t)&(((s *)0)->m)
를 선언하고 
struct amix {
struct ac   a;
struct as   b;
struct ai   c;
};
자료 구조의 위치를 정확히 알아보자
printf("offsetof mix a %d\n", offsetof(struct amix, a));
printf("offsetof mix b %d\n", offsetof(struct amix, b));
printf("offsetof mix c %d\n", offsetof(struct amix, c));
의 실행 결과는 
offsetof mix a 0
offsetof mix b 2
offsetof mix c 4
와 같이 나온다.
X86
offsetof mix a 0
offsetof mix b 2
offsetof mix c 4
ARM
offsetof mix a 0
offsetof mix b 4
offsetof mix c 8
X86 에서는 좀더 스마트하게 앞의 두개의 인자가 4를 초과하지 않지만 그 안에서도 2의 배수로 정렬하고
4의 배수로 정렬해 주었다.


이번엔 
struct amix {
struct ac   a;
struct ai   c;
struct as   b;
};
와 같이 int 와 short 의 순서를 바꾸었다.
X86
offsetof mix a 0
offsetof mix c 4
offsetof mix b 8
ARM
offsetof mix a 0
offsetof mix b 4
offsetof mix c 8
이번엔 결과가 동일하다 왜냐하면 X86에서도 첫번째 인자와 두번째 인자의 합이 4를 초과하므로
4의 정렬로 맞추어진 것이다.
본바와 같이 정렬의 크기와 순서에 따라 또한 아키텍쳐에 따라 매우 다른 경우를 보여준다.
이제 마지막으로 이 모든것을 똑같이 맞추는 방법을 시험해 보자.
아래의 소스의 실행 결과를 눈여겨 보자.
#include <stdio.h>

struct ac {
char    a;
}__attribute__((packed));

struct as {
short   a;
}__attribute__((packed));

struct ai {
int     a;
}__attribute__((packed));

struct amix {
struct ac   a;
struct as   b;
struct ai   c;
}__attribute__((packed));

#define offsetof(s,m)   (size_t)&(((s *)0)->m)

int main(int argc, void *argv[])
{

printf("sizeof char  %d\n", sizeof(struct ac));
printf("sizeof short %d\n", sizeof(struct as));
printf("sizeof int   %d\n", sizeof(struct ai));
printf("sizeof mix   %d\n", sizeof(struct amix));

printf("sizeof char  %d\n", __alignof__(struct ac));
printf("sizeof short %d\n", __alignof__(struct as));
printf("sizeof int   %d\n", __alignof__(struct ai));
printf("sizeof mix   %d\n", __alignof__(struct amix));

printf("offsetof mix a %d\n", offsetof(struct amix, a));
printf("offsetof mix b %d\n", offsetof(struct amix, b));
printf("offsetof mix c %d\n", offsetof(struct amix, c));

return 0;
}
X86
sizeof char  1
sizeof short 2
sizeof int   4
sizeof mix   7
sizeof char  1
sizeof short 1
sizeof int   1
sizeof mix   1
offsetof mix a 0
offsetof mix b 1
offsetof mix c 3
ARM
sizeof char  1
sizeof short 2
sizeof int   4
sizeof mix   7
sizeof char  1
sizeof short 1
sizeof int   1
sizeof mix   1
offsetof mix a 0
offsetof mix b 1
offsetof mix c 3

보는 것처럼 모든 결과가 동일하게 되었다.
주의 할 것은  mix 처럼 자료구조형을 갖고 있는 자료형의 경우 내부에서 사용된 
모든 struct에 __attribute__((packed)) 를 붙여주어야 한다는 것이다.

그렇지 않을 경우 또 예기치 못하는 결과가 올수도 있다.