13-3.구조체의 초기화

13-3-가.초기화

구조체 멤버는 일반 변수와 완전히 동일하다. 그래서 멤버에 값을 대입할 때도 일반 변수와 마찬가지로 대입 연산자를 사용하는데 Friend.Age=30이라고 대입하면 된다. 이런 식으로 구조체의 각 멤버에 원하는 값을 대입할 수 있는데 멤버가 아주 많다면 일일이 대입하기 무척 귀찮을 것이다. 그래서 구조체를 선언함과 동시에 멤버의 값을 초기화시킬 수 있는 방법이 제공된다.

구조체를 초기화하는 방법은 배열과 거의 비슷하다. 선언시에 = 구분자와 { } 괄호를 쓰고 괄호안에 멤버의 초기값을 나열하면 된다. 다음은 tag_Friend형의 구조체 Friend를 선언하면서 이 구조체를 초기화하는 예이다.

 

tag_Friend Friend={"장달상", 30, 178.2 };

 

Name 멤버에 "장달상" 문자열이 복사되고 Age에는 30이 대입되며 Height는 178.2가 될 것이다. 초기값이 없는 멤버는 자동으로 0으로 초기화된다. 배열의 경우와 다른 점이라면 초기값이 대응되는 멤버의 타입과 같아야 한다는 점이다. Age는 정수형이므로 정수 상수를 대입해야 하며 Height는 실수형이므로 실수 상수를 대입해야 한다. 또한 Name은 문자 배열이므로 문자열 상수를 주어야 하는데 초기화 문자열이 Name 멤버의 길이보다 더 커서는 안된다.

구조체 배열을 초기화하는 방법도 배열의 초기화 방법과 동일하다. { } 중괄호안에 각 배열 요소가 되는 구조체의 초기값을 콤마로 끊어서 나열하면 된다. 다음 예제는 크기 10의 Friends 배열을 선언하고 이 중 앞쪽 다섯 개의 요소를 초기화한다.

 

: InitStruct

#include <Turboc.h>

 

struct tag_Friend {

     char Name[10];

     int Age;

     double Height;

};

 

void main()

{

     tag_Friend Friends[10]={

          {"김은철", 30, 178.2 },

          {"이노성", 42, 169.8 },

          {"이상엽", 26, 176.5 },

          {"이상문", 58, 172.3 },

          {"이영아", 60, 171.6 },

     };

 

     printf("세 번째 사람 정보 : 이름=%s, 나이=%d, 키=%.1f\n",

          Friends[2].Name,Friends[2].Age,Friends[2].Height);

}

 

초기식에 나타나는 순서대로 배열을 구성하는 구조체가 초기화될 것이다. Friends[0]에 김은철의 정보가 들어가고 Friends[1]은 이노성의 정보로 초기화된다. 만약 배열 크기보다 더 많은 초기식이 있으면 에러로 처리되며 초기식이 부족하면 나머지 요소는 모두 0으로 초기화되는데 이 점도 배열과 동일하다. 이 예제를 실행해 보면 세 번째 구조체의 정보가 출력된다.

구조체의 초기화 방법은 배열과 비슷하므로 특별히 새로울 것이 없다. 마지막 행의 여분 콤마는 구조체의 초기값 순서를 바꾸거나 개수를 늘릴 때의 편의를 위해 삽입된 것이며 컴파일러는 이 여분 콤마를 무시한다. 단순히 중괄호안에 초기화시킬 값을 나열하기만 하면 되므로 기술이라고 할만한 것도 아니다. 단, 크기가 큰 구조체를 초기화할 때는 가급적이면 지역으로 선언하지 않는 것이 좋다는 것만 알아 두자. 컴파일러는 구조체 초기식을 대입문으로 바꿔서 기록하기 때문에 구조체를 초기화하는 것은 굉장히 많은 시간이 걸린다.

위 예제의 경우 Friends 구조체가 별로 크지 않고 또한 main 함수는 어차피 한 번만 실행되기 때문에 큰 문제가 없지만 거대한 구조체를 일반 함수에서 초기화한다면 문제가 심각해진다. 그 함수가 불릴 때마다 그 큰 구조체에 일일이 값을 대입하는 동작을 해야 한다면 함수의 실행 속도가 아주 느려질 것이다. 심할 경우 함수 실행 시간의 99%가 구조체 초기화에 허비되는 경우도 있다. 이 점은 배열도 마찬가지이다.

초기화가 필요한 구조체나 배열은 보통 참고용 정보이기 때문에 읽기 전용의 속성을 가지는 경우가 많다. 예를 들어 과목별 성적 산출 방법이나 가중치에 대한 정보, 과세 기준표, 서비스별 수수료 금액 등이 좋은 예이다. 이런 정보들은 한 번 초기화한 후 바뀌지 않기 때문에 매번 초기화할 필요가 없으며 전역변수로 한 번만 초기화하는 것이 좋다. 또는 정 함수 내부로 통용 범위를 국한시키고자 한다면 static으로 선언해야 한다.

다음은 좀 더 복잡한 구조체를 초기화하는 예를 보자. 구조체 안에 2차 배열과 또 다른 구조체가 중첩되어 있다.

 

: InitStruct2

#include <Turboc.h>

 

struct tag_A {

     short i;

     int j;

};

 

struct tag_B {

     double d;

     int ari[3][2];

     tag_A A;

     char ch;

};

 

tag_B arb[]={

     {0.0, 0,0,0,0,0,0, 0,0,'0'},                      // 모든 멤버 나열

     {1.0, {{1,1},{1,1},{1,1}}, {1,1},'1'},            // 완전한 형식

     {2.0, {{2,2},{2,2},}, {2,2},'2'},                   // 배열 행의 일부 생략

     {3.0, {{3,3},{3,},{3,3}}, {3,3},'3'},              // 배열 열의 일부 생략

     {4.0, {{4,4},{4,4},{4,4}}, {4,},'4'},              // 포함 구조체의 일부 생략

     {5.0, },                                               // 배열 열의 일부 생략

};

 

void main()

{

     printf("%f\n",arb[2].d);

     printf("'%c'\n",arb[3].ch);

     printf("%d\n",arb[4].A.j);

}

 

이 예제는 배열을 포함한 중첩 구조체 배열을 초기화하는 여러 가지 방법들을 보여준다. arb[0] 초기식은 모든 멤버를 순서대로 나열했으며 빠진 값이 없기 때문에 별도의 중괄호가 없어도 된다. 그러나 이렇게 하면 보기 좋지 않기 때문에 멤버 별로 중괄호를 표시하는 것이 좋다.

arb[1]이 가장 완벽한 초기화 형식이다. 포함된 배열 전체를 { } 괄호로 한 번 싸고 배열의 각 행별로 다시 { }를 싸 주었으며 포함된 구조체도 마찬가지로 { }로 싸 주었다. ari 배열 전체의 초기값을 감싸는 { } 괄호는 생략할 수 없는데 이 괄호가 생략되면 최초의 중괄호 {1,1}을 배열에 대한 전체 초기값으로 판단해 버리므로 초기값이 남는다는 에러로 처리된다.

완벽한 형식으로 초기식을 작성하면 0으로 초기화하고 싶은 값에 대해서는 초기값을 생략할 수 있다. arb[2]는 배열 행의 일부를 생략했으며 arb[3]은 배열 열의 일부를 생략했는데 이렇게 하더라도 컴파일러가 { }에 의해 어떤 값이 어떤 멤버의 초기값인지를 구분할 수 있기 때문이다. arb[4]는 포함된 구조체의 초기값 중 j를 주지 않았는데 이 경우 j는 0으로 초기화된다. 만약 여기서 포함된 구조체를 감싸는 { }를 없애 버리면 i는 4가 되고 j는 이어지는 '4'의 값을 가지며 ch는 0이 되어 원하는대로 초기화되지 않는다.

arb[5]는 첫 번째 멤버인 d만 초기화했고 나머지는 모두 생략하여 0으로 초기화하도록 했다. 실전에서는 이 예보다 좀 더 복잡한 구조체도 사용될 수 있는데 예를 들어 배열을 포함한 구조체의 배열을 포함하는 구조체의 배열이 필요할 수도 있다. 이럴 때도 기본적인 초기화 방법을 응용하기만 하면 어떤 복잡한 구조체라도 초기화할 수 있을 것이다.