💻 오늘의 목표 : 구조체 완전 정복
1. 구조체
- 하나 이상의 변수를 묶어서 사용하기 위한 자료형
- 의미상 연관 관계가 있는 항목을 그룹으로 묶어 표현한 자료형
=> 구조화된 데이터를 처리할 때 유용
(예시) 사람의 인적사항을 정리할 때, 이름, 전화번호, 사는 곳을 한 번에 묶어서 구조체에 저장할 수 있다.
우리가 많이 사용하는 int, char 자료형이 있듯이 구조체도 내가 원할 때 선언해서 사용하는 자료형이다.
- 멤버(member) : 구조체를 구성하는 변수들
2. 구조체 정의, 선언, 사용
int, char 같은 자료형은 변수를 사용하기 전에 미리 선언이 필요했다.
int main() {
int i; // 변수 선언
scanf("%d", &i);
printf("%d", i + 100);
}
하지만 구조체는 선언하여 사용하기 전에 내가 구조체 안에 어떤 변수를 저장해야 할지 정의해 주는 작업이 필요하다.
[구조체 정의]
=> 기본 형태
struct person { // 구조체_자료형_이름
// 멤버_자료형 멤버_변수;
char name[10];
int phone_number;
char address[20];
};
struct로 정의하고 그 뒤에는 내가 앞으로 사용할 구조체 자료형 이름을 넣어준다.
그리고 중괄호를 열고 안에는 내가 쓰고 싶은 멤버의 자료형과 변수 이름을 지정해서 적어준다.
그리고 중괄호 닫고 마지막에 세미콜론!
- 기본 자료형, 배열, 포인터, 구조체까지 모두 멤버 자료형으로 사용이 가능하다.
- 정의만 해줬을 뿐 아직 메모리 공간할당이 되지 않은 상태
- 모든 함수에서 사용하기 위해 주로 함수 밖에 구조체를 정의함
- 함수 안에서 정의한 구조체는 그 함수 안에서만 사용 가능
=> 다양한 정의 형태
1. 자료형 정의와 변수 선언 동시에 하는 경우
struct person { // 구조체_자료형_이름
// 멤버_자료형 멤버_변수;
char name[10];
int phone_number;
char address[20];
} person;
닫힌 중괄호 뒤에 변수를 선언해 준다.
선언하는 동시에 변수 초기화도 가능하다.
//맨 마지막 줄
} P = {"Justin", 123456, "NewYork" };
2. 구조체 자료형 이름 생략하는 경우
(자료형 이름이 없으므로 다른 곳 사용 불가능)
struct {
// 멤버_자료형 멤버_변수;
char name[10];
int phone_number;
char address[20];
}person;
이때는 구조체 마지막에 선언한 전역변수 person만 선언이 가능하다.
[구조체 선언, 초기화]
이제 정의한 구조체를 선언하여 데이터를 저장할 수 있는 메모리를 할당시켜주어야 한다.
//구조체 선언
struct person P1, P2;
위와 같은 방식으로 'struct 구조체_자료형_이름 변수이름; ' 으로 선언할 수 있다.
(메모리 공간)
- 구조체 자체는 메모리 공간을 따로 할당받지 않는다.
- 구조체 안에 멤버 변수들의 공간이 각 자료형의 크기대로 각각 할당된다.
char name[10] | int phone_number | char address[20] |
(주의) 구조체 전체의 메모리는 각 멤버들의 메모리 합이 아닐 수 있다.
=> 구조체의 메모리 할당 공간을 알고 싶을 땐 sizeof 연산자 사용할 것
(초기화)
이제 초기화를 해보자
맨 처음 선언하면서 값을 중괄호 안에 나열하여 초기화시켜 줄 수 있다.
//구조체 선언
struct person P1 = {"Justin", 123456, "NewYork" };
char name[10] | int phone_number | char address[20] |
Justin | 123456 | NewYork |
[구조체 멤버 변수 사용]
구조체 변수는 구조체 멤버 전체를 나타내고, 만약 멤버 하나에 접근하고 싶다면 구조체 멤버 연산자(.)를 사용한다.
//구조체 변수.멤버변수
P1.phone_number;
[구조체 사용가능 연산자]
- 산술 연산, 비교 연산 지원되지 않음
- 제한적인 연산자 : 대입 연산자, 주소 연산자, 간접 참조 연산자, sizeof
3. 구조체 배열
구조체 변수들도 묶어서 배열로 쓸 수 있다.
[구조체 배열 선언]
struct person { // 구조체_자료형_이름
// 멤버_자료형 멤버_변수;
char name[10];
int phone_number;
char address[20];
};
int main(void) {
// 구조체 배열 선언
struct person P[5];
P[0].phone_number = 123;
}
이런 방식으로 구조체 배열을 사용할 수 있다.
선언과 동시에 초기화해 줄 수도 있다.
int main(void) {
//선언 동시에 초기화 (생략된 부분은 0으로 초기화됨)
struct person P[5] = { {"ABC", 123, "1-1"}, {"DEF", 456, "2-2"} };
}
이때 주의할 점은 꼭 중괄호 안에 데이터들을 나열해 주어야 한다는 것.
4. 구조체 포인터
<연산자 우선순위 주의할 것>
<정의된 구조체>
struct person { // 구조체_자료형_이름
// 멤버_자료형 멤버_변수;
int id;
int phone_number;
char* pname;
};
1. 포인터가 구조체의 멤버로 사용되는 경우
int main(void) {
struct person p1;
//문자 포인터에 문자열 연결
p1.pname = "ABC";
printf("%s\n", p1.pname); //문자열 출력
printf("%c\n", p1.pname[0]); // 0번 인덱스 문자 출력
printf("%c\n", *p1.pname); //문자 출력(포인터 간접 참조)
}
2. 구조체를 가리키는 포인터 (구조체 포인터)
- 구조체 포인터 변수를 선언하려면 참조 연산자 * 사용
int main(void) {
struct person P1 = { 1 , 123456, "Justin" }, P2;
struct person *pp;
pp = &P1; // 구조체 포인터 연결
P2 = *pp; //간접 참조 P1==P2
}
간접 참조를 이용하여 구조체 변수의 멤버에 접근하려면?
- 포인터가 가리키는 구조체에 접근하기 위해 참조연산자 * 사용
- 구조체 멤버에 접근하기 위해 멤버 연산자. 사용
- 멤버 연산자 우선순위 > 참조 연산자 우선순위 (괄호 필요)
int main(void) {
struct person P1 = { 1 , 123456, "Justin" }, P2;
struct person *pp;
pp = &P1; // 구조체 포인터 연결
P2 = *pp; //간접 참조 P1==P2
(*pp).id = 2; //멤버 변수 간접 참조
}
그러나 항상 사용할 때마다 괄호를 쓰는 것이 불편하기 때문에 구조체 포인터 전용 참조 연산자 (->) 가 있다.
pp->id = 2;
3. 구조체 포인터가 배열의 원소로 사용되는 경우(구조체 배열 포인터)
int main(void) {
struct person P1 = { 1 , 123456, "Justin" }, P2;
struct person* pp[4] = { &P1, &P2 }; // 구조체 포인터 배열 선언, 초기화
pp[2] = pp[1];
*pp[2] = *pp[0];
pp[1]->id = 3;
}
구조체 배열 | 구조체 포인터 | 구조체 포인터 배열 |
struct person p[5]; | struct person *p; | struct person *p[5]; |
컴퓨터 프로그래밍 전공하는 대학생이 공부했던 내용을 정리하기 위해 적는 소소한 블로그입니다.
잘못된 내용은 댓글에 달아주시면 피드백 감사히 받겠습니다!
[참고한 도서]
- 실전 C프로그래밍
댓글