티스토리 뷰

프로매퍼에서 제공하는 강력한 기능 중에 하나가 가변배열이다. 이 가변 배열용 api를 이용하면 포인터라는 어려운 개념을 알지 못하는 사람도 충분히 메모리를 동적 할당(alloc)할 수 있도록 해 준다.  그렇지만 아래에 기술되어 있는 내용 정도는 정확히 이해를 해 줘야 문제를 야기하지 않고 즐거운 코딩을 할 수 있다.

1. 정의

Varray 의 타입에 대하여 pfmVarray.h  속에  다음과 같이 미리 선언되어 있다.

#define pfmVarray(type) \
    struct
{ \
        long
size; \
        long
count; \
        type *data; \
    }


따라서 가변배열 변수를 선언할 때는 다음과 같이 한다.

pfmVarray(sfee2105a_in_sub01) grid_id;

여기서 sfee2105a_in_sub01 은 타입이고 grid_id 는 변수명이다.  결국 위의 선언문장은 실제적으로는 다음과 같이 소스가 풀어지게 된다.

    struct {
        long
size;
        long
count;
        sfee2105a_in_sub01 *data;
    } grid_id;


위의 풀어진 가변 배열의 실체를 머릿속에 기억하고 있다면 프로그램을 짤 때 혼란이 적을 것이다.

2. 샘플 코드

다음과 같은 샘플 예제를 작성하여 돌려 보았다.

mfeebiz_sfee2105a_in_sub01  sub;
mfeebiz_sfee2105a_in in;

PFM_DBG(" mfeebiz_sfee2105a_in       = [%ld]", sizeof(mfeebiz_sfee2105a_in));
PFM_DBG(" mfeebiz_sfee2105a_in_sub01 = [%ld]", sizeof(mfeebiz_sfee2105a_in_sub01));

bzero( &in, sizeof(mfeebiz_sfee2105a_in));
PFM_DBG("Varray size           = [%ld]", pfmVarraySize(in.grid_id));
PFM_DBG("Varray count          = [%ld]", pfmVarrayCount(in.grid_id));
PFM_DBG("Varray Buffer Size    = [%ld]", pfmVarrayBufferSize(in.grid_id));
PFM_DBG("Varray Structure Size = [%ld]", pfmVarrayStructSize(in.grid_id));

for ( int ix = 0; ix < 102; ix++ ) {
    PFM_DBG("add [%d] =======", ix);
    pfmVarrayAppend( in.grid_id, &sub );
   
    PFM_DBG("Varray size           = [%ld]", pfmVarraySize(in.grid_id));
    PFM_DBG("Varray count          = [%ld]", pfmVarrayCount(in.grid_id));
    PFM_DBG("Varray Buffer Size    = [%ld]", pfmVarrayBufferSize(in.grid_id));
    PFM_DBG("Varray Structure Size = [%ld]", pfmVarrayStructSize(in.grid_id));
}


3. 수행결과

0153]  mfeebiz_sfee2105a_in       = [88]
0154]  mfeebiz_sfee2105a_in_sub01 = [336]
0157] Varray size           = [0]
0158] Varray count          = [0]
0159] Varray Buffer Size    = [0]
0160] Varray Structure Size = [336]
0163] add [0] =======
0166] Varray size           = [100]
0167] Varray count          = [1]
0168] Varray Buffer Size    = [33600]
0169] Varray Structure Size = [336]
0163] add [1] =======
0166] Varray size           = [100]
0167] Varray count          = [2]
0168] Varray Buffer Size    = [33600]
0169] Varray Structure Size = [336]

중간생략 --

0163] add [99] =======
0166] Varray size           = [100]
0167] Varray count          = [100]
0168] Varray Buffer Size    = [33600]
0169] Varray Structure Size = [336]
0163] add [100] =======
0166] Varray size           = [202]
0167] Varray count          = [101]
0168] Varray Buffer Size    = [67872]
0169] Varray Structure Size = [336]
0163] add [101] =======
0166] Varray size           = [202]
0167] Varray count          = [102]
0168] Varray Buffer Size    = [67872]
0169] Varray Structure Size = [336]

4. 결론

결론 초기에 추가할 수 있는 한계치로 100 을 설정해 놓고 있다가 100개를 초과하게 되면 size는 다시 자동 늘려준다.  메모리도 구조체의 사이즈 * size 만큼의 메모리를 미리 확보해 놓았다가 사이즈를 넘어서면 다시 추가로 더 늘려서 allocate 해 놓게 된다. 이 100 이라는 초기 배열의 개수를 정하는 알고리즘은 이 함수를 개발한 사람이 알고 있겠지만 늘상 100 으로 초기설정되는 것은 아니고 그 type 에 따라 달라지는 것을 보았다.
반면에 count 값은 몇번을 append 했는지의 회수 정보이다. 따라서 영원히 count  가 size 를 초과할 수는 없게된다.

특정 회수번지의 값을 찍어 보고자 한다면 다음과 같이  할 수 있다. 예를 들어 10번째의 것을 찍어본다면

PFM_DBG("  name = [%s]", grid_id.data[9].name );


5. 또 다른 예제

pfmVarray(long) vcnt;
long *cnt;
long *num;

bzero(&vcnt, sizeof(vcnt));

cnt = pfmVarrayAlloc(vcnt, 10);  /* 10개의 저장 공간 확보 */
cnt[1] = 1;  /* 두번째 배열에 값을 1 세팅함 */
PFM_DBG("sizeof(vcnt)          = [%ld]", sizeof(vcnt));
PFM_DBG("Varray size           = [%ld]", pfmVarraySize(vcnt));
PFM_DBG("Varray count          = [%ld]", pfmVarrayCount(vcnt));
PFM_DBG("Varray Buffer Size    = [%ld]", pfmVarrayBufferSize(vcnt));
PFM_DBG("Varray Structure Size = [%ld]", pfmVarrayStructSize(vcnt));

PFM_DBG("==== 출력 ====" );

PFM_DBG("vcnt.data[1]               = [%ld]", vcnt.data[1] );
PFM_DBG("pfmVarrayGet(vcnt, 1)      = [%p]", (long *)pfmVarrayGet(vcnt, 1));
PFM_DBG("pfmVarrayElementAt(vcnt,1) = [%p]", (long *)pfmVarrayElementAt(vcnt, 1));

/*좀 더 어렵게 출력해 보는 방법으로 */
PFM_DBG("*(_num = pfmVarrayGet(vcnt, 1)) = [%ld]", *(num=pfmVarrayGet(vcnt, 1)));



6. 실행결과


 sizeof(vcnt)          = [24]
 Varray size           = [10]
 Varray count          = [0]
 Varray Buffer Size    = [80]
 Varray Structure Size = [8]
 ==== 출력 ====
 vcnt.data[1]               = [1]
 pfmVarrayGet(vcnt, 1)      = [111593978]
 pfmVarrayElementAt(vcnt,1) = [111593978]
 *(_num = pfmVarrayGet(vcnt, 1)) = [1]



7. 결론

pfmVarrayGet() 과 pfmVarrayElementAt() 함수는 동일한 포인터를 리턴해 준다는 것을 기억해야 한다. 가변 배열 변수는 궁극적으로 스트럭쳐 라는 것을 기억한다면 이 변수의 초기화는 bzero(&vcnt, sizeof(vcnt)); 를 사용하거나 memset(&vcnt, 0x00, sizeof(vcnt)); 를 사용할 수 있다.

8. 위험한 상상

pfmVarrayAppend 로 값을 append 세팅해 놓은 다음 bzero(&vcnt, sizeof(vcnt)); 와 같이 초기화하는 것은 의미가 없으며 매우 위험하다. sizeof(vcnt) 의 값은 append 하기 전 선언당시의 구조체사이즈(24byte) 이기 때문이다. 추가로 할당된 메모리는 초기화되지 않게 된다. 심지어 vcnt.data 가 가지고 있던 포인터까지 지워버렸기 때문에 append 될 때 추가 할당된 메모리는 포인터를 잃어 버려서 free시킬 수도 없는 dangliing 상태에 놓이게 되며 memory leak 의 주요 원인이 된다.

또다른 예로서 memcpy( &another, &vcnt, sizeof(vcnt)); 뭐 이런식으로 sizeof를 사용했을 때는 딸랑 24byte 만 copy 된다는 것을 기억해야 한다. 왜냐면 Varray 변수의 사이즈는 24 byte ( long 변수 size와 count 로 2개, pointer 변수 data 로 1개 도합 8byte * 3 = 24 byte ) 이기 때문이다.
 

9. 가변배열의 메모리 해제

가변 배열에 구조체를 계속 append하다가 오류가 발생될 경우 PFM_TRY를 타고 프로그램이 종료된다. 프레임웍이 종료될 때 가변배열변수는 자동 해제를 시키지만 프레임웍의 시스템후처리를 지나기 전에 반복적으로 가변배열을 사용하는 모듈을 호출하게 되면 미처 해제되지 못한 가변배열이 쌓여서 시스템의 힙메모리가 부족해 지는 사태를 초래할 수 있다. 따라서 가변배열을 사용하는 모듈은 오류종료될 경우에는 아래와 같이 메모리를 직접 해제시키고 나가도록 코딩을 기술해 주면 시스템 안정성에 도움이 된다.

/*오류발생시 가변배열을 free 시켜서 메모리 leakage 방지 */
pfmVarrayFree(OUTPUT->grid_id);
/* 또한 그리드에 append한 개수를 관리하는 변수가 따로 있다면 이 값도 초기화 */
OUTPUT->rept_ncnt = 0; 

댓글
댓글쓰기 폼