본문 바로가기
Windows/MFC 강좌 & Tips

CString 함수 AfxExtractSubString / strtok, strtok_s, wcstok_s

by 미티치 2018. 2. 19.
AfxExtractSubString 함수 사용 시 메모리 부족 오류 나는 현상

if( AfxExtractSubString(szTmpBuffOne, szTmpBuff,  i, '\n') )

여기서 szTmpBuff는 사용자에게 입력받은 경로 값
-> 경로 길이가 너무 길어서 함수 내부에서 Exception 발생, 디버그로 돌리면 '메모리가 부족합니다'라는 오류 메시지 출력




따라서 그냥 메모리 힙 잡아서 구분자 사용하려고 수정


char str[] = "My name is Hong Gil Dong";
char *pToken = NULL;
char *pSeparator = " "; // 구분자
 
/* 첫번째 수행시에" " 구분자로 자른 후 My 라는 문자열을 리턴합니다. 정확히 얘기하면 주소값을 리턴합니다. 
나머지 name is.... 문자열은 strtok() 함수 내부에서 static 변수로 기억되고 있다가, 
두 번째 호출시 파라미터에 NULL을 받게되면 기억하고 있던 name is... 문자열을 꺼내어 다시 자르게 됩니다. */
 
pToken = strtok(str, pSeparator);
printf("%s\n", pToken);
 
/* 두번째 수행부터 strtok() 호출시에는 파라미터에 NULL을 넣음으로 계속해서 자르기 작업을 수행하게 됩니다.
strtok가 NULL을 반환하면 더 이상 자를 문자열이 없으므로 while 문을 빠져 나갑니다. */
 
while (NULL != (pToken = strtok(NULL, pSeparator)))
{
    printf("%s\n", pToken);
}

기본적인 함수 사용 방법은 예제와 같습니다. 하지만 주의사항으로 세 가지가 있습니다. 첫 번째는 strtok() 함수 사용 후 원본 문자열인 str[]의 데이터를 보장할 수 없습니다. 확인 결과 위 예제에서 strtok() 함수를 수행 후에 str[]내부의 “ “ space 문자는 NULL로 모두 변환 되어버립니다.
해결 방법은 원본 데이터를 보존하기 위한 임시 버퍼 변수를 사용하는 방법이 있겠습니다.
두 번째는 VS6.0에서는 strtok()는 잘 작동하지만 VS2005이상에서는 원할한 작동을 보장하지 않습니다. 그러므로 온라인상에서도 주요 포럼 게시물들을 살펴보면 strtok() 보다는 VS2005 이상에서 사용할 수 있게끔 개선된 strtok_s() 함수의 사용을 권장하고 있습니다.
세 번째는 strtok() 함수는 내부적으로 문자열 분리를 위한 위치 식별을 위해 static변수를 놓고 정적 공유하여 사용한다고 합니다. 그러므로 현재 진행 중인 분리 작업을 훼손할 수 있기 때문에 Thread-Safe 하지 않습니다. 이는 하나의 스레드에서 서로 다른 문자열을 동시에 분리하게 되면 문제가 발생될 여지가 있습니다. 이를 개선한 함수가 strtok_s()가 되겠습니다.
strtok_s() 의 사용법은 strtok()과 동일하나 세 번째 인자가 추가 되었습니다.



출처: http://ogoons.com/70 [오군의 기술 블로그]


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
void CControlTestDlg::OnBnClickedButtonCheckstring()
{
    // TODO: Add your control notification handler code here
    CString strStringTest;
    TCHAR * szStringTest = NULL;
 
    GetDlgItem(IDC_EDIT_StringTest)->GetWindowText(strStringTest);
    szStringTest = (TCHAR *malloc( strStringTest.GetLength() * sizeof(TCHAR) );
    //ZeroMemory(szStringTest, strStringTest.GetLength() * sizeof(TCHAR) );
    BOOL isValidPath = IsValidPath(szStringTest, strStringTest.GetLength() * sizeof(TCHAR) );
}
 
// ValidCheck
BOOL CControlTestDlg::IsValidPath(TCHAR* szInPaths, int szInPathsBuffSize)    
{
    TCHAR * pSeparator = _T("\r\n");
    TCHAR * context = NULL;
    TCHAR * pPath = NULL;
    BOOL ret = TRUE;
    TCHAR szDebugString[MAX_PATH] = {0};
    
    CString strInPaths;
    strInPaths.SetString(szInPaths );
    MessageBox(strInPaths);
 
    pPath = wcstok_s(szInPaths, pSeparator, &context);
    _stprintf_s(szDebugString, _T("wcstok_s(szInPaths, pSeparator, &context) Return = [0x%x]"), pPath);
    MessageBox(szDebugString);
    return ret;
}



사용자한테 EditCtrl로 입력받은 값을 strStringTest 에 저장한 후
strStringTest 값을 szStringTest에 복사를 안하고(깜빡함) 위처럼 테스트했더니 자꾸만 pPath가 NULL 값이 안나옴.

디버깅 하니까 szStringTest에 쓰레기 값이 들어가있었는데, 우연찮게 \r\n이 그 안에..있었나봄 (디버깅으로 확인했을 땐 이상한 특수문자들이 나왔음)
그래서 pPath가 valid한 값으로 나옴 

Line 9 에서 ZeroMemory 를 주석 푸니까 pPath가 return NULL 하더라...



'Windows > MFC 강좌 & Tips' 카테고리의 다른 글

(2) MFC 초기화 함수 : InitInstance, OnInitDialog  (0) 2020.08.08
(1) MFC 시작하기  (0) 2020.08.08
Dialog에서 툴바 생성  (0) 2017.08.23
UpdateData 함수  (0) 2017.01.04
Dialog based 그림판  (2) 2017.01.04