[C언어] C언어 그림으로 쉽게 보는 구조체와 구조체 포인터, 코드 예제
구조체와 포인터
지난 번에 구조체에 대한 이야기를 쬐~~~끔 했었죠? 이번에도 구조체를 가지고 놀아봐요.
구조체를 통해서 ,이야기했다 시피 여러가지 자료형을 통합적이고 효율적으로 작업을 할 수 있는 것이 장점입니다.
자료형이라...
우리는 이제까지 int, char, double, float 같은 자료형을 많이 봐왔지요. 하지만 구조체 자체도 자료형이 될 수 있습니다. 즉, 변수로 선언이 가능하다는 것이죠.
한번 되짚어 봅시다. int 자료형 변수 a를 선언하고 10이라는 값을 집어 넣어 보아라 한다면 우리는 식은 죽 먹기로 해낼 수 있습니다.
int a;
a = 10;
이렇게 자료형을 통한 변수는 값을 대입하는 것 외에도
1. 매개변수로 쓰일 수 있다.
2. 포인터로 참조할 수 있다.
3. 배열로 쓰일 수 있다.
4. 구조체의 변수로 쓰일 수 있다.
뭐 이밖에도 여러분이 더 잘 알거에요.
그래서 무슨 말이 하고 싶은 거냐 넌?
구조체도 자료형이 될 수 있다고 했습니다. 그러니까 위와 같이 쓰일 수도 있다는 이야깁니다. 똑같습니다. 위의 순서대로 구조체를 갖고 놀아 봅시다.
일단 매개변수로 쓰이는 경우를 보도록 하지요.
#include <stdio.h> typedef struct student{ char *name; int math; int kor; int eng; } student; float avg(student person); student getHonorStudent(student me, student you); int main() { student reakwon = { "REAKWON",40,50,40 }; student seonmi = { "선미",90,95,100 }; student honorStudent = getHonorStudent(reakwon, seonmi); printf("우등생은 %s입니다.\n", honorStudent.name); return 0; } float avg(student person) { return (person.math + person.kor + person.eng) / 3.0; } student getHonorStudent(student me, student you) { if (avg(me) > avg(you)) return me; if (avg(me) < avg(you)) return you; }
전교생이 두 명인 학교에서 우열을 참 가리기 쉬운 학생 두명이 있습니다(그러니 평균이 같은 학생은 없다는 가정을 하겠습니다). 누가 우등생인가를 구하는 코드입니다.
위 코드에서 reakwon이라는 학생 성적은 제 고등학교 시절과 정확히 똑같군요.
우리는 avg나 getHonorStudent함수에서 매개변수로 쓰인 student 구조체를 주목해야합니다. 변수가 매개변수로 함수로 전달하는 것과 일치하죠. 그 매개변수 앞에는 student라는 자료형이 있는 것과 같은 겁니다.
결과를 보죠.
예상했다 싶이 선미라는 아이가 우등생이네요.
오~ 매개변수로 쓰일 수 있구나!
두번째, 포인터로 참조할 수도 있습니다.
우리는 포인터를 통해 그 주소에 접근할때 *를 이용해서 접근했었죠.
포인터도 역시 똑같습니다.
(*구조체 변수).변수이름
구조체는 이와 같은 접근 방법외에도 다른 방법으로도 포인터를 통해 참조할 수 있습니다. "->" 이와 같은 표시로 말이죠.
구조체 변수->변수이름
마치 화살표 같은게 포인터 티가 나죠?
이제 코드로 한번 확인해보도록 합시다.
#include <stdio.h> typedef struct student{ char *name; int math; int kor; int eng; } student; int main() { student reakwon = { "REAKWON",40,50,40 }; student *me = &reakwon; printf("me의 크기:%d\n", sizeof(me)); printf("reakwon의 주소:%p, me가 가리키는 주소:%p\n", &reakwon, me); printf("\n"); printf("포인터를 통해서 값을 읽어오는 방법 1"); printf("수학:%d, 국어:%d, 영어:%d\n", (*me).math,(*me).kor,(*me).eng); printf("\n"); printf("포인터를 통해서 값을 읽어오는 방법 2"); printf("수학:%d, 국어:%d, 영어:%d\n", me->math, me->kor, me->eng); return 0; }
다음 결과 사진을 보고서 다시 이야기해 보도록 합시다.
포인터를 배울때와 같이 변수 me는 reakwon의 주소를 값으로 갖고 있습니다. 그리고 접근하는 방법 두가지 역시 같은 값을 나타내고 있습니다.
하지만 크기를 보세요. 구조체 포인터는 역시 포인터 크기(4바이트)와 같은 크기입니다. 주소만 갖고 있으면 되기 때문이죠.
그림으로 그려보면 이런 그림이겠군요.
우리는 한가지 생각해볼 점이 있습니다.
구조체의 크기가 크고 함수 매개변수로 쓰일 경우 어떻게 넘겨주는 것이 더 효율적일까요?
값을 복사하는 normal한 매개변수로 쓴다면 구조체의 크기만큼 복사해야합니다.
그러나 포인터를 사용한다면 단지 주소값만 넘겨주면 되기 때문에 시간 면에서나 효율 면에서 유리할 수 있습니다.
물론 포인터는 매개변수의 변형을 일으킬 수도 있지만, 그런 원치않는 조작을 막기위해서 const라는 키워드가 존재하는 겁니다.
이제 구조체를 배열로 관리해보겠습니다. 역시 쉽습니다. 바로 코드로 봅시다.
#include <stdio.h> typedef struct student{ char name[30]; int math; int kor; int eng; } student; float avg(student who) { return (who.math + who.kor + who.eng) / 3.0; } int main() { student students[3]; for (int i = 0; i < 3; i++) { printf("이름:"); scanf("%s", students[i].name); printf("수학 점수:"); scanf("%d", &students[i].math); printf("국어 점수:"); scanf("%d", &students[i].kor); printf("영어 점수:"); scanf("%d", &students[i].eng); printf("\n"); } for (int i = 0; i < 3; i++) { printf("%s의 점수\n",students[i].name); printf("수학 %d, 국어 %d, 영어 %d\n", students[i].math, students[i].kor, students[i].eng); printf("평균 %.1lf\n",avg(students[i])); printf("\n"); } return 0; }
세명의 학생의 이름과 점수를 입력받고 점수와 평균을 출력해주는 코드입니다.
배열을 포인터 연산으로 나타낼 수 있듯이 구조체 배열 역시 포인터 연산으로 나타낼 수 있습니다. 위의 코드를 (*(students+i)).kor 과 같이 코드를 한 번 바꾸어 실행해보세요.
위 코드의 결과가 아래의 캡처화면입니다.
구조체는 이렇게 편리합니다.
이제 마지막, 구조체는 구조체를 포함할 수 있습니다. 이것도 역시 변수와 같은 성격이죠. 구조체를 변수로 쓰는 방법은 아래와 같습니다.
typedef struct person {
char name[30];
int age;
char sex[10];
struct person friends[3];
} person;
구조체 안에 같은 구조체 타입의 변수가 배열로 들어가 있습니다. 구조체는 자료형이라고 했기 때문에 뭐 놀랍지도 않군요.
물론 다른 구조체 타입의 변수까지 멤버로 가질 수도 있습니다. 이런 엉뚱한 구조체는 어디서 쓰일까요?
나중에 자료구조에서나 알고리즘에서 트리의 노드와 같은 것으로 쓰일 수가 있습니다.
이제까지 구조체에 대해서 공부해봤습니다. 바이바이~