Developer

22.(C언어) 구조체(3) 본문

Programming Language/C

22.(C언어) 구조체(3)

DPhater 2020. 8. 1. 22:02

CPU는 비트에 따라서 메모리 접근 단위가 다르다. 32비트의 컴퓨터는 32비트 단위로, 64비트의 컴퓨터는 64비트 단위로 접근한다. 대부분의 C언어 컴파일러는 CPU가 효율적으로 메모리에 접근할 수 있도록 구조체를 정렬해준다. 그럼 구조체 정렬이 무엇인지 알아보자.

구조체 정렬

#include<stdio.h>

typedef struct {  //구조체 정의
	char a;
	int b;
}some;


int main() {
	some c;      //구조체 변수 선언
	printf("멤버변수 a의 크기 :%d Byte\n", sizeof(c.a));
	printf("멤버변수 b의 크기 :%d Byte\n", sizeof(c.b));
	printf("구조체 변수 c의 크기 :%d Byte\n", sizeof(c));
	return 0;
}

코드1 실행 결과

char형 변수 한개와 int형 변수 하나를 가지고 있는 구조체변수를 정의 하였다. 익명구조체를 사용하였고 별칭은 some으로 정의했다. 그런 후 구조체 변수 c를 선언해 멤버 변수들과 구조체 자체의 크기를 살펴보았다.

멤버 변수 a는 char형이니 1Byte라고 맞게 출력되었다. 멤버 변수 b는 int형이니 4Byte라고 맞게 출력되었다.

그런데 구조체 전체의 크기는 a와 b의 크기를 합친 5Byte를 예상 했겠지만 8Byte가 출력되었다. 구조체를 정렬하기 때문이다.

예상한 구조체 c의 메모리 상태

이렇게 구조체는 a와 b각각 1Byte와 4Byte로 이루어져서 구조체가 5Byte가 될것이라 예상했겠지만

실제 메모리에 할당된 구조체 c의 상태

실제 메모리에 할당 될 때에는 위의 그림처럼 char형 변수 a의 뒤에 3개의 빈 공간이 존재해 실제 8Byte라는 값이 출력된 것이다. 이렇게 C에서는 구조체의 멤버중 가장 큰 자료형의 크기를 기준으로 구조체를 정렬한다.

코드1의 경우 int형이 가장 크므로 4의 배수로 멤버들을 정렬한 것이다. 그리고 저렇게 남은 공간을 채우는것을 패딩(padding)이라고 한다.

쉽게 생각하기

쉽게 생각하면 우선 가장 큰 자료형을 찾는다. 위의 경우 int(4Byte)이다.

그런 후 해당 바이트만큼의 공간을 1개 생성한다고 생각하자.

그런 후 구조체 멤버변수들을 순서대로 할당해보자. 가장 먼저 나오는 변수는 char형이다.

공간이 충분하니까 a는 저 위치의 메모리를 차지한다. 다음 변수는 int형이고 4Byte가 필요하다. 그런데 지금 3Byte의 공간밖에 없으므로 padding하고 새로운 4Byte의 공간에 b를 할당한다고 생각하자.

구조체 멤버변수 순서에 따른 차이

#include<stdio.h>

typedef struct {  //구조체 정의
	char a;
	char a1;
	int b;
}some;


int main() {
	some c;      //구조체 변수 선언
	printf("구조체 변수 c의 크기 :%d Byte\n", sizeof(c));
	return 0;
}

코드2 실행 결과

#include<stdio.h>

typedef struct {  //구조체 정의
	char a;
	int b;
	char a1;
}some;


int main() {
	some c;      //구조체 변수 선언
	printf("구조체 변수 c의 크기 :%d Byte\n", sizeof(c));
	return 0;
}

코드3 실행 결과

코드 2와 3을 보면 멤버 변수의 순서만 다르지 내용은 똑같은 것을 알 수 있다.

하지만 코드2의 경우 char형 두개가 먼저 나오고 그 뒤에 int형 변수가 나온다.

반면 코드3의 경우 중간에 int형 변수가 선언되어 있다.

코드2의 구조체

코드3의 구조체

둘 다 int형이 가장 큰 자료형이므로 4Byte의 배수 단위로 메모리에 할당된다.

따라서 코드2의 경우 a와 a1이 연속으로 선언되었으므로 4Byte공간중 2Byte를 차지하고 남은 2Byte가 padding된다.

반면 코드3의 경우 a가 선언되고 그 이후 int형인 b가 선언되어 있으므로

a이후 3Byte의 padding이 필요하다. 그리고 b이후에 a1이 선언되어 있으므로 a1이후 또다시 3Byte의 padding이 필요한것이다.

이렇게 구조체 멤벼변수를 어떤 순서로 정의하느냐에 따라서 구조체가 메모리를 차지하는 양이 달라진다. 그렇기에 메모리를 효율적으로 사용하기 위해서는 구조체를 정의할때 멤버변수의 순서를 신경 써주는 것이 좋다.

원하는 크기로 구조체 정렬하기

구조체 정렬의 기준은 Cpu에 따라 달라지지 않고 컴파일러에서 정한다.

Visual studio의 경우 Default를 8Byte로 설정하고 있다. 그런데 이상할것이다 8Byte 단위가 아니라 위의 코드를 보면 4Byte단위로 했는데....?

정렬기준이 8Byte라는 말은 최대 8의 배수로 정렬한다는 것이다. 즉 8Byte가 넘는 자료형이 사용될 경우 정렬을 보장하지 않는것이다.

이렇게 성능을 위해 컴파일러에서 구조체를 정렬 하지만 네트워크로 전송할 때 불필요한 패딩 때문에 패킷 사이즈가 늘어날 수 있다. 이러한 경우를 위해 사용자가 원하는 크기만큼 정렬 기준을 설정할 수 있다.

#include<stdio.h>
#pragma pack(1)
typedef struct {  //구조체 정의
	char a;
	int b;
}some;
#pragma pack(8)

int main() {
	some c;      //구조체 변수 선언
	printf("구조체 변수 c의 크기 :%d Byte\n", sizeof(c));
	return 0;
}

코드4 실행 결과

#include<stdio.h>
#pragma pack(push,1)
typedef struct {  //구조체 정의
	char a;
	int b;
}some;
#pragma pack(pop)

int main() {
	some c;      //구조체 변수 선언
	printf("구조체 변수 c의 크기 :%d Byte\n", sizeof(c));
	return 0;
}

코드5 실행 결과

코드4 와 5는 구조체 정렬 기준을 1Byte로 설정한 것이다.

#pragma pack(1)
구조체 정의
#pragma pack(8)

위의 코드는 구조체 정렬 크기를 1Byte단위로 하고 구조체를 정의한 뒤

다시 8Byte(기본값)단위로 변경해 준 것이다. 굳이 다시 기본값으로 돌려주는 이유는 처음에 말하였다. 컴파일러는 성능을 위해 정렬 단위를 정해 놓는다. 따라서 이후 작성될 코드에 영향을 주지 않기 위해서 기본값으로 돌려주는 것이다.

#pragma pack(push,1)
구조체 정의
#pragma pack(pop)

위의 코드는 코드5의 일부이다. 코드4와 완전히 똑같은 의미이지만 컴파일러의 구조체 정렬 기준 default값에 따라 달라진다. 코드4의 경우 기준을 1Byte로 바꾼 후 다시 8Byte로 바꾸어준 것이고, 코드5의 경우 기준을 1Byte로 바꾼 후 다시 기본값으로 바꾼것이다. 만약 기본값이 4Byte였다면 4Byte로 바뀔것이다. 컴파일러에 따라 기본값이 다르게 설정되어 있을 수 있으므로 2번째 방법을 추천한다.

'Programming Language > C' 카테고리의 다른 글

24.(C언어) 지역변수와 전역변수  (0) 2020.08.01
23.(C언어) 공용체, 열거형  (0) 2020.08.01
21.(C언어) 구조체(2)  (0) 2020.08.01
20.(C언어) 구조체(1)  (0) 2020.08.01
19.(C언어) 문자열 함수  (0) 2020.08.01
Comments