리메이크 중/C,C++ 이론 중심

C(&C++) 이론 14. 사용자 정의 자료형 1) 구조체

라이피 (Lypi) 2021. 6. 11. 22:00
반응형


내용 참고

YES C (정보공학연구소/생능출판사)

혼자 연구하는 C/C++ (Soen.kr/와우북스)

Microsoft Docs (구 MSDN)


C언어 이론 14 / 사용자 정의 자료형 1) 구조체

■ 사용자 정의 자료형이란 개발자가 직접 필요에 따라 새롭게 정의한 자료형을 의미한다.

■ 자료형이라는 것은 메모리상에 데이터를 저장할 공간을 지정하고 이를 해석할 방법을 정의한 것이다.

■ 하지만 C(&C++)에서 이런 의미의 기본적인 자료형은 이미 제공되고 있다.

■ 그러므로 C(&C++)에서 사용자 정의 자료형을 만들 때 메모리 크기나 해석 방법을 지정하는 경우는 없다.


Ⅰ. 구조체

■ 구조체란 여러 변수를 하나로 묶어놓은 형태라는 점에서 배열과 비슷하다. 

■ 배열은 한가지 타입의 집합이지만 구조체는 서로 다른 타입들도 하나의 이름으로 묶을 수 있다는 차이가 있다.

■ C언어에서 구조체는 변수만 멤버로 가질 수 있었다.

■ 하지만 C++에서는 객체(Class)라는 개념이 추가되면서 C++의 구조체도 함수를 멤버로 가질 수 있게 되었다.

■ 결과적으로 C++에서 구조체와 객체의 차이점이라고는 기본적인 접근성 외에는 없다.


ⅰ. 구조체의 정의

■ 구조체는 struct 키워드를 사용해서 아래와 같은 4가지 방법으로 정의할 수 있다.

■ 2)번과 같이 구조체 변수를 선언할 때 struct 키워드를 생략하는 것은 C++에서 추가된 방법이다.

■ 구조체 이름이 있으므로 구조체와 변수를 함께 선언한 뒤에도 1),2)번과 같이 추가로 구조체 변수를 선언할 수 있다.

■ 구조체 이름이 없으므로 선언할 때 외에는 구조체 변수를 선언할 수 없다.

■ typedef를 이용해서 구조체 이름을 지정하면 구조체 선언과 함께 변수를 선언할 수 없다.

■ typedef를 이용했으므로 "struct '구조체명' '변수명'"의 형태로 변수를 선언할 수는 없다.

 

■ 방법이 4가지나 있지만 특별한 경우가 아니라면 거의 첫번째 방법만 사용된다.

#include <iostream>
using namespace std;

struct point {
    int x, y;
};

int main()
{
    point z;
    z.x = 0; z.y = 0;
    printf("x:%d y:%d",z.x, z.y);
}

 

■ 구조체 이름은 '태그(Tag)'라고 하고, 구조체를 구성하는 각 변수들은 '필드(Field)'라고 부른다.

■ 이 필드들을 '구조체 멤버(Structure Member)'라고 부른다.

■ typedef 키워드를 사용하지 않았어도 구조체는 사용자 정의 자료형이므로 다른 자료형처럼 자유롭게 사용할 수 있다.

■ 즉, 구조체 배열도 가능하고, 구조체를 구조체의 멤버로 사용할 수 있으며, 구조체에 대한 포인터도 정의할 수 있다.


ⅱ. 구조체 선언과 정의의 차이점

■ 구조체를 선언하는 것은 컴파일러에게 이러한 자료형이 있다는 것을 알려주는 행위이다.

■ 실제 변수를 정의하기 전까지는 구조체 변수의 메모리가 할당되지 않는다.

■ 실제로 메모리에 할당된 변수를 '인스턴스(Instance)'라고 한다.


반응형

ⅲ. 구조체를 초기화 하는 방법

■ 구조체도 배열과 같이 초기화 리스트를 이용해서 초기화 할 수 있다.

■ 이 때, 데이터 타입이 다른 초기화 값들의 순서에 주의해야한다.

■ 초기화 리스트에서 뒷부분의 값을 생략할 경우 배열과 같이 정수는 '0, 실수는 '0.0', 문자는 '\0'으로 초기화된다.

//구조체 초기화 리스트 사용 예제
#include <iostream>
using namespace std;

struct point {
    char name[20];
    int x, y;
};

int main()
{
    point a = {"원점",0,0};
    printf("%s: x:%d, y:%d \n", a.name, a.x, a.y);

    return 0;
}

■ 순수한 C언어에서는 구조체의 멤버에 초기값을 줄 수 없지만, C++에서는 기본값을 설정할 수 있다.

//구조체 멤버 기본값 사용 예제
#include <iostream>
using namespace std;

struct point {
    char name[20] = "원점";
    int x = 0,y = 0;
};


int main()
{
    point a;
    printf("%s: x:%d, y:%d \n", a.name, a.x, a.y);
    
    return 0;
}

■ 이에따라 위의 구조체 초기화 리스트 사용 예제와 구조체 멤버 기본값 사용 예제의 결과는 같다.


ⅳ. 구조체 필드에 접근하는 방법

■ 구조체 변수 a와 b의 멤버 변수 x, 즉, a.x와 b.x는 기본적으로 서로 별개의 메모리를 사용한다. 

■ 포인터가 아닌 구조체 변수 내의 변수에 접근할 때는 '.(직접 멤버선택 연산자)'를 이용한다.

■ 예를 들어 point라는 구조체에 대한 구조체 변수 a의 멤버 변수 x에 접근한다면 a.x로 접근한다.

■ point 구조체 a에 대한 포인터는 point* pa = &a의 형태로 선언할 수 있다.

■ 구조체 a에 대한 포인터 pa의 멤버 변수 x에 접근할 때는 (*pa).x 혹은 pa->x를 이용한다.

■ (*pa).x라고 쓸 때는 *(포인터)연산자보다 .(직접 멤버선택 연산자)의 우선순위가 높기 때문에 괄호를 꼭 써야한다.

■ (*pa).x와 pa->x는 같은 의미이기 때문에 ->(간접 멤버선택 연산자)를 활용한 pa->x의 형태를 더 많이 사용한다.


ⅴ. 구조체 들의 배열, 구조체 속의 배열, 구조체 속의 구조체

■ 구조체도 하나의 타입이므로 당연히 구조체들의 배열을 선언할 수 있다.

■ sample구조체에 대해서 sample sa[10]을 선언했다면 sa[0]의 필드 x에 대해서는 sa[0].x로 접근할 수 있다.

■ sa -> x는 sa[0].x와 같다.

■ 구조체 속의 필드는 어떤 타입이든 가능하므로 배열이 들어갈 수도 있다.

■ sample구조체 속에 int ia[10]이 들어있고, sample s1을 선언했다면 s1의 ia[0]에는 s1.ia[0]로 접근할 수 있다.

■ 구조체 속의 필드에는 당연히 구조체도 들어갈 수 있다.

■ 하지만 sample 구조체의 필드로 sample 구조체(자기자신)를 넣는 것은 안된다.

■ 대신에 자기자신에 대한 포인터는 필드로 가질 수 있다.

■ ex라는 구조체안에 sample 구조체가 i라는 이름으로 들어있고, ex a1을 선언했다면 a1.i.x로 a1안의 x에 접근할 수 있다.


ⅵ. 구조체끼리의 대입 (얕은 복사, 깊은 복사)

■ 배열 이름은 배열의 첫 시작부분을 가리키는 포인터 상수이기 때문에 대입의 대상이 될 수 없었다.

■ 하지만 같은 정의에서 선언된 구조체 변수끼리는 서로 대입이 가능하다.

■ 이러한 대입은 구조체 안에 포인터 변수가 없다면 제대로 된 복사가 가능하나, 포인터 변수가 있다면 '얕은 복사' 밖에 되지 않는다.

■ 얕은 복사란 같은 주소를 가리키는 포인터 변수만 복사하는 것을 의미한다.

■ 포인터 변수가 가리키고 있는 공간 자체를 새롭게 만들어 데이터까지 복사하는 것을 '깊은 복사'라고 한다.

■ 공간을 새롭게 만들기 위해서는 동적 할당을 해야 하므로 '깊은 복사'는 자동으로 이루어지지 않는다.

■ 이 부분을 자동으로 해결하기 위해서 깊은 복사를 하는 '복사 생성자'를 정의해 줄 필요가 있다.

(생성자 개념은 클래스에서 소개되므로 여기서는 생략한다.)


ⅶ. 얕은 복사가 문제가 되는 경우

■ 모든 경우에 깊은 복사가 필요한 것은 아니지만 깊은 복사가 필수적인 경우가 있다.

■ 예를 들어 구조체가 문자열을 포함하고 있는 경우이다.

■ 문자열은 내부적으로 char형 배열이고, 배열의 이름은 포인터 상수이기 때문이다.

■ 이 상태의 구조체 변수를 복사하면 같은 문자열을 가리키고 있는 상태로 복사가 된다.

■ 즉, 복사 후의 문자열을 바꾸면 복사 전의 문자열도 변경되는 것이다.

■ 이를 방지하려면 새로운 문자열을 담을 공간을 새로 만드는 깊은 복사를 해야한다.


반응형