Programming/C++

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

OKOK 2017. 7. 26. 15:13

구조체란 무엇인가요?

구조체란 하나 이상의 변수를 그룹 지어서 새로운 자료형을 저의하는 것입니다. 그룹 지어진 변수들은 서로 다른 자료형의 변수라 할지라도 상관이 없으며, 포인터 변수나 배열도 그룹에 속할 수 있습니다. 예제를 통해서 살펴본 내용을 정리하도록 하겠습니다. 이 예제에서는 사용자로부터 두 점의 위치를 입력받아서, 두 점 사이의 거리를 계산하여 그 결과를 출력해 줍니다.


#include <stdio.h>

#include <math.h>


struct point {

int x;

int y;

};


int main(void)

{

struct point p1, p2;

double distance;


fputs("Fisrt point x, y : ", stdout);

scanf_s("%d %d", &p1.x, &p1.y);


fputs("Second point x, y: ", stdout);

scanf_s("%d %d", &p2.x, &p2.y);


distance = sqrt((p1.x - p2.x)*(p1.x - p2.x) + (p1.y - p2.y)*(p1.y - p2.y));


printf("distance is %f. ", distance);


return 0;


point 구조체의 변수 p1, p2를 선언하고 있습니다. 변수 p1의 x, y 멤버를 입력받기 위해서 scanf 함수를 사용하였습니다. sqrt 함수는 math.h 헤더 파일에 선언되어 있는 함수로서 전달되는 인자의 루트값을 반환합니다. 이로부터구조체의 정의 및 변수의 선언, 구조체 변수의 멤버 접근 방법까지 설명하였습니다. 참고로 하나의 함수는 하나의 기능을 지녀야 합니다. 하나의 함수가 둘 이상의 기능을 지니는 경우에는 두 개의 함수로 나눠야 좋습니다.



#include <stdio.h>

#include <string.h>


struct person {

char name[20];

char phone[20];

};


int main(void)

{

struct person p;


strcpy_s(p.name, "Free Lec");

strcpy_s(p.phone, "02-123-123");


printf("name: %s, phone: %s\n", p.name, p.phone);


return 0;


person 구조체 변수 p를 선언하고 있습니다. 변수 p를 이루는 두 개의 배열에 문자열을 복사하고 있습니다. 



구조체 변수의 초기화

기본 자료형의 변수를 선언하고 값을 대입하는 과정을 하나로 묶어서 선언과 동시에 초기화하는 것이 가능하다. 이러한 기능을 적절히 사용하게 되면 프로그램의 라인수도 줄어들게 되며, 더불어 전체적으로 가독성도 좋아지게 된다. 무엇보다도 선언된 변수가 잠시라도 쓰레기 값을 지니지 않도록 안정적인 변수의 형태를 유지할 수 있게 된다. 구조체 변수 또한 선언과 동시에 초기화가 가능하다.  구조체 멤버 변수를 지정하여 초기화하는 문법은 가장 최근의 ANSI 표준에서 반영된 사항이므로 일부 컴파일러는 아직 이를 지원하지 않는다.



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

배열을 선언하고 세 사람 정도의 데이터를 키보드로부터 입력받고 나서, 모니터로 출력을 하게끔 예제를 변경해 보자.


#include <stdio.h>


struct person {

char name[20];

char phone[20];

};


int main(void)

{

struct person pArray[3];

int i;


for (i = 0; i < 3; i++)

{

printf("name, phone : ");

scanf_s("%s %s", pArray[i].name, pArray[i].phone);

}


printf("\n result like this\n");

for (i = 0; i < 3; i++)

{

printf("name: %s, ", pArray[i].name);

printf("phone:%s\n", pArray[i].phone);

}


return 0;


첫 번째 구조체를 입력받은 후 에러발생합니다. 초기화 방식은 2차원 배열의 초기화와 완전히 똑같습니다. 따라서 2차원 배열의 초기화 방식을 조금 응용하면 구조체 배열의 초기화는 문제도 아닙니다. 



구조체와 포인터


구조체 포인터를 선언하여 구조체 변수를 가리키는 경우와 구조체의 멤버로 포인터 변수가 선언된ㄴ 경우가 있습니다. 먼저 전자부터 살펴보도록 합니다. 즉, 구조체 포인터를 선언하여 구조체 변수를 가리킬 수 있음에 대해서 설명하겠습니다. 


#include <stdio.h>


struct person {

char name[20];

char phone[20];

};


int main()

{

struct person man = { "Thomas", "354-1234" };

struct person* pMan;

pMan = &man;


printf("name : %s\n", man.name);

printf("phone : %s\n", man.phone);


printf("name : %s\n", (*pMan).name);

printf("phone : %s\n", (*pMan).phone);


printf("name : %s\n", pMan->name);

printf("phone : %s\n", pMan->phone);


return 0;




구조체 변수 man 을 선언하였습니다. 그리고 person 구조체의 변수를 가리킬 수 있는 포인터 pMan을 선언하였습니다. 구조체 변수 man 의 주소 값을 구조체 포인터 pMan에 대입하였습니다. 포인터 pMan 을 이용해서 구조체 변수 man 의 멤버에 접근하는 방법을 보여줍니다. 괄호를 사용한 이유는 * 연산자가 . 연산자보다 우선 순위가 낮기 때문입니다. -> 연산자(간접 멤버 접근 연산자)를 이용하여 멤버에 접근하고 있는 것을 보여줍니다. (*pMan).name 과 pMan->name 은 같은 의미를 지닙니다. 즉, -> 연산자는 구조체 포인터를 이용하여 멤버에 접근하기 위해서 사용됩니다. 많은 프로그래머들이 편의상 -> 연산자의 사용을 즐깁니다.


포인터 변수와 구조체 멤버

구조체의 멤버로 포인터 변수가 선언되는 경우에 대해서 이야기하겠습니다. 

#include <stdio.h>


struct perInfo {

char addr[30];

char tel[20];

};


struct person {

char name[20];

char pID[20];

struct perInfo* info;

};


int main()

{

struct perInfo info={ "Korea Seoul", "123-1234" };

struct person man = { "Mr.Lee", "123-123213123123" };

man.info = &info;


printf("name: %s\n", man.name);

printf("pID : %s\n", man.pID);

printf("addr : %s\n", man.info->addr);

printf("tel : %s\n", man.info->tel);


return 0;


perInfo 구조체 변수를 가리킬 수 있는 포인터를 멤버로 선언하고 있음을 알 수 있습니다. perInfo 구조체 변수와 person 구조체 변수를 각각 생성하고 있습니다. person 구조체의 멤버(포인터 멤버)가 perInfo 구조체 변수를 가리키도록 구현하고 있습니다.  info가 가리키는 구조체 변수의 멤버에 접근하기 위해서 ->(간접 멤버 접근 연산자)를 사용하고 있습니다.  person 이라는 구조체를 정의하는데, 구조체의 멤버로 person 구조체의 포인터 변수가 올 수 있습니다. 


#include <stdio.h>


struct person {

char name[20];

char pID[20];

struct person* frnd;

};

int main()

{

struct person man1 = { "Mr.Lee", "8123-1231232" };

struct person man2 = { "Mr.Lee's Friend", "81231-23123" };


man1.frnd = &man2;

printf("[Mr. Lee]\n");

printf("name: %s\n", man1.name);

printf("pID : %s\n", man1.pID);


printf("[His Friend]\n");

printf("name : %s\n", man1.frnd->name);

printf("pID : %s\n", man1.frnd->pID);


return 0;


man1 과 man2 는 둘 다 person 구조체 변수입니다. 자료 구조를 공부하다 보면, 자주 사용하게 됩니다. 아하 이제 이것 보고 C, C++ 보고 자료 구조 보고 문제 풀이 바로 시작하면 되겠습니다. 자료 구조를 보게 되면 오픈 소스에 담긴 파일들을 분석 할 수 있게 되나요.


구조체 변수의 주소 값과 구조체 변수의 첫 번째 멤버의 주소 값

은 같다. 이러한 구조체의 특징을 이해하고 있다면, 문제 해결을 위한 본인의 사고는 그만큼 넓어지게 됩니다. 

#include <stdio.h>


struct simple {

int data1;

int data2;

};


int main()

{

struct simple s = { 1,2 };


printf("address1 : %d\n", &s);

printf("address2 : %d\n", &(s.data1));


return 0;


실행 결과를 통해서 확인 할 수 있습니다. 이번 포스팅에서는 구조체를 정의하고 구조체 변수를 선언 및 접근하는 방법에 대해서 공부하였습니다. 구조체와 배열 그리고 구조체와 포인터와의 관계를 이야기 하였습니다. 또한 구조체 변수의 주소 값이 첫 번째 멤버의 주소 값과 일치한다는 특징에 대해서도 이야기 하였습니다.