struct 와 union 비교

rgbi3307의 이미지

struct s_tag {
    char  cval;
    int   ival;
    float fval;
    char  *sval;
} st;
 
union u_tag {
    char  cval;
    int   ival;
    float fval;
    char  *sval;
} un;

위에서 st의 크기는 13바이트, un의 크기는 4바이트가 맞은가요?
틀리다면, 왜그런지...

From:
*알지비 (메일: rgbi3307(at)nate.com)
*학창시절 마이크로마우스를 만들었고, 10년동안 IT관련 개발자로 일하고 있음.
*틈틈히 커널연구회(http://www.kernel.bz/) 내용물들을 만들고 있음.
*((공부해서 남을 주려면 남보다 더많이 연구해야함.))

tj의 이미지

아키텍쳐와 abi에 따라 다릅니다. 근데 아무리 그래도 얼라인먼트 때문에 저 구조체가 13byte가 되긴 힘들구요 (cval이 제일 뒤로 가도 마찮가지).

chlwldud83의 이미지

tj님께서 아키텍쳐에 따라 다르다고 하신 부분은 어떤 CPU(?)에서는 최소 할당을 4byte로 해주기 때문입니다.
char같은 경우 일단 최소단위 4byte로 할당을 해주고 나머지 3byte는 짜른다고 생각하시면 될거 같네요.
ARM아키텍쳐 책에서 본기억이 있는데 embedded system에서는 함수 선언 자체를 4byte(그러니까 대부분 int 형)로 해주는게 성능면에서 좋다고 합니다.
그렇지 않은 경우 위에 말씀 드렸다시피 CPU가 4byte를 할당하지만 다시 3byte를 짜르는 그런 연산을 더 해야 한다는 군요.

근데 저 문제에 대해서는....13byte가 나오지 않을까요?? 물리메모리가 아닌 MMU를 거친거라면 말이죠.

제가 알고 있는게 정확한건지 모르겠네요^^;; 틀린부분있으시면 수정해주세요. 공부시작한지 얼마 안된 초짜라서요~~

요긴 다들 멋진말만 남기더군요.-_-

drinkme의 이미지

x86 계열의 경우, unalignment data에 접근 가능하지만,
ARM 계열의 경우, 이게 안됩니다. exeception이 발생하죠.

어쨋건, alignment를 맞추는 최대 목적은 '성능'이 맞습니다.
ARM에서도 armcc에서 말하는 packed structure로 구성할 수 있겠지만, 결국은 '성능'이 문제겠죠.

packed structure임을 명확히 명시했다면,
13byte가 맞을 겁니다. network protocol이나 filesystem 등을 만드신다면,
뭐 이렇게 하시는 것도 좋겟지만,
일반 용도라면 역시 '성능'이 문제가 되겠습니다.

일단 위에서는 이련 명시적인 내용이 없으므로,
'아키텍쳐'나 abi마다 다르다는게 맞을 것 같습니다.

그리고, MMU는 이 내용이랑 상관이 없는 듯 합니다.
MMU는 byte단위가 아니라, segment나 page단위로 mapping을 합니다.

dragonkun의 이미지

의사 코드로 대충 이렇지 않을까요.

sizeof(s_tag) == sizeof(char) + sizeof(int) + sizeof(float) + sizeof(char*) + padding
sizeof(u_tag) == max(sizeof(char), sizeof(int), sizeof(float), sizeof(char*))

정확한 값이라면 아키텍쳐에 따라 다르다는 게 답이겠구요..
---
Emerging the World!

Emerging the World!

drinkme의 이미지

일단은, structure내의 각 member가 님이 말씀하신 것처럼, packed structure라는 보장이 없고요.
alignment 단위가 얼만큼인지도 제각각이랍니다.

JuEUS-U의 이미지

아키텍쳐도 아키텍쳐지만,
운영체제마다 다르기도 하고, 컴파일러에서 조절할 수도 있습니다.
메모리를 어떻게 쓰는지는 전적으로 프로그램에 달려있으니까요.

inhosens의 이미지

좀더 정확하게 하자면 packed가 아닐 경우에

sizeof(s_tag) == sizeof(char) + padding_char + sizeof(int) + padding_int + sizeof(float) + padding_float + sizeof(char*) + padding_pointer

padding_XXXXX 는 platform에 따라 다르겠죠.

dragonkun의 이미지

음.. 좀 애매한게 padding의 경우 타입에 따라 결정된 게 아니라..
구조체 내 멤버 중 가장 큰 alignment 에 맞추는게 아닌가요?

생각해보니 이것도 아니군요..-_-;;
각 개발환경 별 타입별로 각각의 alignment 값이 있고, 그 alignment 에 위배되지 않게
padding 을 계산해서 채워넣는 거군요.
----
Emerging the World!

Emerging the World!

rgbi3307의 이미지

공부하고 있는터라, 다음과 같이 정리해 봅니다.

아래내용은 리눅스 커널소스의 Documents 폴더에 있는 UNALIGNED_MEMORY_ACCESSES.txt 파일을
제가 의역한 것입니다.

Linux Kernel Document: UNALIGNED MEMORY ACCESSES
===================================================

메모리 배치선(alignment)이 일치하지 않는 접근
=============================================
메모리 배치선에 일치하지 않는(unaligned memory accesses) 접근은
N 바이트의 데이터를 메모리에서 읽고자 할때, 시작주소가 N으로 나누어지지 않을때 발생한다.(addr % N != 0)
예를들면, 주소가 0x10004인 메모리에서 4바이트의 데이터는 읽기 좋지만,
0x10005인 주소에서는 메모리 배치선에 일치하지 않는(unaligned memory accesses) 접근이 발생한다.

Natural alignment
=================
N 바이트의 메모리에 접근하고자 할때, 메모리 기준 주소(base address)는 N으로 나누어 지도록 한다.(addr % N == 0)
코드를 작성할때, target architecture는 메모리 배치선이 맞추어 지도록 요구한다(Natural alignment)
실제적으로, 몇몇의 architectures 만이 Natural alignment을 요구하지만,
우리는 모든 architectures에 대하여 고려해야 하며, 이것은 호환성을 높일 수 있는 가장 손쉬운 방법이다.
만약, Natural alignment 조건에 위배되는 코드를 작성하게 되면, 특정 플랫폼에서 정확하게 동작하지
않을 수 있으며 성능저하의 원인이 된다.

Code that does not cause unaligned access
=========================================
메모리 배치선에 위배되지 않도록 하는 코드

다행히도 컴파일러가 이러한 일을 해준다. 아래의 구조체를 가지고 예를들면,

struct foo {
u16 field1; //2바이트
u32 field2; //4바이트
u8 field3; //1바이트
};

위의 foo 구조체의 인스턴스가 메모리 주소 0x10000에서 시작한다고 가정하면,
구조체 안의 필드들은 아래와 같은 주소에 배치된다고 직관적(기본적)으로 생각할 수 있다.

0x10000: field1
0x10002: field2
0x10006: field3

위에서 field2의 크기는 4바이트 이지만, 주소는 4로 나누어 지지 않으므로 unaligned 접근이 발생한다.
다행히, 컴파일러가 이러한 문제를 알고 있으므로, field1과 field2사이에 2바이트 크기의 padding을 삽입한다.

0x10000: field1
0x10002: (padding)
0x10004: field2
0x10008: field3

이번에는 구조체안의 필드 배열 순서를 아래와 같이 변경해 보자.

struct foo2 {
u32 field2; //4바이트
u16 field1; //2바이트
u8 field3; //1바이트
};

위의 foo2 구조체의 인스턴스가 메모리 주소 0x10000에서 시작한다고 가정하면,
구조체 안의 필드들은 아래와 같은 주소에 배치된다고 직관적(기본적)으로 생각할 수 있다.

0x10000: field2
0x10004: field1
0x10006: field3

위의 경우, 각각의 필드 주소는 메모리 배치선에 위배되지 않으므로, 컴파일러는 padding을 삽입하지 않는다.
단, 구조체 끝에 1바이트를 패딩하는데, 이것은 구조체들의 배치선을 일치시켜 주기 위함이다.

구조체안에 패딩을 삽입하지 않도록 컴파일러에게 지시해 주는 것이 있다.
구조체 형식에 __attribute__((packed))를 사용하면 되는데, 이것은 GCC에서 지정해 주는 것이다.
이 옵션은 structure padding이 꼭 필요하지 않는 경우에만 사용하도록 한다.

From:
*알지비 (메일: rgbi3307(at)nate.com)
*학창시절 마이크로마우스를 만들었고, 10년동안 IT관련 개발자로 일하고 있음.
*틈틈히 커널연구회(http://www.kernel.bz/) 내용물들을 만들고 있음.
*((공부해서 남을 주려면 남보다 더많이 연구해야함.))

From:
*알지비 (메일: rgbi3307(at)nate.com)
*커널연구회(http://www.kernel.bz/) 내용물들을 만들고 있음.
*((공부해서 남을 주려면 남보다 더많이 연구해야함.))