C 프로그래밍 입문/구조체와 유니온 데이터: 두 판 사이의 차이

내용 삭제됨 내용 추가됨
Joshuajh (토론 | 기여)
Joshuajh (토론 | 기여)
258번째 줄:
</source></div>
 
객체지향 언어를 아는 사람이라면 그런 언어들에서 사용되는 함수나 연산자 오버로딩에 비하면 불편하기 짝이 없어 보이겠지만 나름대로 프로그래밍의 자유도를 높이는데 큰 역활을 해준다. 물론 함수의 매개변수를 보이드 타입을 이용해도 같은 효과를 얻을 수 는 있다.
 
먼저 사용된 변수와 C 예약어들을 간단하게 나마 설명해 보겠다. 먼저, '''sizeof() 연산자는 지정된 변수나 타입에 할당되어질 메모리 공간의 크기를 바이트단위로 알아내 주는 '연산자'이다.''' '함수'가 아닌 '연산자'이기 때문에 변수 뿐 아니라 변수의 타입도 사용될 수 있는 것이니 나중에 함수를 배울때 헷갈리지 않도록 주의해야 한다. switch-case구조는 switch 문에 지정된 변수의 값과 동일한 case 문에 해당되는 문장을 수행는 제어구조 이다. 위의 샘플을 예로 들자면 target_type변수의 값이 0이면 case 0: 아래에 있는 문장이 수행되고, 1이면 case 1: 아래에 있는 문장이 수행된다. 자세한 설명은 나중에 제어구조에서 하도록 할 것이다.
 
유니온을 이용하면 다음과 같은 구조체를 사용할 수 도 있다:
union a_register {
uint32_t eax;
uint16_t ax;
struct {
uint8_t al;
unit8_t ah;
};
}
 
인라인 어셈블러를 사용하는 경우에 가끔 사용되는 구조체로 실제 x86 계열의 레지스터와 같은 형태로 메모리가 배열되기 때문에 편리하게 사용될 수 있다. 위의 경우 구조체 변수를 eax로 액세스 하게되면 32비트 전체를, ax로 액세스 하게 되면 eax의 하위 16비트만을, al은 최하위 8비트를 그리고 ah는 차하위 8비트를 액세스 할 수 있도록 해준다.
 
<div style='border: dashed 1px #ff0000; background: #fff0f0; padding: 7px; margin: 10px 0px 10px 0px;'>'''주의''':
위와 같은 방법을 사용하여 만든 프로그램은 -- 사실 인라인 어셈블러를 사용하는 시점부터 소스레벨의 호환성은 완전히 사라지게 된다. 왜냐 하면 첫째, 어셈블러는 당연히 CPU마다 조금씩 다르고, 같은 CPU라 해도 컴파일러 -- 정확하게는 컴파일러 내부에서 호출하게 되는 어셈블러에 따라 표기 방식이 달라질 수 있기 때문이다. 둘째, 앞서 정수형 데이터에서 언급되었던 엔디언 문제 때문에 예상했던 것 과는 다른 결과를 얻을 수 있다. 셋째, 컴파일러는 실행속도의 향상과 사용되는 메모리의 양을 줄이기 위해 여러가지 최적화 작업이 수행되는데, 구조체의 경우 구조체 멤버의 순서를 바꿈으로서 이러한 최적화를 수행하게 된다. 그러므로 위에 적은 방식으로 유니온을 사용하는 경우에는 소스레벨의 호환성을 포기 해야 한다.
</div>
 
<div style='border: dashed 1px #ff0000; background: #fff0f0; padding: 7px; margin: 10px 0px 10px 0px;'>'''참조''':
네트워크 프로그래밍을 하는 경우 패킷의 헤더를 구조체로 정의하여 사용하는 경우가 있는데, 이 경우 최적화에 의해 구조체 멤버의 순서가 바뀌는 경우가 있다. x86 계열의 CPU의 경우 CPU의 특성 때문에 32비트 CPU의 경우 액세스 되는 변수의 메모리 시작 위치가 4의 배수가 되는 위치인 경우에 액세스 속도가 가장 빠르기 때문에 각 멤버 변수들의 액세스 위치를 맞추기 위해 구조체내 순서를 바꾸어 준다. (16비트 CPU의 경우 2의 배수가 되는, 즉 짝수 메모리인 경우 홀수 메모리 인 경우 보다 액세스 속도가 빠르다.)
 
이와같은 문제를 해결하기 위해 일반적으로 제안되는 해결방법은 메모리 공간을 더 차지 하는 변수를 구조체의 앞쪽에 두는 것 -- 즉 long long int, long int, int, short int, char의 순서로 구조체를 구성하는 것 이지만 항상 그것이 가능한 것은 아니기 때문에 구조체를 구성할 때 2의 배수 단위로 메모리가 배치 될 수 있도록 신경을 써 주면 된다. 예를 들어:
struct samp_stru {
uint8_t a;
uint16_t b;
uint32_t c;
uint8_t d;
}
 
위와같이 구조체가 배열된 경우 구조체 멤버 b, c의 경우 모두 홀수 메모리에서 시작되기 때문에 액세스 속도가 느려지기 때문에 최적화를 수행하는 경우 일반적으로 다음과 같이 최적화 된다:
struct samp_stru {
uint32_t c;
uint16_t b;
uint8_t a;
uint8_t d;
}
 
물론, 반드시 위의 순서로 최적화 되리라는 보장은 없다. 컴파일러에 따라, 컴파일되는 타겟 시스템에 따라 전혀 컴파일 되지 않을 수 도 있다. 그러나 네트워크 패킷을 만드는 경우라면 기대한 방법대로 데이터가 전송되도록 보장되도록 구조체를 구성해야 할 필요가 있다. 그렇기 때문에 처음부터 위와 같은 방식으로 구조체를 만들어도 되고, 순서를 반드시 지켜야 한다면 다음의 두 가지 방법중 하나를 선택해서 구조체를 디자인 해도 된다.
struct samp_stru {
uint8_t a;
uint8_t d;
uint16_t b;
uint32_t c;
}
 
struct samp_stru {
uint16_t a;
uint16_t b;
uint32_t c;
uint8_t d;
}
</div>
 
==== 주석 및 참고자료 ====