구조체와 사용자 정의 자료형 1

유석현(SeokHyun Yu)·2022년 7월 24일

C

목록 보기
21/26
post-thumbnail

1. 구조체란 무엇인가?

'구조체'라는 것은 하나 이상의 변수(포인터 변수와 배열 포함)를 묶어서 새로운 자료형을 정의하는 도구이다.

즉, 구조체를 기반으로 우리는 새로운 자료형을 정의할 수 있다.

물론 이렇게 정의되는 자료형은 기본 자료형과 차이가 있다.

그럼 구조체에 대한 이야기를 시작하겠다.


프로그램 상에서 마우스의 좌표정보를 저장하고 관리해야 한다고 가정해보자.

그렇다면 다음과 같이 두 개의 변수를 선언해야 한다.

int xpos;
int ypos;

그런데 이 둘은 항상 함께하기 마련이다.

이 둘은 서로 독립된 정보를 표현하는 것이 아니라, 마우스의 위치라는 하나의 정보를 표현하기 때문이다.

이 중 하나가 어떠한 이유로 인해서 소멸된다면 나머지 하나도 의미 없는 변수가 되어버린다.

따라서 다음과 같이 생각하지 않을 수 없다.

"이 둘을 묶어버리는 방법은 없을까? 마우스의 좌표를 표현하기 위해서 이 둘을 묶어놓으면 프로그램 상에서의 데이터 표현과 관리가 용이할텐데"

이러한 이유로 등장한 것이 구조체이며, 다음과 같이 구조체를 정의함으로써 위의 두 변수를 하나로 묶을 수 있게 된다.

struct point
{
   int xpos;
   int ypos;
};

생각보다 묶는 법이 간단하지 않은가?

위의 정의는 point라는 이름의 구조체를 정의한 결과이다.

이 때 point라는 이름이 intdouble과 같은 자료형의 이름이 되는 것이다.

물론 이는 기본 자료형은 아니다.

기본 자료형 변수를 묶어서 새로운 자료형을 만든 것이다.

그래서 이를 가리켜 '사용자 정의 자료형(user defined data type)'이라 한다.


앞서 point라는 이름의 구조체를 정의하였다.

즉, 한 개의 자료형을 정의한 것이다

따라서 이제는 이 자료형들을 대상으로 변수를 선언할 수 있다.

그리고 이렇게 선언되는 변수를 가리켜 '구조체 변수'라 한다.

그럼 구조체 변수의 선언방법을 보이겠다.

struct type_name val_name;

위의 문장에서 보이듯이 구조체 변수를 선언할 때에는 맨 앞에 struct 선언을 추가해야 하며, 이어서 구조체의 이름과 구조체 변수의 이름을 선언해야 한다.

즉, 앞서 정의한 point 구조체 변수 pos를 선언하고자 하는 경우에는 다음과 같이 문장을 구성해야 한다.

struct point pos;

이렇게 생성된 구조체 변수 pos에는 int형 변수 xposypos가 존재하며, 이를 멤버라고 한다.

그럼 구조체 변수 안에 존재하는 멤버에 접근할 때는 어떻게 해야 할까?

접근의 기본형식은 다음과 같다.

구조체 변수의 이름.구조체 멤버의 이름

예를 들어서 구조체 변수 pos의 멤버 xpos에 20을 저장하려면 다음과 같이 문장을 구성해야 한다.

pos.xpos=20;

이렇듯 구조체 변수의 멤버에 접근할 때에는 . 연산자를 사용한다.

그러면 지금까지 설명한 내용의 확인을 위해서 앞서 정의한 point 구조체 기반의 예제를 먼저 제시하겠다.

#include <stdio.h>

struct point
{
   int xpos;
   int ypos;
};

int main(void)
{
   struct point pos;
   pos.xpos=10;
   pos.ypos=20;

   printf("%d", pos.xpos); // 10
   printf("%d", pos.ypos); // 20

   return 0;
}

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

struct person
{
	char name[20];
};

int main(void)
{
	struct person man;
	
	strcpy(man.name, "홍길동");
	 
	printf("%s", man.name); // 홍길동
	
	return 0;
}

배열에 문자열을 넣을 때, 선언과 동시에 초기화 할 때를 제외하고는 문자열을 삽입할 수 없다.

배열 이름은 상수 형태의 포인터 변수이기 때문에 man.name="홍길동" 연산이 불가능하기 때문이다.

따라서 배열을 선언할 당시가 아닌 그 이후에 문자열을 넣기 위해서는 strcpy를 이용하면 된다.


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

struct person
{
	char name[20];
	int age;
};

int main(void)
{
	struct person man={"홍길동", 20};
	 
	printf("%s, %d", man.name, man.age); // 홍길동, 20
	
	return 0;
}

구조체 변수의 초기화 과정이다.

여기서 주목할 사실은, 초기화 과정에서는 문자열 저장을 위해서 strcpy 함수를 호출하지 않아도 된다는 점이다.


2. 구조체와 배열 그리고 포인터

#include <stdio.h>

struct point
{
	int xpos;
	int ypos;
};

int main(void)
{
	struct point arr[3];
	
	for(int i=0; i<3; i++)
		scanf("%d %d", &arr[i].xpos, &arr[i].ypos);
	
	for(int i=0; i<3; i++)
		printf("%d, %d", arr[i].xpos, arr[i].ypos);
	
	return 0;
}

다수의 int형 변수를 선언할 때 int형 배열의 선언을 고려하듯이, 다수의 구조체 변수를 선언할 때에는 구조체 배열의 선언을 고려해야 한다.

구조체 배열의 선언방법은 일반적인 배열의 선언방법과 동일하다.


#include <stdio.h>

struct person
{
	char name[20];
	int age;
};

int main(void)
{
	struct person man[3]={
		{"홍", 20},
		{"길", 21},
		{"동", 22}
	};
	
	for(int i=0; i<3; i++)
	{
		printf("%s, %d\n", man[i].name, man[i].age);
	}
	
	return 0;
}

구조체 배열의 선언과 동시에 초기화 방법이다.


#include <stdio.h>

struct point
{
	int xpos;
	int ypos;
};

int main(void)
{
	struct point pos={10,20};
	struct point* ptr=&pos;
	
	printf("%d, %d", (*ptr).xpos, ptr->xpos);
	
	return 0;
}

구조체 변수도 포인터 연산이 가능하다.

포인터 변수를 대상으로 * 연산을 하는 것은 동일하다.

다만, 구조체 포인터 변수의 경우 접근하고자 하는 멤버의 선택을 위해서 . 연산을 추가했을 뿐이다.

그리고 굳이 * 연산자와 . 연산자를 쓰지 않고 다음과 같이 쓸 수도 있다.

ptr->xpos

(*ptr).xpos보다 ptr->xpos가 훨씬 간편하고 직관적이므로, -> 연산자를 사용하는 것이 좋다.


#include <stdio.h>

struct point
{
	int xpos;
	int ypos;
};

struct circle
{
	double radius;
	struct point* center;
};

int main(void)
{
	struct point pos={10,20};
	double rad=5.5;
	
	struct circle ring={rad, &pos};
	
	printf("%d, %d", ring.center->xpos, ring.center->ypos);
	
	return 0;
}

위 코드는 포인터 변수를 구조체의 멤버로 선언한 방법을 보여준다.


#include <stdio.h>

struct point
{
	int xpos;
	int ypos;
	struct point* ptr;
};

int main(void)
{
	struct point pos1={1,1};
	struct point pos2={2,2};
	
	pos1.ptr=&pos2;
	pos2.ptr=&pos1;
	
	printf("[%d, %d]와(과) [%d, %d]연결\n", pos1.xpos, pos1.ypos, pos1.ptr->xpos, pos1.ptr->ypos);
	printf("[%d, %d]와(과) [%d, %d]연결\n", pos2.xpos, pos2.ypos, pos2.ptr->xpos, pos2.ptr->ypos);
	
	return 0;
}

type형 구조체 변수의 멤버로 type형 포인터 변수를 둘 수 있다.(자기 자신을 가리키는 변수)


#include <stdio.h>

struct point
{
	int xpos;
	int ypos;
};

int main(void)
{
	struct point pos={1,1};

	printf("%p, %p", &pos, &pos.xpos); // 003EF7B8, 003EF7B8 출력
	
	return 0;
}

구조체 변수의 주소 값은 구조체 변수의 첫 번째 멤버의 주소 값과 동일하다.

profile
Backend Engineer

0개의 댓글