juuuding

CH 22 구조체와 사용자 정의 자료형 1 본문

C언어/윤성우의 열혈 C

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

jiuuu 2023. 3. 23. 18:01

 1. 구조체란 무엇인가?

 

1. 구조체의 정의

 '구조체'란 하나 이상의 변수(포인터 변수, 배열 포함)를 묶어 새로운 자료형을 정의하는 도구이다. 독립된 정보가 아닌 함께 있어야 의미를 지니는 변수들을 묶기 위해 등장한 것이 구조체이며, 다음과 같이 구조체를 정의함으로써 여러 변수를 하나로 묶을 수 있게 된다.

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

이 때 person이라는 이름이 int나 char과 같은 자료형의 이름이 되는 것이다. 이는 기본 자료형은 아니며 기본 자료형 변수를 묶어서 새로운 자료형을 만든 것이다. 이를 가리켜 '사용자 정의 자료형'이라 한다.

 

 

2. 구조체 변수의 선언과 접근

위에서 person이라는 이름의 구조체를 정의하였다. 이제는 이 자료형을 대상으로 변수를 선언할 수 있다. 그리고 이렇게 선언되는 변수를 가리켜 '구조체 변수'라고 한다. 

struct type_name val_name;    //구조체 변수 선언의 기본 형태

이러한 기본 형태를 가지고 앞서 정의한 person 구조체의 변수 man을 선언하고자 하는 경우에는 다음과 같이 문장을 구성해야한다.

struct person man;

 

구조체 변수의 구성

 

구조체 변수 안에 존재하는 멤버에 접근할 때의 기본 형식은 다음과 같다.

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

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

 man.age=20;

 

자주 쓰이는 방법은 아니지만, 다음과 같이 구조체를 정의함과 동시에 변수를 선언할 수도 있다.

struct person {
		char name[20];
		char phoneNum[20];
		int age;
	} man1, man2, man3;
    

//위의 코드와 동일한 결과
struct person {
		char name[20];
		char phoneNum[20];
		int age;
	}
    
struct person man1, man2, man3;

 

 

3. 구조체 변수의 초기화

 구조체 변수도 선언과 동시에 초기화할 수 있다. 그리고 이 초기화 방법은 배열의 초기화와 동일하다.

struct person {
		char name[20];
		char phoneNum[20];
		int age;
	}
    
struct person man1 = {"김사람", "010-1234-4567", "25"};

위의 코드로 구조체 변수의 초기화 방법을 쉽게 알 수 있다. 여기서 더 주목해야할 점은 구조체의 초기화 과정에서는 문자열 저장 시 strcpy 함수를 호출하지 않아도 된다는 점이다. 구조체 변수 선언 멤버에 문자열을 저장 한다면 strcpy를 호출해야했다. 하지만 초기화 과정에서는 멤버에 저장할 데이터를 단순히 나열하면 된다.

 

 

 

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

 

1. 구조체 배열의 선언과 접근

 다수의 구조체 변수를 선언하기 위해 구조체 배열의 선언을 고려해야한다. 

person형 변수: struct person man  ->  person 배열: struct person man[10]

 

 

2. 구조체 배열의 초기화

 구조체 변수와 마찬가지로 구조체 배열을 선언과 동시에 초기화할 때에는 다음과 같이 배열의 길이만큼 중괄호를 이용해서 초기화를 진행하면 된다.

struct person man[3] = {
	{"사람1", "010-1234-4567, 25"},
	{"사람2", "010-9874-4567, 23"},
	{"사3", "010-1313-4567, 21"}
};

 

[문제 22-2 작성 코드]

*구조체 배열 초기화 참고하기

#define CRT_SECURE_NO_WARNINGS
#include <stdio.h>

struct employee{
	char name[20];		//종업원 이름
	char regNum[20];	//주민번호
	int salary;			//급여 정보	
};

int main(void) {
	
	int i;
	struct employee man[3];

	for (i = 0; i < 3; i++) {
		printf("종업원 이름: ");
		scanf("%s", &man[i].name);
		printf("주민번호: ");
		scanf("%s", &man[i].regNum);
		printf("급여정보: ");
		scanf("%d", &man[i].salary);
	}

	for (i = 0; i < 3; i++) {
		printf("\n종업원 이름: %s\n",man[i].name);
		printf("주민번호: %s\n", man[i].regNum);
		printf("급여정보: %d\n", man[i].salary);
	}
}

 

 

3. 구조체 변수와 포인터

 point형 구조체 포인터 변수도 기본자료형 포인터 변수와 동일하게 선언하고 초기화한다.

struct point pos={11,12};     //xpos, ypos를 각각 11,12로 초기화
struct point *pptr=&pos;     //포인터 변수 ptr이 구조체 변수 pos를 가리킴

 

위의 point형 변수 pptr을 이용해서 다음과 같이 구조체 변수 pos에 접근할 수 있다.

(*pptr).xpos=10;     //pptr이 가리키는 구조체 변수의 멤버 xpos에 10 저장
(*pptr).ypos=20;    //pptr이 가리키는 구조체 변수의 멤버 ypos에 20 저장

 

위의 두 문장은 각각 다음과 같이 쓸 수도 있다.

pptr -> xpos=10;
pptr -> ypos=20;

즉, *연산과 .연산을 하나의 -> 연산으로 대신할 수 있다.

 

다음 코드를 통해 이 내용들을 정리할 수 있다.

struct point {
	int xpos;
	int ypos;
};

int main(void) {
	struct point pos1 = { 1,2 };
	struct point pos2 = { 100,200 };
	struct point* pptr = &pos1;

	(*pptr).xpos += 4;
	(*pptr).ypos += 5;
	printf("[%d, %d]\n", pptr->xpos, pptr->ypos);

	pptr = &pos2;
	pptr->xpos += 1;
	pptr->ypos += 1;
	printf("[%d, %d]\n", (*pptr).xpos, (*pptr).ypos);

	return 0;

}

 

 

4. 포인터 변수를 구조체의 멤버로 선언하기

 포인터 변수도 구조체의 멤버가 될 수 있다. 다음 코드를 보며 관련 내용을 확인할 수 있다.

struct point {
	int xpos;
	int ypos;
};

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

int main(void) {
	struct point cen = { 2,7 };
	double rad = 5.5;

	struct circle ring = { rad, &cen };
	printf("원의 반지름: %g\n", ring.radius);
	printf("원의 중심: [%d, %d]\n", ring.center ->xpos, ring.center -> ypos);

	return 0;
}

 

여기에 추가로 "TYPE형 구조체 변수의 멤버로 TYPE형 포인터 변수를 둘 수 있다"는 것을 알아야 한다. 즉 다음 코드와 같은 선언이 가능하다는 것이다.

struct point {
	int xpos;
	int ypos;
	struct point* ptr;	//구조체 point의 포인터 변수 선언
};

 

이것을 이용하여 다음 코드에서 보이듯이 삼각형을 이루는 세 점의 연결관계도 표현이 가능하다.

struct point {
	int xpos;
	int ypos;
	struct point* ptr;	//구조체 point의 포인터 변수 선언
};

int main(void) {
	struct point pos1 = { 1,1 };
	struct point pos2 = { 2,2 };
	struct point pos3 = { 3,3 };

	pos1.ptr = &pos2;
	pos2.ptr = &pos3;
	pos3.ptr = &pos1;

	printf("점의 연결관계..\n");
	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);
	printf("[%d %d]와 [%d %d]의 연결", pos3.xpos, pos3.ypos, pos3.ptr->xpos, pos3.ptr->ypos);

	
	return 0;
}

 

 

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

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