42. 구조체 포인터
1. 키워드
- 구조체 포인터
2. 구조체 포인터 사용하기
- 보통 구조체는 멤버 변수가 여러 개 들어있어서 크기가 큰 편이다.
- 그래서 구조체 변수를 일일이 선언해서 사용하는 것보다는 포인터에 메모리를 할당해서 사용하는 편이 효율적이다.
3. 구조체 포인터를 선언하고 메모리 할당하기
- 다른 자료형과 마찬가지로 구조체도 포인터를 선언할 수 있으며, 구조체 포인터에는
malloc
함수를 사용하여 동적 메모리를 할당할 수 있다.
#define _CRT_SECURE_NO_WARNINGS // strcpy 보안 경고로 인한 컴파일 에러 방지
#include <stdio.h>
#include <string.h> // strcpy 함수가 선언된 헤더 파일
#include <stdlib.h> // malloc, free 함수가 선언된 헤더 파일
struct Person // 구조체 정의
{
char name[20]; // 구조체 멤버 1
int age; // 구조체 멤버 2
char address[100]; // 구조체 멤버 3
};
int main()
{
struct Person *p1 = malloc(sizeof(struct Person)); // 구조체 포인터 선언, 메모리 할당
// 화살표 연산자로 구조체 멤버에 접근하여 값 할당
strcpy(p1->name, "홍길동");
p1->age = 30;
strcpy(p1->address, "서울시 용산구 한남동");
// 화살표 연산자로 구조체 멤버에 접근하여 값 출력
printf("이름: %s\n", p1->name);
printf("나이: %d\n", p1->age);
printf("주소: %s\n", p1->address);
free(p1); // 동적 메모리 해제
return 0;
}
// 이름: 홍길동
// 나이: 30
// 주소: 서울시 용산구 한남동
- 먼저
struct Person *p1
과 같이struct
키워드와 구조체 이름을 사용하여 구조체 포인터를 선언한다. - 이때 일반 변수가 아닌 포인터 변수이므로 반드시
*
를 붙인다. - 그리고
malloc
함수로 메모리를 할당할 때 크기를 알아야 하므로sizeof(struct Person)
과 같이 구조체 크기를 구해 넣어준다.
- 지금까지
.
을 사용해서 멤버에 접근했지만, 구조체 포인터의 멤버에 접근할 때는->
(화살표 연산자)를 사용한다.
// 화살표 연산자로 구조체 멤버에 접근하여 값 할당
strcpy(p1->name, "홍길동");
p1->age = 30;
strcpy(p1->address, "서울시 용산구 한남동");
// 화살표 연산자로 구조체 멤버에 접근하여 값 출력
printf("이름: %s\n", p1->name);
printf("나이: %d\n", p1->age);
printf("주소: %s\n", p1->address);
p1->age = 30;
과 같이 구조체 포인터의 멤버에 접근한 뒤 값을 할당하고,p1->age
와 같이 값을 가져온다.p1->name
등의 문자열 멤버는=
로 저장할 수 없으므로strcpy
함수를 사용하면 된다.- 마지막으로
free(p1);
처럼 할당한 메모리를 해제해 준다.
구조체 포인터에서 .
으로 멤버에 접근하기
- 구조체 포인터에서 멤버에 접근하려면
p1->age
와 같이 화살표 연산자를 사용하는데()
(괄호)와*
(역참조)를 사용하면.
으로 멤버에 접근할 수 있다.
(*p1).age
와 같이 구조체 포인터를 역참조하면pointer to struct Person
에서pointer to
가 제거되어서struct Person
이 된다.- 따라서
.
으로 멤버에 접근할 수 있다.
구조체의 멤버가 포인터일 때 역참조하기
- 구조체의 멤버가 포인터일 때 역참조를 하려면 맨 앞에
*
를 붙이면 된다. - 이때 구조체 변수 앞에
*
가 붙어있더라도 멤버의 역참조이지 구조체 변수의 역참조가 아니다.
#include <stdio.h>
#include <stdlib.h>
struct Data
{
char c1;
int *numPtr; // 포인터
};
int main()
{
int num1 = 10;
struct Data d1; // 구조체 변수
struct Data *d2 = malloc(sizeof(struct Data)); // 구조체 포인터에 메모리 할당
d1.numPtr = &num1;
d2->numPtr = &num1;
printf("%d\n", *d1.numPtr); // 구조체의 멤버를 역참조
printf("%d\n", *d2->numPtr); // 구조체 포인터의 멤버를 역참조
d2->c1 = 'a';
printf("%c\n", (*d2).c1); // 구조체 포인터를 역참조하여 c1에 접근
// d2->c1과 같음
printf("%d\n", *(*d2).numPtr); // 구조체 포인터를 역참조하여 numPtr에 접근한 뒤 다시 역참조
// *d2->numPtr과 같음
free(d2);
return 0;
}
// 10
// 10
// a
// 10
- 구조체 변수
d1
의 멤버numPtr
을 역참조하는 방법과 구조체 포인터d2
의 멤버numPtr
을 역참조하는 방법을 그림으로 표현하면 다음과 같은 모양이 된다.
- 만약 역참조한 것을
()
로 묶으면 구조체 변수를 역참조한 뒤 멤버에 접근한다는 뜻이 된다. *(*d2).numPtr
처럼 구조체 포인터를 역참조하여numPtr
에 접근한 뒤 다시 역참조할 수도 있다.
- 여기서
(*d2).c1
은d2->c1
과 같고,*(*d2).numPtr
은*d2->numPtr
과 같다. - 즉, 구조체 포인터를 역참조한 뒤
()
로 묶으면->
연산자에서.
연산자를 사용하게 되므로 포인터가 일반 변수로 바뀐다는 뜻이다.
4. 구조체 별칭으로 포인터를 선언하고 메모리 할당하기
struct
키워드로 포인터를 선언하고 메모리를 할당했으니,typedef
로 정의한 구조체 별칭으로도 포인터를 선언하고 메모리를 할당할 수 있다.
#define _CRT_SECURE_NO_WARNINGS // strcpy 보안 경고로 인한 컴파일 에러 방지
#include <stdio.h>
#include <string.h> // strcpy 함수가 선언된 헤더 파일
#include <stdlib.h> // malloc, free 함수가 선언된 헤더 파일
typedef struct _Person // 구조체 이름은 _Person
{
char name[20]; // 구조체 멤버 1
int age; // 구조체 멤버 2
char address[100]; // 구조체 멤버 3
} Person; // typedef를 사용하여 구조체 별칭을 Person으로 정의
int main()
{
Person *p1 = malloc(sizeof(Person)); // 구조체 별칭으로 포인터 선언, 메모리 할당
// 화살표 연산자로 구조체 멤버에 접근하여 값 할당
strcpy(p1->name, "홍길동");
p1->age = 30;
strcpy(p1->address, "서울시 용산구 한남동");
// 화살표 연산자로 구조체 멤버에 접근하여 값 출력
printf("이름: %s\n", p1->name);
printf("나이: %d\n", p1->age);
printf("주소: %s\n", p1->address);
free(p1);
return 0;
}
// 이름: 홍길동
// 나이: 30
// 주소: 서울시 용산구 한남동
- 구조체 별칭을 사용하면 포인터를 선언하고 메모리를 할당하는 방법이 좀 더 간단하다.
Person *p1
과 같이 구조체 별칭으로 포인터를 바로 선언한 뒤malloc
함수로 메모리를 할당한다.- 이때 할당할 메모리 크기도
sizeof(Person)
처럼 구조체 별칭으로 구하면 된다.
- 구조체 별칭으로 선언한 포인터도 구조체 멤버에 접근할 때는
->
를 사용한다.
// 화살표 연산자로 구조체 멤버에 접근하여 값 할당
strcpy(p1->name, "홍길동");
p1->age = 30;
strcpy(p1->address, "서울시 용산구 한남동");
// 화살표 연산자로 구조체 멤버에 접근하여 값 출력
printf("이름: %s\n", p1->name);
printf("나이: %d\n", p1->age);
printf("주소: %s\n", p1->address);
- 마지막으로 구조체를 다 사용했다면 반드시
free
함수로 메모리를 해제해 준다.
- 익명 구조체도 실제로 사용하려면 구조체 별칭을 지정해 줘야 하므로 메모리 할당 방법은 앞과 같다.
#define _CRT_SECURE_NO_WARNINGS // strcpy 보안 경고로 인한 컴파일 에러 방지
#include <stdio.h>
#include <string.h> // strcpy 함수가 선언된 헤더 파일
#include <stdlib.h> // malloc, free 함수가 선언된 헤더 파일
typedef struct // 구조체 이름이 없는 익명 구조체
{
char name[20]; // 구조체 멤버 1
int age; // 구조체 멤버 2
char address[100]; // 구조체 멤버 3
} Person; // typedef를 사용하여 구조체 별칭을 Person으로 정의
int main()
{
Person *p1 = malloc(sizeof(Person)); // 구조체 별칭으로 포인터 선언, 메모리 할당
// 화살표 연산자로 구조체 멤버에 접근하여 값 할당
strcpy(p1->name, "홍길동");
p1->age = 30;
strcpy(p1->address, "서울시 용산구 한남동");
// 화살표 연산자로 구조체 멤버에 접근하여 값 출력
printf("이름: %s\n", p1->name);
printf("나이: %d\n", p1->age);
printf("주소: %s\n", p1->address);
free(p1);
return 0;
}
// 이름: 홍길동
// 나이: 30
// 주소: 서울시 용산구 한남동
5. 구조체 포인터에 구조체 변수의 주소 할당하기
- 지금까지
malloc
함수로 구조체 포인터에 동적 메모리를 할당했다. - 동적 메모리를 할당하지 않고 구조체 포인터를 사용하려면 구조체 변수에
&
(주소 연산자)를 사용하면 된다.
#include <stdio.h>
struct Person // 구조체 정의
{
char name[20]; // 구조체 멤버 1
int age; // 구조체 멤버 2
char address[100]; // 구조체 멤버 3
};
int main()
{
struct Person p1; // 구조체 변수 선언
struct Person *ptr; // 구조체 포인터 선언
ptr = &p1; // p1의 메모리 주소를 구하여 ptr에 할당
// 화살표 연산자로 구조체 멤버에 접근하여 값 할당
ptr->age = 30;
printf("나이: %d\n", p1.age); // 구조체 변수의 멤버 값 출력
printf("나이: %d\n", ptr->age); // 구조체 포인터의 멤버 값 출력
return 0;
}
// 나이: 30
// 나이: 30
- 먼저
struct Person p1;
과 같이 구조체 변수를 선언하고,struct Person *ptr;
과 같이 구조체 포인터를 선언했다. - 아직까지는
p1
과ptr
은 관계가 없다.
- 구조체 변수는
&
를 사용하여 메모리 주소를 구할 수 있으며, 이렇게 구한 메모리 주소는 구조체 포인터에 할당할 수 있다.
ptr
은 구조체 포인터이므로->
로 멤버에 접근하여 값을 할당했다.- 그리고
printf
함수로p1
의 멤버와ptr
의 멤버를 출력해 보면 같은 값이 나오는 것을 알 수 있다.
// 화살표 연산자로 구조체 멤버에 접근하여 값 할당
ptr->age = 30;
printf("나이: %d\n", p1.age); // 구조체 변수의 멤버 값 출력
printf("나이: %d\n", ptr->age); // 구조체 포인터의 멤버 값 출력
- 즉,
ptr
에p1
의 메모리 주소를 할당했으므로ptr
의 멤버를 수정하면 결국p1
의 멤버도 바뀐다. - 접근하는 방식만 차이가 있을 뿐 결국 같은 곳의 내용을 수정하게 된다.