본문 바로가기
C C++/C언어 기초

C언어 - 구조체 배열과 포인터

by Go! Jake 2022. 5. 5.

구조체 배열과 포인터에 대해 알아보겠습니다. 우리는 앞 서 배열에 대해 공부하고, 포인터에 대해 공부하였는 데, 실질적으로 구조체의 배열과 포인터도 동일한 구조를 가지고 있습니다. 익숙하지 않은 게 문제인데, 하나하나 비교하면서 살펴보도록 하겠습니다.

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

우리는 앞 서 특정 type의 여러 변수를 저장하는 배열을 배웠습니다. 구조체에서도 동일하게 구조체 배열이 있습니다.

struct point {
		int xpos;
		int ypos;
	};

우선 위와 같이 point 구조체를 선언합니다. 그리고 아래와 같이 구조체 배열을 선언합니다.

struct point arr[3];

구조체의 배열은 어떤 구조를 가지고 있을까요? 바로 아래와 같습니다.

구조체 배열

각각 배열마다 구조체 변수가 배열과 함께 들어있는 형태입니다. 예를 들어 구조체 배열를 통해 여러 사람의 개인 정보를 저장하는 경우 배열을 선언 하여, 일일이 구조체 변수 선언 없이 한 번에 배열로 관리할 수 있습니다.

 

구조체 배열에 관한 아래 예시를 볼 수 있습니다.

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

struct point {
		int xpos;
		int ypos;
	};

int main()
{
	struct point arr[3];
	int i;
	for (i=0; i<3; i++)
	{
		printf("x, y 좌표:");
		scanf("%d %d", &arr[i].xpos, &arr[i].ypos); 
	}
	
	for(i=0; i<3; i++)
		printf("[%d, %d]\n", arr[i].xpos, arr[i].ypos);
	
	return 0;
}

scanf("%d %d", &arr[i].xpos, &arr[i].ypos);로 구조체 배열의 arr[i].xpos, arr[i].ypos의 주소를 &를 붙여 접근합니다.

구조체 배열의 초기화

구조체 배열도 마찬가지로 선언과 동시에 초기화를 할 수 있습니다.

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

struct point {
		int xpos;
		int ypos;
	};

int main()
{
	struct point arr[2] = {{1,2}, {3,4}};
	int i;
	
	for(i=0; i<2; i++)
		printf("[%d, %d]\n", arr[i].xpos, arr[i].ypos);
	
	return 0;
}

출력:

[1, 2][3, 4]

구조체 변수와 포인터

구조체 포인터는 '구조체 변수'를 가리킨다. 이는 다른 포인터와 동일하다. 아래와 같이 구조체 포인터 변수를 선언하고 가리킬 수 있다.

	struct point pos;
	struct point* ptr;
	ptr = &pos;
	
	pos.xpos = 1;
	pos.ypos = 2;

위 코드를 통해 ptr은 구조체 변수 pos를 가리키고 있다. 그렇다면, 우리는 구조체 변수 pos를 포인터 변수 ptr을 통해 사용할 수 있게 됩니다. 포인터 특성 그대로 사용할 수 있다는 의미입니다.

 

그렇다면, 구조체 변수 pos를 통해 ptr에 접근하는 것은 아래와 같다.

(*ptr).xpos
(*ptr).ypos

pos.xpos와 pos.ypos는 포인터로 접근하면 (*ptr).xpos과 (*ptr).ypos와 같습니다.

 

그런데, C언어는 여기서 접근을 용이하게 하도록 연산자 하나를 지원 해 줍니다. 아래와 같습니다.

ptr->xpos
ptr->ypos

ptr->xpos와 ptr->ypos는 실제로는 (*ptr).xpos와 (*ptr).ypos와 같습니다.

 

읽을 때 굉장히 헷갈릴 수 있습니다. ptr->xpos를 읽을 때,

- ptr이 가리키는 구조체 변수 pos를 먼저 생각합니다. pos겠죠.

- 그 이후 구조체 멤버를 읽습니다.

즉, ptr->xpos는, ptr이 가리키는 구조체 변수 pos의 xpos 멤버 변수를 의미합니다.

 

그럼 아래와 같이 종합적인 예시를 보도록 하겠습니다.

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

struct point {
		int xpos;
		int ypos;
	};

int main()
{
	struct point pos;
	struct point* ptr;
	ptr = &pos;
	
	pos.xpos = 1;
	pos.ypos = 2;
	
	printf("%d %d\n", (*ptr).xpos, (*ptr).ypos);
	printf("%d %d\n", ptr->xpos, ptr->ypos);

	return 0;
}

출력:

1 2

1 2

(*ptr).xpos, (*ptr).ypos의 출력과 ptr->xpos, ptr->ypos의 출력이 동일한 것을 알 수 있습니다.

포인터 변수를 구조체의 멤버로 선언하기
struct point {
		int xpos;
		int ypos;
	};

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

구조체 struct point를 정의합니다. 구조체 변수로 int xpos와 int ypos를 가지고 있습니다.

그리고 struct circle을 정의합니다. 구조체 변수로 double radius와 struct point* center를 가지고 있습니다. 구조체 변수를 가리키는 포인터 center라는 구조체 포인터입니다.

 

그럼 이와 같이 구조체 포인터로 어떤 식으로 구조체를 가리키고 사용할 수 있는 지 보도록 하겠습니다.

#include <stdio.h>

struct point {
		int xpos;
		int ypos;
	};

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

int main()
{
	struct point cen = {2,7};
	double rad = 5.5;
	
	struct circle ring;
	ring.radius = rad;
	ring.center = &cen;
	
	printf("[%d %d]",ring.center->xpos, ring.center->ypos);
	
	return 0;
}

struct circle ring; 을 통해 구조체 circle형 ring 구조체 변수를 선언하였습니다. 위와 같이 ring 구조체 변수 내에는 구조체 포인터로 center라는 변수 멤버가 있습니다.

ring.center = &cen;

아래와 같이 구조체 point형 cen 변수의 주소를 넣어 cen 변수를 가리키게 하였습니다. 이제 마음껏 ring.center를 통해 cen에 접근할 수 있습니다.

printf("[%d %d]",ring.center->xpos, ring.center->ypos);

화살표 연산자를 통해 cen.xpos와 cen.ypos에 ring.center를 통해 접근하였습니다.

 

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

구조체 변수의 주소 값과 첫 번째 멤버의 주소 값은 같습니다.

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

struct point {
		int xpos;
		int ypos;
	};

int main()
{
	struct point cen;
	printf("%p\n%p", &cen, &cen.xpos);
	
	return 0;
}

출력:

000000000062FE10
000000000062FE10

둘의 결과가 같은 것을 알 수 있습니다.

해당 내용은 윤성우 열혈 C프로그래밍을 참조하였습니다.

댓글