구조체
구조체는 하나의 메모리 블록을 다양한 데이터 형으로 분할, 조합해서 사용하게 구조화된 자료형이다. 배열처럼 여럿이 모여서 하나를 이룬 것이다.
구조체는 배열과 비교해서 볼 수 있다. 배열은 int같은 같은 자료형이 n개 뭉쳐있는 것을 말하고, 구조체는 다른 자료형이 n개 뭉쳐있는 것을 말한다. 그 자료형이 다양한 것을 뭉쳐서 새로운 자료형을 만드는 것까지가 구조체다.
int[3]으로 배열이 int롤 3개 뭉쳐있으면, 4바이트*3개해서 12개라고 한다. 이것이 char[12](1바이트*12개)와 같다고 볼 수 있는데, 이것이 구조체와 차이점은, 전자는 간격이 균등하고 후자는 간격이 일정하지 않은 것만 다르다. 사실은 그것 말고는 같다. (물론 구조체는 ‘맴버 접근연산자’가 새로 붙기는 한다.)
구조체가 필요한 이유
만약 사람들의 이름, 나이를 저장하는 코드를 짠다고 한다면, 이렇게 배열선언 할 수 있다. char aListName[10][32] = {0};같이 이름, 나이를 저장할 요소를 만들어주고, 행마다 각각의 이름과 나이의 값을 strcpy로 넣어주면 될 것이다. 그리고 printf로 출력할 때, aAge배열과, aListName배열을 동시에 불러서 넣어준 값을 맞게 출력해줘야 되는데, 문제가 발생한다. 한 사람의 이름과 나이는 세튼데, 그 정보가 저장된 배열은 둘이 따로 놀게 된다. (예제에서는 인덱스를 맞춰줬지만, 그러지 않을 경우와 인덱스가 같아도 꼬일 경우를 생각해야 된다.) 이런 정보를 세트로 맞춰주는 것이 효과적인데, 이때 구조체를 효과적으로 쓸 수 있다.
구조체의 선언, 정의
“struct 구조체이름 변수이름;” 과 같이 구조체를 선언해주고, 정의는 main함수 밖에 “struct 구조체이름 { 자료형 멤버이름; };“ 과 같이 해주면 된다. 위의 int aAge[10] = {0}; 이랑 char aListName[10][32] = {0}; 는 남겨두고, 이둘을 묶어준다. 어떻게 묶어 주냐면, 우선 구조체 선언을 struct USERDATA user;로 한다.
그리고 struct USERDATA { int nAge; char szName[32]; }; 로, 스코프{ }안에 멤버를 넣어준다. 그리고 직접 써먹을 때는 ”구조체이름. 변수이름” 같이 해주면, .이 맴버 접근연산자로, 구조체에 정보를 넣어줄 수가 있다. 예를 들면, user.nAge + 5; 같이 해줄 수 있다.
간단하게 풀어보면, 큰 상자(구조체, USERDATA)안에, 두 개의 다른 상자(맴버, int nAge, char szName[32])가 들어있게 되고, 그 다른 상자 안에 다른 정보(값)을 넣어 준다고 비유할 수 있다. nAge, szName폴더를 user폴더 안으로 묶어주는 것으로도 비유 할 수 있다.
구조체의 배열과, 포인터
근데, 위와 같이 해주면, 한 묶음의 구조체 밖에 구현된다. 이름과 나이라면, 김동현, 17으로 묶인 한 개가 끝인 것이다. 이것을 여러 개 해주고 싶으면, 구조체의 선언에, 변수를 추가해주거나, 변수를 배열로 [n]같이 해주면 된다. 변수를 추가하는 것은, struct USERDATA user, newuser;로, 변수의 배열은 struct USERDATA user[10]같이 해주면 된다.
구조체는 포인터로는 pData.이 아니고, pData->(포인터 맴버 연산자)~과 같이 된다. 왼쪽의 피연산자가 주소가 들어가기 때문이다. 이때, ~에 들어가는 것은 ‘인텔리센스 데이터 베이스‘라고 하니 알아두자.
구조체 중첩 선언과 typedef과 연계
구조체도 중첩 선언이 가능하다. 그럴 경우 재밌는 점은, 변수이름에.(맴버 접근 연산자)이 두 개 들어갈 수도 있다. (그보다 많이도 가능하다만 잘 안 쓴다.)
63장의 사용자 정의 자료형, typedef과 연계해서도 사용할 수 있다. 아예 구조체 정의 앞에 typedef를 붙여주고, 정의 스코프 뒤에 사용자 정의 자료형의 이름을 붙여주면, 그 이름으로 구조체를 사용할 수 있다. 유용하게 쓰이니 기억해두자.