팥빵 먹으면서 코딩하는 블로그

05-2. [Linux/Ubuntu] 네트워크 프로그래밍 - 구조체 정렬 및 조건부 컴파일 옵션 본문

study/NETWORK PROGRAMMING

05-2. [Linux/Ubuntu] 네트워크 프로그래밍 - 구조체 정렬 및 조건부 컴파일 옵션

김팥빵_ 2025. 4. 4. 16:59

구조체 정렬

  • 이기종 시스템간 데이터 송수신
  • 구조체 정렬 방식
    • 구조체 내부에서 가장 큰 데이터 타입의 배수로 필드를 정렬
    • 이렇게 되면 메모리 낭비 및 네트워크 대역폭 손실을 초래한다.
    • Zero padding: 0으로 채워진 데이터(낭비된 데이터)
    • 하지만 변수의 위치를 바꿈으로써 padding값을 줄일 수 있다.
      • 구조체 변수들의 가장 위에서부터 내려가며 변수의 maximum byte값을 적용한다.
      • 따라서 변수의 작은 값부터 큰 값까지 정렬하여 변수의 위치를 정하는 게 가장 효율적이다.

Padding 예시
구조체 내 변수의 위치를 바꿔 padding값 줄이는 예시

 

구조체 정렬 방법: #pragma pack

  • pack 지시자
    • 코드 상에서 pack 이후에 위치한 구조체의 정렬 방식을 지정한다.
    • 한마디로, 구조제의 바이트 정렬 값을 지정
  • #pragma pack(push, 정렬 바이트수)
    • 정렬 바이트 수로 구조체를 정렬함(1, 2, 4, 8, 16)
    • 1바이트를 쓰는 게 가장 효율적
    • #pragma pack(push, 1) -> 1 byte 단위로 구조체를 정렬하겠다는 뜻
  • #pragma pack(pop)
    • 정렬 설정을 이전 상태(기본값)로 되돌림

#pragma pack 예시1 (정렬 바이트 수가 구조체마다 다르다면 이 방식을 권장)
#pragma pack 예시2 (정렬 바이트 단위가 같다면 가장 권장)

  • 양방향 통신에서 구조체를 똑같이 위치한다면 size가 같으므로 에러사항 없음
  • 하지만 위치를 달리 했을 때는 통신 결과, 값이 충돌날 수 있음
  • 따라서 pack 지시자를 적극 이용하는 게 필요함.
  • 하지만 가장 중요한 건 코드간 서로 상호간 협의를 해놓는 게 중요!

 

  • 참고) typedef로 바이트 수를 정의하는 법

unsigned char의 바이트 수는 8이므로 "u8"

 

  • 서로 다른 구조체 정렬을 이용한 통신 결과
    • server에는 pack을 사용(1 byte alignment)하고 client에는 pack을 쓰지 않은(no alignment) 경우
      • client는 8바이트를 보냈지만, server는 1바이트 단위로 정렬해놨으므로 5바이트까지 수신한다.
      • 따라서, 둘 다 정렬을 안 하거나, 둘 다 정렬을 하거나 해서 맞춰야 한다.
      • 과거, 영국은 갤런단위, 미국은 그램단위로 연료량을 정했다가 연료가 과포함되어 로켓이 폭파한 사례

통신 결과

 

조건부 컴파일

 

Debug 정보 출력

  • 계측 코드 내장 (instrumentation)
  • C 전처리기 활용
    • #defind DEBUF, #ifdef DEBUG ~ #else ~ #endif 등
    • 선택적 컴파일
    • 소스를 건들이지 않아도 컴파일 시점에 컴파일 옵션만 바꿔주면 됨
  • !조건부 컴파일!
    • -D<매크로이름> -o
    • #ifdef DEBUG
    •     printf(“variable x has value = %d\n”, x);
    • #endif
  •  컴파일 방법
    • $gcc 소스 파일.c –DDEBUG –o 실행파일이름
  •  버그날 때, 저 조건을 넣어서 컴파일하면 디버깅할 수 있다.
  • Debug용 매크로
    • __LINE__ : 현재 행 번호를 표시
    • __FILE__ : 현재 파일 이름을 뜻하는 문자열
    • __DATE__ : 현재 날짜를 뜻하는 문자열 “mmm dd yyyy” 형태
    • __TIME__ : 현재 시간(컴파일)을 뜻하는 문자열 “hh:mm:ss” 형태
  • 빅엔디안, 리틀엔디안 디버깅
  • 등등. 

 

조건부 컴파일 예시

  • #if ~ #endif
  • #if ~ #elif ~ (#else) ~ #endif
  • #if ~ #else ~ #endif
  • #ifdef ~ #endif
  • #if defined (조건1) && !defined (조건2)
    • 여러 조건을 사용할 경우, defined를 사용함.
  • #ifndef ~ #endif
    • if not defined의 의미
  • #error
    • #error 출력 문자열
    • 컴파일 과정에서 #error 부분이 컴파일되면 컴파일을 중지하고 오류 문자 출력
    • 어느쪽에 정의가 되었는지 확인하기 위해 주로 사용한다.
#include <stdio.h>

#ifdef EN
#define	HELLO_MESSAGE	"Hello" 
#elif defined KO
#define	HELLO_MESSAGE	"안녕하세요"
#elif defined FR
#define	HELLO_MESSAGE	"Bonjour"
#endif

int main() {
	printf("%s\n",	HELLO_MESSAGE);
	return 0;
}
#include	<stdio.h>

#ifdef	_32BIT	
typedef unsigned int uint32;	
typedef int int32;	
typedef unsigned long long uint64;	
typedef long long int64;	
#else
typedef unsigned int uint32;	
typedef int int32;	
typedef unsigned long uint64;	
typedef long int64;	
#endif

void main()	
{	
#if	defined	(LINUX)	|| defined	(OSX)	
	printf("Unix	machine\n");	
#elif defined	(WINDOWS)	&& defined(_32BIT)	
	printf("Windows	machine	32bit\n");
	printf(“sizeof(int64)=%ld\n”,	sizeof(int64));
#elif defined	(WINDOWS)	&& !defined(_32BIT)	
	printf("Windows	machine	64bit\n");	
#else
	printf("Unknown	machine\n");	
#endif
}
  • && 또는 || 으로 여러 조건을 같이 사용할 수 있음