구조체를 파일에 읽거나 쓸 때 알고 쓰자.
보통 구조체를 통해 파일을 읽고 쓸 수 있는 것은 알 것이다.
fwrite(&somestruct, sizeof (somestruct), 1, fp);
형식으로 쓰면 되는데 만약 구조체가 포인터( char*타입의 문자열이나 다른 구조체를 가리키고 있는 포인터)
포함하고 있다면 파일내의 데이터는 단지 주소값만 저장되기 때문에
fread가 이루어진다 해도 주소값만 저장되지 주소값이 가르키고 있는 값은 저장되지 않는다.
밑의 코드 예를 보자.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define FILENAME1 "test1.txt"
typedef struct TmpData{
int f;
char arr[100];
char* pArr;
}sTmpData;
typedef struct FileData{
int a;
int c;
char b;
sTmpData e;
}sData;
int WriteStFile(sData* in);
int ReadStFile(sData* out);
int main()
{
sData WriteData = {2,33,'d'};
sData ReadData;
memset(&ReadData, 0, sizeof(ReadData) );
// init WriteData
WriteData.e.f = 100;
strcpy( WriteData.e.arr, "aaa");
WriteData.e.pArr = (char*)malloc( sizeof( WriteData.e.arr ) );
strcpy( WriteData.e.pArr, "bbb");
// Write File
WriteStFile(&WriteData);
// Read Structure Data from File
ReadData.e.pArr = (char*)malloc( sizeof( WriteData.e.pArr ) );
ReadStFile(&ReadData);
// print
printf("%d %d %c %d %s", ReadData.a, ReadData.c, ReadData.b, ReadData.e.f, ReadData.e.pArr);
free( WriteData.e.pArr );
system("pause");
return 0;
}
int WriteStFile(sData *in)
{
FILE* pFile = NULL;
pFile = fopen( FILENAME1, "wb" );
fwrite(in, sizeof(sData) , 1, pFile);
fclose(pFile);
return 0;
}
int ReadStFile(sData *out)
{
FILE* pFile = NULL;
pFile = fopen( FILENAME1, "rb" );
fread(out, sizeof(sData) , 1, pFile);
fclose(pFile);
return 0;
}
실행은 하면 다음과 같은 결과가 뜬다.
위에 예상 결과와 달리 bbb라는 문자열이 잘 출력되고 있다. 무엇이 문제일까?
바로 WriteData 라는 구조체 변수에서 할당되었던 값을 의미하는 것이다.
즉 ReadData.e.pArr 가 가르키는 주소값과 WriteData.e.pArr 가 가르키는 주소값이 같기 때문에
아무 이상없이 출력되는 것이다. 혹 이것을 보고 bbb라는 문자열 또한 다른 메모리로 복사된 것으로
착각하기 쉽다.
위 코드에서 생성된 파일을 가지고 다음 코드를 주석처리한 다음 다시 실행해보자.
WriteData.e.pArr = (char*)malloc( sizeof( WriteData.e.arr ) );
strcpy( WriteData.e.pArr, "bbb");
결과를 보면
메모리에 값이 없거나 쓰레기 값, 또는 다른 곳에서 쓰고 있는 값을 가지고 올 수도 있는 것이다.
또한 많이 하는 실수는 메모리 해제에 있다.
같은 메모리를 가르키고 있는 상태를 그대로 쓴다면 메모리 해제에 있어 다음과 같은 실수를 저지를 수 있다.
free( WriteData.e.pArr );
free( ReadData.e.pArr );
즉 같은 메모리 주소를 가지고 두번 메모리 해제를 하기 때문에 힙 에러가 나는 것이다.
마지막으로 데이터 파일의 이식성을 위해서는 fopen()시 b 플러그를 사용하는 것이 좋다.