프로그래밍 언어/C

[자료형(data type)] C 구조체 배열과 구조체 포인터, 구조체 활용

순정법사 2023.08.17
더보기
자료형 중 파생형 구조체 공부 https://ddanzimind.tistory.com/32

 

B. 구조체 배열과 구조체 포인터

1. 구조체와 배열

a. 구조체 배열 선언 및 초기화

배열의 요소에는 제한이 없어서 구조체도 가능함

구조체 배열을 선언하는 방법은 일반 배열 선언방법과 같고, 접근 방법도 동일함

 

🤓 예제 : 구조체 배열의 선언 및 초기화

struct book text_book[3] =	//2차원 배열의 초기화 방법과 똑같은 방법으로 초기화
{
    {"국어", "홍길동", 15000},
    {"영어", "이순신", 18000},
    {"수학1", "강감찬", 10000}
};   

puts("각 교과서의 이름은 다음과 같습니다.");

printf("%s, %s, %s\n", text_book[0].title, text_book[1].title, text_book[2].title);
	//멤버 연산자(.)를 사용하여 각 배열 요소의 멤버에 접근

✨ 실행결과

각 교과서의 이름은 다음과 같습니다.

국어, 영어, 수학1

 

🎨 도식화

 

 

 

2. 구조체와 포인터

b. 구조체 포인터 선언

구조체의 이름은 구조체를 가리키는 주소 

👉 따라서 포인터에 할당할 때에는 반드시 주소 연산자(&)를 사용

 

📘 문법

struct 구조체이름* 구조체포인터이름;

🤓 예제

struct book* ptr_my_book;

 

b. 구조체의 멤버에 접근하기

구조체 포인터를 이용하는 방법에는 두가지가 있음 (동일하게 작동)

 

1) 참조 연산자(*)를 이용하는 방법

 

📘 문법

(*구조체포인터).멤버변수이름

//참조 연산자(*)는 멤버 연산자(.)보다 연산자 우선순위가 낮으므로 반드시 괄호(())를 사용

🤓 예제

(*ptr_my_book).author

//참조 연산자(*)는 멤버 연산자(.)보다 연산자 우선순위가 낮으므로 반드시 괄호(())를 사용

 

2) 화살표 연산자(->)를 이용하는 방법 (더 많이 사용)

 

📘 문법

구조체포인터 -> 멤버변수이름

🤓 예제

ptr_my_book -> author

 

🤓 예제 : 참조 연산자와 화살표 연산자를 동시에 사용

#include <stdio.h>
#include <string.h>

struct book
{
	char title[30];
	char author[30];
	int price;
};

int main(void)
{
	struct book my_book = {"C언어 완전 해부", "홍길동", 35000};
	struct book* ptr_my_book;	// 구조체 포인트 선언 
	
	ptr_my_book = &my_book; 
	
	strcpy((*ptr_my_book).title, "C언어 마스터");	// 참조 연산자(*)를 이용하는 방법
	strcpy(ptr_my_book->author, "이순신");			// 화살표 연산자(->)를 이용하는 방법
	my_book.price = 32000;							// 구조체 변수을 이용한 직접 수정 
	
	printf("책의 제목은 %s이고, 저자는 %s이며, 가격은 %d원입니다.\n", my_book.title, my_book.author, my_book.price);
	return 0;
}

✨ 실행결과

책의 제목은 C언어 마스터이고, 저자는 이순신이며, 가격은 32000원입니다.

 

 

 


C. 구조체의 활용

1. 구조체 활용

a. 함수와 구조체

함수 전달 인수나 반환값으로 구조체를 사용할 수 있음

방식은 기본 타입과 같고, 구조체 포인터나 구조체의 멤버 변수만을 사용할 수도 있음

 

1) 구조체를 인수로 전달하는 방식

 

함수가 원본 구조체의 복사본을 가지고 작업해 안전함

 

🤓 예제 : 구조체의 멤버 변수를 함수의 인수로 전달

#include <stdio.h>

typedef struct
{
	int savings;
	int loan;	
} PROP;

int calcProperty(int, int);

int main(void)
{
	int hong_prop;
	PROP hong =	{10000000, 4000000};
	
	hong_prop = calcProperty(hong.savings, hong.loan);	// 구조체의 멤버 변수를 함수의 인수로 전달함 
	
	printf("홍길동의 재산은 적금 %d원에 대출 %d원을 제외한 총 %d원입니다.\n", hong.savings, hong.loan, hong_prop);
	return 0;
}

int calcProperty(int s, int l)
{
	return (s - l);
}

✨ 실행결과

홍길동의 재산은 적금 10000000원에 대출 4000000원을 제외한 총 6000000원입니다.

 

2) 인수로 구조체의 주소를 직접 전달하는 방식

 

주소 하나만을 전달하므로 처리가 빠름

💥 원본 구조체에 직접 접근하므로 원본 데이터의 보호 측면에서는 매우 위험

 

🤓 예제 : 함수의 인수로 구조체의 주소를 직접 전달

#include <stdio.h>

typedef struct
{
	int savings;
	int loan;	
} PROP;

int calcProperty(PROP*);

int main(void)
{
	int hong_prop;
	PROP hong =	{10000000, 4000000};
	
	hong_prop = calcProperty(&hong);	// 구조체의 주소를 함수의 인수로 전달함. 
	
	printf("홍길동의 재산은 적금 %d원에 대출 %d원을 제외한 총 %d원입니다.\n", hong.savings, hong.loan, hong_prop);
	return 0;
}

int calcProperty(PROP* money)
{
	money->savings = 100;	// 호출된 함수에서 원본 구조체의 데이터를 변경
	return (money->savings - money->loan);
}

✨ 실행결과

홍길동의 재산은 적금 100원에 대출 4000000원을 제외한 총 -3999900원입니다.

 

3) 인수로 구조체를 전달하면서 원본을 보호하는 방식

 

🤓 예제 : const 키워드를 사용

#include <stdio.h>

typedef struct
{
	int savings;
	int loan;	
} PROP;

PROP initProperty(void);
int calcProperty(const PROP*);

int main(void)
{
	PROP prop;
	int hong_prop;
	
	prop = initProperty();
	hong_prop = calcProperty(&prop);
	
	printf("홍길동의 재산은 적금 %d원에 대출 %d원을 제외한 총 %d원입니다.\n", prop.savings, prop.loan, hong_prop);
	return 0;
}

PROP initProperty(void)
{
	PROP hong =	{10000000, 4000000};
	return hong;	// 구조체를 함수의 반환값으로 반환함.
}

int calcProperty(const PROP* money)	// const 키워드를 사용하여 구조체의 데이터를 직접 수정하는 것을 방지함.
{
	return (money->savings - money->loan);
}

✨ 실행결과

홍길동의 재산은 적금 10000000원에 대출 4000000원을 제외한 총 6000000원입니다.

 

b. 중첩된 구조체

구조체를 정의할 때 멤버 변수로 또 다른 구조체를 포함 가능

 

🤓 예제 : 또 다른 구조체를 포함하기

#include <stdio.h>

struct name
{
	char first[30];
	char last[30];
};

struct friends
{
	struct name friend_name;	//friends 구조체는 또 다른 구조체인 name 구조체를 멤버 변수로 포함
	char address[30];
	char job[30];
};

int main(void)
{
	struct friends hong = 
	{
		{ "길동", "홍" },
		"서울시 강남구 역삼동",
		"학생"
	};
	
	printf("%s\n\n", hong.address);
	printf("%s%s에게,\n", hong.friend_name.last, hong.friend_name.first);
	printf("그동안 잘 지냈니? 아직 %s이지?\n", hong.job); 
	puts("공부 잘 하고, 다음에 꼭 한번 보자.\n잘 지내^^");
	return 0;
}

✨ 실행결과

서울시 강남구 역삼동

홍길동에게,
그동안 잘 지냈니? 아직 학생이지?
공부 잘 하고, 다음에 꼭 한번 보자.
잘 지내^^

 

c. 구조체의 크기

구조체의 크기는 멤버 변수들의 크기에 따라 결정

하지만 구조체의 크기가 언제나 멤버 변수들 총합과 일치하는 것은

 

🤓 예제

#include <stdio.h>

typedef struct
{
	char a;
	int b;
	double c;
} TYPESIZE;

int main(void)
{
	puts("구조체 TYPESIZE의 각 멤버의 크기는 다음과 같습니다.");
	printf("%d %d %d\n", sizeof(char), sizeof(int), sizeof(double));
	
	puts("구조체 TYPESIZE의 크기는 다음과 같습니다.");
	printf("%d\n", sizeof(TYPESIZE));
	return 0;
}

✨ 실행결과

구조체 TYPESIZE의 각 멤버의 크기는 다음과 같습니다.
1 4 8

구조체 TYPESIZE의 크기는 다음과 같습니다.
16	//13바이트가 아님

 

d. 바이트 패딩(byte padding)

컴파일러가 메모리의 접근을 쉽게 하기 위해(프로그램 속도 향상) 크기가 가장 큰 멤버 변수를 기준으로 모든 멤버 변수의 메모리 크기를 맞추는것(할당)

 

  • 패딩 바이트(padding byte) : 이때 추가되는 바이트

 

🎨 도식화

 

 

 

📝 이미지 설명

 

  1. char형 멤버 변수를 위해 8바이트가 할당되며, 할당되는 1바이트를 제외한 7바이트가 남음
  2. int형 멤버 변수는 남은 7바이트보다 작으므로, 그대로 7바이트 중 4바이트를 할당
  3. double형 멤버 변수는 8바이트인데 남은 공간은 3바이트뿐이므로 다시 8바이트를 할당
  4. 총 이 구조체의 크기는 총 16바이트가 되며, 그중에서 패딩 바이트(padding byte)는 3바이트가 됨

 

 

 


출처 : http://www.tcpschool.com/c/c_struct_pointer

 

코딩교육 티씨피스쿨

4차산업혁명, 코딩교육, 소프트웨어교육, 코딩기초, SW코딩, 기초코딩부터 자바 파이썬 등

tcpschool.com