컴퓨터공부/C & C++ & STL

가변 크기의 구조체 요소(임의적으로 크기를 변경할 수 있는 구조체 요소)

achivenKakao 2009. 7. 11. 11:35
일본 블로그에서 가져온 것입니다.

아래 글은 네이버 번역한 것이고요..

정확하진 않아도 의미는 충분히 전달되네요..

그럼 즐공~


+

구조체의 멤버에 가변장 배열(제1부 제 73장참조)를 넣는 일이 있다고 생각합니다.그러나, 그것을 파일에 보존하고 싶을 때, 처리가 조금 귀찮습니다.만약, 그 멤버 자체가 가변장이라면 간단합니다만....

 그럼, 이번 요점입니다.

  • 구조체의 마지막 멤버를 배열로 해 두면, 그 구조체의 뒤에 있는 데이터를 취급할 수 있다.
  • 의도적으로 크게 메모리를 확보해, 그 부분을 배열 멤버의 요소로 온다.


 그럼, 말해 봅시다.


 그런데, 파일의 정보를 구조체에 넣어 그것을 메모리상에 보존해 둔다고 합니다.간단 때문에, 정보는 패스명(파일의 위치명), 속성, 사이즈의 3개로 합니다.

struct SFileData
{
    char          szPath[_MAX_PATH];  // 패스명
    unsigned int  fAttribute;         // 속성
    unsigned int  nFileSize;          // 사이즈
};

 그러나, 패스명이라고 하는 것은 최고 문자수 _MAX_PATH 에 이르는 일은 분별없게 않은 것입니다.그 뿐만 아니라, 반에조차 못 미친 것이 대부분입니다.

 그렇게 되면,szPath 멤버는 이렇게도 확보하는 것은 메모리가 아깝습니다.그렇다고는 해도,_MAX_PATH 만 필요한 경우도 있으므로, 사이즈를 작게 하는 것도 할 수 없습니다.

 그럼, 어떻게 하면 좋은 것일까요? 가변장 배열(제1부 제 73장참조)를 사용하면, 어느 정도 문제는 해결할 것 같습니다.

struct SFileData
{
    char*         pszPath;     // 패스명
    unsigned int  nPathLen;    // 패스명의 길이
    unsigned int  fAttribute;  // 속성
    unsigned int  nFileSize;   // 사이즈
};

 이것으로 좋은 느낌입니다.

 그런데, 이것을 파일에 보존도 할 수 있도록(듯이) 하고 싶다고 합니다.이 구조체를 직접 보존하려고 하면,pszPath (은)는 포인터이므로, 거기에는 확보한 메모리에의주소하지만 보존되어 버립니다.그리고, 간신의 확보한 메모리의 내용은 보존되지 않습니다.

 그렇게 되면, 먼저 nPathLen 이후의 데이터를 보존해, 다음에 확보한 메모리의 내용을 보존한다고 하는, 2회의 처리가 필요하게 됩니다.전자의 것에서는 그대로 보존할 수 있다는데, 이것은 조금 귀찮습니다.

 가변장(이)면서멤버 변수에 포인터를 사용하지 않는다그렇다고 할 수 있으면, 이 문제도 해결합니다만....


 거기서, 다음과 같은 거친 기술을 생각할 수 있습니다.

 먼저,

struct SFileData
{
    unsigned int  nSize;      // 구조체의 사이즈
    unsigned int  fAttribute; // 속성
    unsigned int  nFileSize;  // 사이즈
    char          szPath[1];  // 패스명
};

그렇다고 하는 구조체를 만듭니다.szPath 의 사이즈는1입니다.

 그리고, 이 구조체를 동적으로 확보합니다.이 때, 구조체의 사이즈보다크게 메모리를 확보하면, 그여분으로 확보한 부분은 szPath (을)를 사용하는 것으로 이용할 수 있습니다.

 이렇게 하면, 파일에 보존할 때도 nSize 만 파일에 쓰면 좋은 것 같게 되는군요.

 왠지 난폭한 방법입니다만, 이 방법은 C/C++에서는 이따금 사용됩니다.이 길이가 바꿀 수 있는 멤버 szPath (을)를,가변장 배열 멤버(이)라고 부릅니다.

 잘 사용하는 곳에서는, 비트 맵의 정보를 보관 유지하는 구조체 BITMAPINFO 하지만 그렇게 되고 있습니다.

 256색비트 맵은 색을 0~255까지의 번호로 취급합니다.그리고, 그 번호에 대응하는 색 정보를 이 BITMAPINFO 구조체 로 유지해 둡니다.이 색 정보를 「팔레트」라고 부릅니다.

 이것은 16색비트 맵에서도 같습니다.그러나, 16색과 256색과는 팔레트의 수는 다릅니다.그 말은, 이 멤버는 가변장이라면 편리하네요.

 이것이 풀 컬러가 되면, 팔레트를 사용하지 않습니다.그래서, 이 멤버는 사용하지 않는다고 하는 것이 됩니다.이것으로부터 알 수 있듯이, 요소수는 1이 아니면 안 되는 것은 아닙니다.


 그럼, 실제로 SFileData 구조체를 사용해 봅시다.

프로그램
// Sizing1.cpp
#include <iostream.h>
#include <string.h>
#include <memory.h>
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>

#define numof(array)   (sizeof (array) / sizeof *(array))

struct SFileData
{
    unsigned int  nSize;      // 구조체의 사이즈
    unsigned int  fAttribute; // 속성
    unsigned int  nFileSize;  // 사이즈
    char          szPath[1];  // 패스명
};

void Init();   // 초기화
bool Input();  // 입력
void Output(); // 출력
void Delete(); // 메모리의 해방

// 파일의 데이터
SFileData* g_apfdata[100];
// 데이터수
int        g_nData = 0;

int main()
{
    Init();

    while(Input());
    Output();

    Delete();

    return 0;
}

// 초기화
// 일단 누르포인타의 값이 0이 아닐 때를 위해서...
void Init()
{
    int i;
    for(i = 0; i < numof(g_apfdata); i++)
        g_apfdata[i] = NULL;
}

// 입력
bool Input()
{
    if(g_nData == numof(g_apfdata))
        return false;

    // 영역은 동적 확보하지 않으면 안 되는 것은 아닙니다
    char       bufFData[sizeof (SFileData) + _MAX_PATH];
    SFileData* pfdTemp = (SFileData*)bufFData;
    char*      pbufTemp;

    cout << "패스명, 속성, 사이즈를 입력해 주세요" << endl;
    cin >> pfdTemp->szPath >> pfdTemp->fAttribute
        >> pfdTemp->nFileSize;
    if(pfdTemp->nFileSize == 0)
        return false;

    // 사이즈는 szPath 까지의 아르바이트수+문자열의 길이+1
    // 마지막 1은 누르캐라크타의 분입니다
    pfdTemp->nSize = offsetof(SFileData, szPath) +
                     strlen(pfdTemp->szPath) + 1;

    // 메모리의 확보
    pbufTemp = new char[pfdTemp->nSize];
    if(pbufTemp == NULL)
        return false;
    g_apfdata[g_nData] = (SFileData*)pbufTemp;

    // 데이터세트
    memcpy(g_apfdata[g_nData], pfdTemp, pfdTemp->nSize);

    g_nData++;
    return true;
}

// 출력
void Output()
{
    FILE* pfile;
    int  i;

    pfile = fopen("Sizing1.dat", "wb");

    for(i = 0; i < g_nData; i++)
        fwrite(g_apfdata[i], 1, g_apfdata[i]->nSize, pfile);

    fclose(pfile);
}

// 메모리의 해방
void Delete()
{
    int i;
    for(i = 0; i < g_nData; i++)
        delete [] (char*)g_apfdata[i];
}
실행 결과예
패스명, 속성, 사이즈를 입력해 주세요
C:\Program\Algorithms.html 32 412315
패스명, 속성, 사이즈를 입력해 주세요
C:\Program\Registry\데스크탑 아이콘의 비표시법.txt 32 155
패스명, 속성, 사이즈를 입력해 주세요
C:\Program\Program.ico 32 3638
패스명, 속성, 사이즈를 입력해 주세요
C:\Program\카셋트 늦추어\slide.html 32 6380
패스명, 속성, 사이즈를 입력해 주세요
0 0 0
Sizing1.dat
16진치 문자
27 00 00 00 20 00 00 00  9B 4A 06 00 43 3A 3A 5C
72 6F 67 72 61 6D 5C 41  6C 67 6F 72 69 74 68 6D
73 2E 68 74 6D 6C 00 43  00 00 00 20 00 00 00 9b
00 00 00 43 3A 5C 50 72  6F 67 72 61 6D 5C 52 65
67 69 73 74 72 79 5C 83  66 83 58 83 4E 83 67 83
62 83 76 83 41 83 43 83  52 83 93 82 CC 94 F1 95
5C 8E A6 96 40 2E 74 78  74 00 23 00 00 00 20 00
00 00 36 0E 00 00 43 3A  5C 50 72 6F 67 72 61 6D
5C 50 72 6F 67 72 61 6D  2E 69 63 6F 00 31 00 00
00 20 00 00 00 EC 18 00  00 43 3A 5C 50 72 6F 67
72 61 6D 5C 83 4A 83 5A  83 62 83 67 82 B8 82 E7
82 B5 5C 73 6C 69 64 65  2E 68 74 6D 6C 00
'... ....J..C:\P
rogram\Algorithm
s.html.C... ....
...C:\Program\Re
gistry\데스크특
 프아이콘의 비표
 시법.txt.#... .
..6...C:\Program
\Program.ico.1..
. .......C:\Prog
ram\카셋트않고들
해\slide.html.

 이와 같이, 가변장 배열 멤버를 가지는 구조체는직접 선언하지 않습니다.즉,

SFileData fdata;

(와)과 같이는 하지 않습니다.단지, 마지막 멤버를 사용하지 않을 때는, 이와 같이 해도 우선 실제 손해는 없습니다.


 이번은 이것으로 마지막입니다.이번 프로그램은 조금 깁니다만, 침착해 해석해 봐 주세요.

 그럼, 이번 요점입니다.

  • 구조체의 마지막 멤버를 배열로 해 두면, 그 구조체의 뒤에 있는 데이터를 취급할 수 있다.
  • 의도적으로 크게 메모리를 확보해, 그 부분을 배열 멤버의 요소로 온다.


 그러면, 또.