반응형
[ 구조체 ]
구조체는 struct 키워드로 정의한다.
struct 구조체이름{
자료형 멤버이름;
...
};
// 구조체는 정의만 해서 사용할 수가 없다. 구조체도 변수로 선언해서 사용한다.
struct 구조체이름 변수이름;
ex.
#define _CRT_SECURE_NO_WARNINGS // strcpy 보안 경고로 인한 컴파일 에러 방지
#include <stdio.h>
#include <string.h> // strcpy 함수가 선언된 헤더 파일
struct Person{ // 구조체 정의
char name[20]; // 구조체 멤버 1
int age; // 구조체 멤버 2
char address[100]; // 구조체 멤버 3
}
int main(){
struct Person p1; // 구조체 변수 선언
// 점으로 구조체 멤버에 접근하여 값 할당
strcpy(p1.name, "홍길동");
p1.age = 30;
strcpy(p1.address, "경기도 분당구 삼평동");
// 점으로 구조체 멤버에 접근하여 값 출력
printf("이름: %s\n", p1.name); // 이름 : 홍길동
printf("나이: %d\n", p1.age); // 나이 : 30
printf("주소: %s\n", p1.address); // 주소 : 경기도 분당구 삼평동
return 0;
}
# 실행 결과
이름 : 홍길동
나이 : 30
주소 : 경기도 분당구 삼평동
- 구조체는 보통 main함수 바깥에 정의한다. 만약 함수 안에 구조체를 정의하면 해당 함수안에서만 구조체를 사용할 수 있다.
- 정의한 구조체를 사용하려면 구조체 변수를 선언해야하고 구조체 이름 앞에 반드시
struct
키워드를 붙인다. - 구조체 멤버에 접근할 때는
.
을 사용한다.p1.age = 30
과 같이 구조체 멤버에 접근한 뒤 값을 할당하고,p1.age
같이 값을 가져온다.p1.name
등의 문자열 멤버는=(할당연산자)
로 저장할 수 없으므로strcpy
함수를 사용해야한다. - 구조체정의와 선언을 따로하지않고 동시에 변수를 선언할 수 있다.
struct 구조체이름{
자료형 멤버이름;
} 변수명;
- 구조체 변수를 선언함과 동시에 초기화 할 수 있다.
struct Person{
char name[20];
int age;
char address[100];
};
int main(){
struct Person p1 = { .name = "홍길동", .age = 30, .address="경기도 분당구 삼평동"};
struct Person p2 = { .name = "김성현", .age = 28, .address="서울시 관악구 신림동"};
...
}
...
typedef로 구조체 선언하기
일일이 struct 키워드를 붙이기 귀찮으니 별칭을 지정하는 방법으로 strcut 키워드를 생략해보자.
typedef struct 구조체이름{
자료형 멤버이름;
} 구조체별칭;
#define _CRT_SECURE_NO_WARNINGS // strcpy 보안 경고로 인한 컴파일 에러 방지
#include <stdio.h>
#include <string.h> // strcpy 함수가 선언된 헤더 파일
typedef struct _Person{
char name[20]; // 구조체 멤버 1
int age; // 구조체 멤버 2
char address[100]; // 구조체 멤버 3
} Person; // typedef를 사용하여 구조체 별칭을 Person으로 정의
int main(){
Person p1; // 구조체 별칭 Person으로 변수 선언
// 점으로 구조체 멤버에 접근하여 값 할당
strcpy(p1.name, "홍길동");
p1.age = 30;
strcpy(p1.address, "서울시 용산구 한남동");
// 점으로 구조체 멤버에 접근하여 값 출력
printf("이름: %s\n", p1.name); // 이름 : 홍길동
printf("나이: %s\n", p1.age); // 나이 : 30
printf("주소: %s\n", p1.address); // 주소 : 서울시 용산구 한남동
return 0;
}
# 실행 결과
이름 : 홍길동
나이 : 30
주소 : 서울시 용산구 한남동
- 만약에 구조체 별칭을 사용하지 않고 구조체 이름으로 변수를 선언하고 싶다면?
struct _Person p1; // 구조체 이름으로 변수 선언
=>struct _Person p1;
과 Person p1
은 완전히 같다.
typedef 활용하기
typedef
는 자료형의 별칭을 만드는 기능이다. 따라서 구조체뿐만 아니라 모든 자료형의 별칭을 만들 수 있다.
typedef 자료형 별칭
typedef 자료형* 별칭
typedef int MYINT; // int를 별칭 MYINT로 정의
typedef int* PMYINT; // int 포인터를 별칭 PMYINT로 정의
MYINT num1; // MYINT로 변수 선언
PMYINT numPtr1; // PMYINT로 포인터 변수 선언
numPtr1 = &num1; // 포인터에 변수의 주소 저장
// 사용방법은 일반 변수, 포인터와 같다.
- 이렇게
typedef
로 정의한 별칭을 사용자정의 자료형, 사용자 정의 타입이라 부른다. - 여기서
PMYINT
는 안에 *가 이미 포함되어 있으므로 포인터 변수를 선언할 때 *를 붙여버리면 이중포인터가 되므로 사용에 주의해야한다.
PMYINT *numPtr1; // PMYINT에는 *가 이미 포함되어 있어서 이중 포인터가 선언됨
int* *numPtr2; // PMYINT와 같은의미. 이중 포인터!
- struct 뒤에 붙는 구조체 이름은 원래 구조체 태그(tag)라고 부른다.(공용체, 열거형도 마찬가지로 공용체 태그, 열거형 태그라 부름). 그리고
typedef
로 정의한 구조체 별칭은 사용자 정이 타입의 이름이라 할 수 있다.
struct TAG{
자료형 멤버이름;
};
typedef struct TAG{
자료형 멤버이름;
} 타입이름;
- C언어는 나온지가 오래되다 보니 여러 관습이 남아 있는데 구조체 태그와 타입 이름을 구분하기 위해 관례상 태그 앞에
_
,_tag
,tag
를 붙이고 있따. 코드에 따라서 태그 뒤에_t
를 붙이기도 한다. - 구조체 태그와 타입이름은 사실상 같은 구조체를 지칭하므로 이름을 완전히 다르게 지을 필요는 없다. 요즘은 구조체 태그와 타입이름을 똑같이 만들기도 한다.
익명구조체
typedef
구조체 별칭을 정의할 때 매번 구조체 이름을 지정해주는것도 번거롭다. 이때는 익명 구조체(anonymous structure)를 사용하면 구조체 이름을 지정하지 않아도 된다.
typedef struct {
자료형 멤버이름;
} 구조체 별칭;
#define _CRTR_SECURE_NO_WARNINGS // strcpy 보안 경고로 인한 컴파일 에러 방지
#include <stdio.h>
#include <string.h>
typedef struct{
char name[20];
int age;
char address[100];
} Person; // typedef를 사용하여 구조체 별칭을 Person으로 정의
int main(){
Person p1; // 구조체 별칭 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);
return 0;
}
** 구조체 포인터 사용하기
보통 구조체는 멤버 변수가 여러 개 들어있어서 크기가 큰 편이다. 그래서 구조체 변수를 일일이 선언해서 사용하는 것 보다는 포인터에 메모리를 할당해서 사용하는 편이 효율적이다.
- 다른 자료형과 마찬가지로 구조체도 포인터를 선언할 수 있으며 구조체 포인터에는 malloc 함수를 사용하여 동적 메모리를 할당할 수 있다.
struct 구조체이름 *포인터이름 = malloc(sizeof(struct 구조체이름));
#define _CRT_SECURE_NO_WARNIGNS // strcpy 보안 경고로 인한 컴파일 에러 방지
#include <stdio.h>
#include <string.h> // strcpy 함수
#include <stdlib.h> // malloc, free 함수
struct Person{
char name[20];
int age;
char address[100];
};
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
키워드를 붙여야 한다는 점만 기억하면 된다. 즉, 포인터를 선언할 때도, sizeof로 크기를 구할 때도struct
키워드를 넣어준다. - 특이하게 지금 까지는 구조체의 멤버에 접근하는 방법이
.(점)
을 이용해서 접근했지만 구조체 포인터의 멤버에 접근할 때는->(화살표 연산자)
를 사용한다.
# 참고
구조체 포인터와 -> 구조체포인터에 접근할 때 -> 를사용하는데 ->는 화살표연산자(arrow operator)라고 부른다.
포인터는 메모리 주소를 저장하므로 어떤값이 있는 곳을 "가리키다"라는 의미가 있다. 그래서 연산자도 어떤 값이 있는 곳을 가리킨다는 의미에서 화살표 사용
# 구조체 포인터에서 .으로 멤버에 접근하기
구조체 포인터에서 멤버에 접근하려면 p1->age와 같이 화살표 연산자를 사용하는데 괄호와 역참조를 사용하면 .(점)으로 멤버에 접근할 수 있다.
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); // 10, 구조체의 멤버를 역참조
printf("%d\n", *d2->numPtr); // 10, 구조체 포인터의 멤버를 역참조
d2->c1 = 'a';
printf("%c\n", (*d2).c1); // a, 구조체 포인터를 역참조 하여 c1에 접근 ( d2->c1과 같음 )
printf("%d\n", *(*d2).numPtr); // 10, 구조체 포인터를 역참조 하여 numPtr에 접근한 뒤 다시 역참조( *d2->numPtr과 같음 )
free(d2);
return 0;
}
** 구조체 별칭으로 포인터를 선언하고 메모리 할당하기
구조체별칭 *포인터이름 = malloc(sizeof(구조체별칭));
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
typedef struct _Person{
char name[20];
int age;
char address[100];
} 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;
}
- 이처럼 구조체 별칭을 사용하면 포인터를 선언하고 메모리를 할당하는 방법이 더 간단해진다
Person *p1
과 같이 구조체 별칭으로 포인터를 바로 선언한 뒤malloc
함수로 메모리를 할당한다. 이때 할당할 메모리 크기도sizeof(Person)
처럼 구조체 별칭으로 구하면 된다. - 익명 구조체 선언하는 방법 또한 이전의 방법과 동일하다
** 구조체 포인터에 구조체 변수의 주소 할당하기
지금까지 malloc 함수로 구조체 포인터에 동적 메모리를 할당하였는데, 동적메모리를 할당하지 않고 구조체 포인터를 사용하는 방법이 있다? 이 때는 구조체 변수에
&(주소 연산자)
를 사용하면 된다.
구조체포인터 = &구조체변수;
#include <stdio.h>
struct Person{
char name[20];
int age;
char address[100];
};
int main(){
struct Person p1; // 구조체 변수 선언
struct Person *ptr; // 구조체 포인터 선언
ptr = &p1; // p1의 메모리 주소를 구하여 ptr에 할당
// 화살표 연산자로 구조체 멤버에 접근하여 값 할당
ptr->age = 30;
printf("나이: %d\n", p1.age); // 나이:30, 구조체 변수의 멤버 값 출력
printf("나이: %d\n", ptr->age); // 나이:30, 구조체 포인터의 멤버 값 출력
return 0;
}
- ptr에 p1의 메모리 주소를 할당했으므로 ptr의 멤버를 수정하면 결국 p1의 멤버도 바뀌게 된다. 접근하는 방식만 차이가 있을 뿐 결국 같은 곳의 내용을 수정하게 된다. ( 메모리 주소는 컴퓨터마다, 실행할 때마다 달라진다. )
반응형
'프로그래밍 언어 > [ C ]' 카테고리의 다른 글
[ C ] 10. 함수와 포인터 (1) | 2020.08.14 |
---|---|
[ C ] 09. 전처리기 ( if, ifdef, elif, ifndef, define, endif ) (0) | 2020.08.14 |
[ C ] 07. 열거형 (0) | 2020.08.13 |
[ C ] 06. 포인터연산 (0) | 2020.08.13 |
[ C ] 05. 메모리와 포인터의 사용 (0) | 2020.08.13 |