구조체
구조체란 ?
구조체는 배열보다 발전된 개념으로 변수의 종류에 관계없이 '서로 관련된 데이터를 하나로 묶어 놓은 것'이다.
학생정보를 입력받는 배열을 만든다면 하나의 배열에 모두 담는다면 좋겠지만 학생의 이름은 int에 저장할 수 없으므로 따로 선언할 수 밖에 없다.
char name[10]; //이름
int score[4]; //학번, 국어 점수, 수학 점수, 영어 점수
그러나 구조체를 사용하면 이러한 문제를 해결할 수 있다.
struct student { //구조체 student를 정의
int no; //학번
char name[10]; //이름
int kor, math, eng; //과목별 점수
} ;
위의 코드는 student라는 이름의 구조체를 정의한 것으로, 학생의 여러 정보를 하나의 구조체로 묶었다.
구조체의 정의는 구조체의 구성요소에 대한 정보일 뿐 구조체를 정의한다고 저장공간이 생기는 것은 아니다.
struct student s1 ; // 구조체 변수 s1을 선언
위와 같이 구조체 변수를 선언해야 저장공간이 생기고 구조체 안에 선언된 순서대로 연속적인 저장공간이 마련된다.
int 형 4개 + char 형 10개 + 패딩 2 바이트 = 28 바이트
패딩이란 ?
시스템의 '정렬 제한'에 의해 구조체의 멤버 사이에 빈 공간이 생긴 것
모든 변수는 주소가 4의 배수인 곳에만 저장되도록 배치한다는 것이다.
구조체 정의와 선언
정의
struct 구조체이름{ //구조체 student를 정의
타입 멤버 이름; //구조체의 멤버
타입 멤버 이름;
...
} ; <-중괄호 뒤에 ' ; '를 붙여야 함
이 구조체의 정의는 새로운 타입을 정의한 것일 뿐, 아직 값을 저장할 공간이 마련되지 않았다.
선언
구조체를 정의하면 struct '구조체 이름' 라는 타입이 새로 추가되고 구조체 타입의 변수를 선언하는 것은 기존의 변수 선언 방법과 다르지 않다.
예)
| #include<stdio.h> #include<string.h> #include<stdlib.h> typedef struct student { int no; char name[10]; int kor, math, eng; }Student; int main() { Student s1 = { 1,"KIM",100,100,100 }; printf("%d %s %d %d %d\n", s1.no, s1.name, s1.kor, s1.math, s1.eng); system("pause"); return 0; } | cs |
| typedef struct student { int no; char name[10]; int kor, math, eng; }Student; | cs |
구조체 타입의 변수를 선언할 때 매번 struct를 붙이는 것이 불편할땐
typedef로 새롭게 정의할 수 있다.
typedef struct student Student ; //struct student를 Student로 정의
Student s1 ; //struct student s1; 과 동일
구조체의 초기화
선언된 구조체 변수의 저장공간에 접근하려면 '구조체 변수이름. 멤버이름'의
형식을 사용해야 한다.
| int main() { Student s1 = { 1,"KIM",100,100,100 }; printf("%d %s %d %d %d\n", s1.no, s1.name, s1.kor, s1.math, s1.eng); system("pause"); return 0; } Colored by Color Scripter | cs |
구조체의 멤버에 접근할 때 사용하는 ' . '는 '멤버 접근 연산자'또는 간단히 '멤버 연산자'라고 한다.
struct student s1={ 1, "KLIDONG", 100,100,100} ;
//구조체 변수를 선언과 동시에 조기화
struct student s1 = { 0 } ;
//구조체 변수의 모든 멤버를 0으로 초기화
구조체의 중첩
구조체도 구조체의 멤버가될 수 있으며, 이렇게 하는 것을 구조체의 중이라고 한다.
| typedef struct userScore { char uerId[8]; //사용자 id int score,rank; //점수 int iyear, imon, iday; //입력날짜 int cyear, cmon, cday; //변경 날짜 }Userscore; | cs |
4,5번째 줄을 보면 입력날짜와 변경날짜를 따로따로 입력해줘야 하는데 이것을 또하나의 구조체로 만들어 중첩시킨다면
| struct date { int year, mon, day; }; typedef struct userScore { char uerId[8]; //사용자 id int score,rank; //점수 struct date inputDate; //입력날짜 struct date changeDate; //변경 날짜 }Userscore; | cs |
이렇게 구조체 userScore가 더 간결해졌고, 코드의 중복이 제거되어 수정도 쉬워졌다. 또한 구조체 date는 다른 구조체를 작성할때도 재사용될 수 있을 것이다. 또한 구조체 자신을 중첩하는 것은 허용되지 않는다.
중첩된 구조체의 멤버에 접근하려면 ' . '연산자를 한번 더 사용해야한다.
s1. inputDate. year = 2016;
s1. inputDate.mon = 1;
s1. inputDate.day = 6;
중첩된 구조체 변수의 선언과 초기화를 함께 할 때는 다음과 같이 괄호를 중첩한다.
s1=userScore s1 = {"myId",100 ,1 , { 2016, 1, 31 },{ 2016 , 2, 2 }};
구조체의 배열
같은 타입의 구조체 변수들을 하나로 묶어서 배열로 선언할 수 있다.
배열 요소의 타입 구조체라는 것만 제외하면 일반 배열과 다르지 않으므로 그동안 배열을 다뤄왔던 것과 똑같이 하면 된다.
| typedef struct student { int no; char name[10]; int kor, math, eng; }Student; int main() { Student stuArr[] = { {1,"KIM",100,100,10}, {2,"LEE",90,90,90}, {3,"CHOI",80,80,80}, {4,"PARK",100,100,100} }; return 0; } | cs |
int no ,char name[10] ,int kor ,math, eng를 멤버로 갖고있는 구조체 Student형인 배열 stuArr를 4개 선언하고 선언과 동시에 초기화 시켰다. 구조체 배열을 초기화 하는 것은 2차원 배열을 초기화 하는 것과 동일하다.
구조체의 포인터
구조체 변수 역시 포인터로 다룰 수 있으며, 다루는 방법은 같다.
struct student s = { 1, "홍길동", 100, 100, 100 } ;
struct student *p = &s ; //구조체 변수 s의 주소를 포인터 p에 저장
포인터로 구조체의 멤버에 접근할 때는 '*'연산자와 '.'연산자를 같이 사용한다.
포인터로 구조체의 멤버에 접근하려면, 두개의 연산자에 괄호까지 더해야 해서 불편하다. 그래서 제공되는 연산자가 '->'이다.
'->'는 포인터가 가리키는 구조체의 멤버에 저장된 값을 반환한다.
s . kor = 90 ;
(*p). kor = 90 ;
p -> kor = 90 ;
구조체의 배열의 포인터
구조체 배열을가리키는 포인터의 타입은 구조체의 타입에 '*'을 붙인 것이다.
struct student stuArr[4];
struct student *pArr = stuArr;