C++Builder Programming Forum
C++Builder  |  Delphi  |  FireMonkey  |  C/C++  |  Free Pascal  |  Firebird
볼랜드포럼 BorlandForum
 경고! 게시물 작성자의 사전 허락없는 메일주소 추출행위 절대 금지
C++빌더 포럼
Q & A
FAQ
팁&트릭
강좌/문서
자료실
컴포넌트/라이브러리
메신저 프로젝트
볼랜드포럼 홈
헤드라인 뉴스
IT 뉴스
공지사항
자유게시판
해피 브레이크
공동 프로젝트
구인/구직
회원 장터
건의사항
운영진 게시판
회원 메뉴
북마크
볼랜드포럼 광고 모집

C++빌더 Q&A
C++Builder Programming Q&A
[3119] Re:안시스트링에서 c_str() 사용의 오류입니다.
박지훈.임프 [cbuilder] 4048 읽음    2000-03-20 00:00
무명씨 님이 쓰신 글 :
: StringGrid를 이용하여 각 셀에 들어있던 정보들을 화일에 저장하였다가 불러오는 프로그램을
: 짜고 있는데 4가지 파일에 저장했다가 불러오는 것입니다. 소스는 아래와 같이 하였습니다.
:
: void __fastcall TRForm::RSave()
: {
:     char szFileName[MAXFILE+4];
:     int iFileHandle;
:     int iColCountLen;
:     int iRowCountLen;
:     int iLength;
:     AnsiString aFileName;
:     AnsiString cColNo;
:     AnsiString cRowNo;
: //SRB(RadioButton) 4개를 설치하여 체크된 결과에 따라 4가지 파일로 저장한다.(그룹으로 묶었음)
:     if(SRB->Checked==true)
:     {
:         aFileName="sreg.orf";
:     }
:     else if(MRB->Checked==true)
:     {
:         aFileName="mreg.orf";
:     }
:     else if(LRB->Checked==true)
:     {
:         aFileName="lreg.orf";
:     }
:     else
:     {
:         aFileName="hreg.orf";
:     }
: //같은 이름의 화일은 백업해 둔다.
:         if (FileExists(aFileName))
:         {
:             fnsplit(aFileName.c_str(), 0, 0, szFileName, 0);
:             strcat(szFileName, ".OLD");
:             RenameFile(aFileName, szFileName);
:         }
:         iFileHandle = FileCreate(aFileName);
: //RSGrid는 StringGrid 임
:         cColNo=IntToStr(RSGrid->ColCount);
:         cRowNo=IntToStr(RSGrid->RowCount);
:
: // 그리드의 rows 와 columns 의 수를 적는다.
:         iColCountLen=cColNo.Length()+1;
:         FileWrite(iFileHandle, &iColCountLen, sizeof(int));
:         FileWrite(iFileHandle, cColNo.c_str(), iColCountLen);
:
:         iRowCountLen=cRowNo.Length()+1;
:         FileWrite(iFileHandle, &iRowCountLen, sizeof(int));
:         FileWrite(iFileHandle, cRowNo.c_str(), iRowCountLen);
:
:
: // 각 string의 길이와 스트링을 적는다.
:         for (int x=0;x<RSGrid->ColCount;x++)
:         {
:             for (int y=0;y<RSGrid->RowCount;y++)
:             {
:                 iLength = RSGrid->Cells[x][y].Length()+1;
:                 FileWrite(iFileHandle, &iLength, sizeof(int));
:                 FileWrite(iFileHandle, RSGrid->Cells[x][y].c_str(), iLength);
:             }
:         }
:         FileClose(iFileHandle);
: }
:
: void __fastcall TRForm::ROpen()
: {
:     int iFileHandle;
:     int iColCountLen;
:     int iRowCountLen;
:     int iLength;
:     AnsiString cColNo;
:     AnsiString cRowNo;
:     AnsiString aFileName;
:
:     if(SRB->Checked==true)
:     {
:         aFileName="sreg.orf";
:     }
:     else if(MRB->Checked==true)
:     {
:         aFileName="mreg.orf";
:     }
:     else if(LRB->Checked==true)
:     {
:         aFileName="lreg.orf";
:     }
:     else
:     {
:         aFileName="hreg.orf";
:     }
:         iFileHandle = FileOpen(aFileName, fmOpenRead);
: //ColCount와 RowCount 수를 읽어온다.
:
:         FileRead(iFileHandle, &iColCountLen, sizeof(int));
:         FileRead(iFileHandle, cColNo.c_str(), iColCountLen);
:         cColNo=(AnsiString)(cColNo.c_str());
:         RSGrid->ColCount=StrToInt(cColNo);
:
:         FileRead(iFileHandle, &iRowCountLen, sizeof(int));
:         FileRead(iFileHandle, cRowNo.c_str(), iRowCountLen);
:         cRowNo=(AnsiString)(cRowNo.c_str());
:         RSGrid->RowCount=StrToInt(cRowNo);
:
: //셀에 저장했던 데이터를 읽어와 보여준다.
:         for (int i=0;i<RSGrid->ColCount;i++)
:         {
:             for (int j=0;j<RSGrid->RowCount;j++)
:             {
:                 FileRead(iFileHandle, &iLength, sizeof(int));
:                 FileRead(iFileHandle, RSGrid->Cells[i][j].c_str(), iLength);
:                 RSGrid->Cells[i][j]=(RSGrid->Cells[i][j].c_str());
:             }
:         }
:         FileClose(iFileHandle);
: }
:
: 저장하는 버튼이 RSave()로 저장하고 라디오버튼을 체크하면 4가지중 한가지가
: StringGrid에 자료를 보여주는데......
: 문제는 2가지 자료는 보여주는데 3번째 자료부터는(무작위로 선택해도) 에러메시지가
: 뜹니다.
: 왜 그런지 누가 가르쳐주시고 잘못된 부분좀 설명해 주셔요...
: TFileStream을 이용하여 짜보다가 않되서 포기하고 API 함수로 짜보았습니다.
: 제발 누가 가르쳐주셔요....벌써 2주째 않되네요..




임펠리테리입니다.

파일을 쓰는 코드에는 별 이상이 없습니다만, 파일을 읽어들이는 부분에 스트링을 잘못 사용한 코드가 있군요. AnsiString의 c_str() 메소드에서 리턴되는 포인터는 그 자체로서는 char * 형이므로 읽거나 쓰거나 어떤 작업을 해도 문법적인 에러는 없습니다만, 이 포인터는 단지 안시스트링 내에 저장된 실제 스트링의 위치만을 가리키고 있을 뿐이므로 쓰는(write) 작업을 위해서 사용해서는 안됩니다.

예를 들어서, AnsiString qq; 라고 선언된 qq 변수를 생각해봅시다. qq에는 아직 아무런 데이터가 들어있지 않으므로 c_str() 이 리턴하는 내부의 포인터는 NULL을 가리키고 있습니다. NULL로 시작되는 메모리 영역이므로 여기에는 직접 어떤 write 작업을 해서도 안됩니다. qq = "www"; 하는 식으로 초기화가 이루어지고 나면, AnsiString은 스트링 값을 실제로 써넣기 전에 내부의 포인터에 실제로 메모리를 할당하고 그 이후에 스트링 데이터를 복사하는 것입니다. 또한, 원래 데이터가 있던 영역이라고 하더라도 원래 데이터보다 길이가 더 긴 데이터가 새로 입력될 경우 이전의 포인터와는 별개의 다른 영역에 새로 메모리를 할당해서 복사할 수도 있습니다.

결론적으로 간단히 말해서, 안시스트링의 c_str() 함수가 리턴하는 포인터는 읽기용으로만 사용해야지 쓰기용으로 사용해서는 안됩니다. 위의 경우와 같이 파일로 부터 읽은 문자열처럼 포인터 값으로 넘어온 것이라면, 파일로부터 읽을때는 별도로 이미 메모리가 할당된 문자열 포인터를 만들고 거기에 파일을 읽어들인후, 다시 안시스트링에 복사해넣어야 합니다.

...
//셀에 저장했던 데이터를 읽어와 보여준다.
char *qq = new char[256]; // 만약 한번에 읽어올 문자열 길이가 256이상일 경우가 있다면 더 크게 잡아준다
for (int i=0;iColCount;i++)
{
    for (int j=0;jRowCount;j++)
    {
        FileRead(iFileHandle, &iLength, sizeof(int));
        FileRead(iFileHandle, qq, iLength);
        RSGrid->Cells[i][j]=qq;
    }
}
...

이와 같이 해야 합니다. 그럼, 이제 TFileStream을 사용해도 되겠죠? ^^

그럼 참고하시길...

+ -

관련 글 리스트
3102 [질문]벌레좀 잡아주셔요....제발 무명씨 3687 2000/03/15
3119     Re:안시스트링에서 c_str() 사용의 오류입니다. 박지훈.임프 4048 2000/03/20
Google
Copyright © 1999-2015, borlandforum.com. All right reserved.