본문 바로가기

programming language/C 언어

열혈 ( C ) chapter 22 - 구조체와 배열 그리고 포인터

 

<예시>

 

#include<stdio.h>

struct point
{
	int xpos;
	int ypos;
};

int main(void)
{
	struct point arr[3];
	int i;

	for (i = 0; i < 3; i++)
	{
		printf("점의 좌표 입력: ");
		scanf("%d %d", &arr[i].xpos, &arr[i].ypos);
	}
	for (i = 0; i < 3; i++)
		printf("[%d,%d] ", arr[i].xpos, arr[i].ypos);
	return 0;
}

 

point라는 이름의 구조체를 정의하였고, 이때 point라는 이름이 int나 double과 같은 자료형의 이름이 된다. 즉 사용자 정의 자료형이라 부를 수 있다.

 

int xpos;  int ypos; 를 구조체의 멤버라고 부른다.

 

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

 

struct point arr[3]; point 형 구조체 배열 선언. 

첫 번째 요소의 값은 arr [0]. xpos와 arr [0]. ypos이다.

 

<구조체 배열의 초기화 예시>

 

#include<stdio.h>
struct person
{
	char name[20];
	char phoneNum[20];
	int age;
};

int main(void)
{
	struct person arr[3] = {
		{"이승기","010-8989-8899",21},
		{"오메가","010-1111-1111",20},
		{"비타민","010-2222-2222",33}
	};
	int i;
	for (i = 0; i < 3; i++)
		printf("%s %s %d\n", arr[i].name, arr[i].phoneNum, arr[i].age);
	return 0;
}

 

<구조체 배열의 초기화는 다음과 같이 진행해준다>

 

struct person arr[3] = {
		{"이승기","010-8989-8899",21},
		{"오메가","010-1111-1111",20},
		{"비타민","010-2222-2222",33}
	};

 

문제 22-2

 

#include<stdio.h>
struct employee
{
	char name[10];
	char num[20];
	int salary;
};

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

	for (int i = 0; i < 3; i++)
	{
		scanf("%s %s %d", arr[i].name, arr[i].num, &arr[i].salary);
	}

	for (int j = 0; j < 3; j++)
	{
		printf("%s %s %d\n", arr[j].name, arr[j].num, arr[j].salary);
	}
	return 0;
}

 

문자열을 scanf 함수를 통해 입력받을 때는 앞에 &(주소 연산자)를 붙이지 않지만, 정수를 입력받을때는 &를 앞에 붙여줘야 한다.

 

scanf("%s %s %d", arr[i].name, arr[i].num, &arr[i].salary);

 

<Struct pointer 예제>

 

#include<stdio.h>

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 += 2;
	printf("[%d, %d]\n", (*pptr).xpos, (*pptr).ypos);
	return 0;

}

 

struct point* pptr = &pos1;  포인터 변수 pptr구조체 변수 pos1을 가리킨다.

point는 자료형과 같은 것이다. &연산자로 주솟값을 반환하는 것은 구조체 변수인 pos1이 돼야 한다.

 

(*pptr). xpos += 4;  현재 pptr이 pos1을 가리키므로, 이 연산은 pos1을 대상으로 한다.

 

pptr = &pos2;  포인터 변수 pptr이 구조체 변수 pos2를 가리키게 된다. 

 

pptr ->xpos += 1;  현재 pptr이 pos2를 가리키므로, 이 연산은 pos2를 대상으로 한다. 

해석을 하자면, pptr이 가리키는 변수의 멤버 xpos의 값을 1 증가한다.

 

-> 연산자는 * 연산과. 연산을 대신한 것이다.

즉, (*pptr). xpos의 의미와 pptr ->xpos 의미는 동일하다. 그러므로 훨씬 더 간결한 -> 연산자를 사용할 수 있도록 해야 한다.

 

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

 

#include<stdio.h>

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;
}

 

struct point* center; 구조체 point의 포인터 변수 center인데, 포인터 변수도 구조체의 멤버가 될 수 있다.

 

struct point cen = { 2,7 }; point라는 이름의 구조체의 구조체 변수 cen을 선언과 동시에 초기화해주었다.

 

struct circle ring = { rad, &cen }; 구조체 변수 ring의 멤버 center가 구조체 변수 cen을 가리키는 형태가 되었다.

앞선 예제에서 공부했듯이 struct point * center = &cen; 와 같을 꼴이 된다.

 

(ring.center)->xpos ring의 멤버 center가 포인터 변수이므로 ring.center를 대상으로 -> 연산을 해서, 구조체 변수 cen의 멤버에 접근해야 한다. 그리고 연산자 우선순위로 인해 -> 연산자의 왼편에 있는 소괄호는 생략이 가능하다고 한다.

 

<p467 예제>

 

#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 };
	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] 연결\n",
		pos3.xpos, pos3.ypos, pos3.ptr->xpos, pos3.ptr->ypos);
	return 0;
}

 

pos1.ptr = &pos2; 구조체 변수 pos1의 멤버인 포인터 변수 ptr이 pos2를 가리킨다.

 

pos1.ptr->xpos :  pos1의 멤버 ptr이 포인터 변수이므로 pos1.ptr을 대상으로 -> 연산을 해서,  pos2의 멤버에 접근한다.

왜 pos2의 멤버에 접근할까? pos1의 멤버 ptr이 pos2를 가리키고 있기 때문이다.

 

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

 

#include<stdio.h>

struct point
{
	int xpos;
	int ypos;
};

int main(void)
{
	struct point pos = { 10,20 };
	printf("%p %p", &pos, &pos.xpos);
	return 0;
}

 

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