문자열 함수는 말 그대로 문자열을 버퍼에 담고 출력하는 함수입니다.
주로 문자열 함수들은 문자열 버퍼의 포인터를 리턴하게 되는데요, 이 때문에 버퍼 오버플로우 현상이 일어날 수 있습니다.
TCHAR* alphabet = "ABCDEFGH"
TCHAR buf[3];
_tcscpy(buf, alphabet);
이와 같은 경우, 담을 수 있는 버퍼(2글자 + NULL)보다 큰 포인터가 들어오게 되고 이 버퍼 이후에 진행될 스택의 내용을 문자열들이 덮어버려 의도하지 않은 결과를 가져올 수 있습니다.
이런 문제를 보완하기 위해 Windows에서는 안전한 문자열 함수들을 제공하고 있습니다.
strsafe.h 헤더를 포함시키면 사용할 수 있으며 그 종류들은 아래와 같습니다.
기존 함수 | 안전한 문자열 함수 |
strcpy | StringCbCopy, StringCbCopyEx StringCchCopy, StringCchCopyEx |
strncpy | StringCbCopyN, StringCbCopyNEx StringCchCopyN, StringCchCopyNEx |
strcat | StringCbCat, StringCbCatEx StringCchCat, StringCchCatEx |
strncat | StringCbCatN, StringCbCatNEx StringCchCatN, StringCchCatNEx |
sprintf | StringCbPrintf, StringCbPrintfEx StringCchPrintf, StringCchPrintfEx |
vsprintf | StringCbVPrintf, StringCbVPrintfEx StringCchVPrintf, StringCchVPrintf |
gets | StringCbGets, StringCbGetsEx StringCchGets, StringCchGetsEx |
strlen | StringCbLength StringCchLength |
이 함수들을 보면, Cb, Cch, Ex가 있습니다. 이 차이는
Cb : 버퍼의 크기를 인자로 받는다. (버퍼가 몇 바이트의 크기인가)
Cch : 버퍼의 길이를 인자로 받는다. (버퍼가 문자열 몇개를 담을 수 있는가)
Ex : 버퍼 패딩, 자름 등 추가 기능을 가진다.
Ex를 제외한 Cb, Cch 함수들을 설명하도록 하겠습니다.
- 이 함수들은 기존 함수들이랑 동일한 문자열을 받도록 되어 있습니다.
- 또한 TCHAR를 인자로 쓴다면 원형 그대로를, WCHAR라면 함수의 맨끝에 W, Ansi 문자열이라면 A를 붙여주어야 합니다.
- StringCbCopyW, StringCbCopyA
- 그런데, W나 A를 붙이지 않고 그냥 쓰면 알아서 바꿔줍니다.
1. StringCbCopy, StringCchCopy
HRESULT StringCbCopy (
OUT LPTSTR pszDest, // 복사 결과를 저장할 버퍼
IN size_t cbDest, (sizeof(TCHAR)) // 복사 결과를 저장할 버퍼의 버퍼의 크기 (pszSrc + 1 NULL 포함)
IN LPCTSTR pszSrc // 복사 원본 (NULL 포함된 문자열)
)
HRESULT StringCbCopy (
OUT LPTSTR pszDest,
IN size_t cchDest, (STRSAFE_MAX_CCH) // 위와 같고, NULL 포함한 pszSrc의 길이
IN LPCTSTR pszSrc
)
위에서 설명한 것처럼 버퍼의 크기를 인자로 받을 것인지, 길이를 인자로 받을 것인지 이외 두 함수간의 다른 점은 없습니다.
strcpy, wcscpy, _tcscpy, lstrcpy.... 등의 문자열 복사 함수들을 대체할 수 있습니다.
리턴형은 HRESULT로
SUCCEEDED, FAILED
2. StringCbCopyN, StringCchCopyN
HRESULT StringCbCopyN(
OUT LPTSTR pszDest, // 복사 결과를 저장할 버퍼
IN size_t cbDest, (sizeof(TCHAR)) // 복사 결과를 저장할 버퍼의 크기 (pszSrc + 1 NULL 포함)
IN LPCTSTR pszSrc, // 복사 원본
IN size_t cbSrc // 복사 원본 중에서 복사하고 싶은 만큼의 버퍼 크기
)
HRESULT StringCchCopyN(
OUT LPTSTR pszDest, // 복사 결과를 저장할 버퍼
IN size_t cchDest, (STRSAFE_MAX_CCH) // 복사 결과를 저장할 버퍼의 길이 (pszSrc + 1 NULL 포함)
IN LPCTSTR pszSrc,
IN size_t cchSrc // 원본 문자열(pszSrc)에서 복사하고 싶은 만큼의 길이
)
원본 문자열에서 원하는 버퍼/길이 만큼 복사하는 함수로
strncpy, wcsncpy, _tcsncpy를 대체합니다.
3. StringCbCat, StringCchCat
HRESULT StringCbCat(
OUT LPTSTR pszDest, // 이어 붙일 문자열의 앞부분
IN size_t cbDest, (sizeof(TCHAR)) // 저장할 버퍼의 총 크기 (pszDest + pszSrc + 1 NULL 포함)
IN LPCTSTR pszSrc, // 이어 붙일 문자열
)
HRESULT StringCchCat(
OUT LPTSTR pszDest, // 이어 붙일 문자열의 앞부분
IN size_t cchDest, // 저장할 문자열의 총 길이 (pszDest + pszSrc + 1 NULL 포함)
IN LPCTSTR pszSrc, // 이어 붙일 문자열
)
StringCbCat, StringCchCat 함수는 2개의 문자열을 서로 합치는 함수로
pszDest + pszSrc 순서로 합친다고 보시면 됩니다.
이 함수는 strcat, lstrcat, wcscat, _tcscat.... 등을 대체합니다.
4. StringCbCatN, StringCchCatN
HRESULT StringCbCatN(
OUT LPTSTR pszDest, // 이어 붙일 문자열의 앞부분
IN size_t cbDest, (sizeof(TCHAR)) // 저장할 버퍼의 총 크기 (pszDest + pszSrc + 1 NULL 포함)
IN LPCTSTR pszSrc, // 이어 붙일 문자열
IN size_t cbMaxAppend // pszSrc의 문자열 중에서 pszDest에 연결하고 싶은 만큼의 버퍼 크기
)
HRESULT StringCchCatN(
OUT LPTSTR pszDest, // 이어 붙일 문자열의 앞부분
IN size_t cchDest, // 저장할 문자열의 총 길이 (pszDest + pszSrc + 1 NULL 포함)
IN LPCTSTR pszSrc, // 이어 붙일 문자열
IN size_t cbMaxAppend // pszSrc의 문자열 중에서 pszDest에 연결하고 싶은 만큼의 문자열 길이
)
StringCbCatN, StringCchCatN 함수는
눈치 채셨겠지만 위의 StringCchCopyN과 마찬가지로 pszSrc에서 pszDest로 문자열을 이어 붙일 때 전부 붙이지 않고 pszSrc의 길이/버퍼를 지정한 만큼(cbMaxAppend)만 복사해 붙입니다.
이 함수는 strncat... 등을 대체합니다.
5. StringCbPrintf, StringCchPrintf
HRESULT StringCbPrintf(
OUT LPTSTR pszDest, // 문자열을 저장할 버펴 변수
IN size_t cbDest, (sizeof(TCHAR)) // 저장할 버퍼의 총 크기 (pszDest + 1 NULL 포함)
IN LPCTSTR pszFormat, // 문자열 포맷 (ex : _T("%s")
........ // 저장 할 문자열
)
HRESULT StringCchPrintf(
OUT LPTSTR pszDest, // 문자열을 저장할 버펴 변수
IN size_t cchDest, (_countof(pszDest)) // 저장할 문자열의 총 길이 (pszDest + 1 NULL 포함)
IN LPCTSTR pszFormat, // 문자열 포맷 (ex : _T("%s"))
........ // 저장 할 문자열 변수나 문자열 (ex: _T("Hello!"))
)
문자열을 버퍼에 담는 변수로 Printf 처럼 3번째 인자 이후로는 표현하고 싶은 내용이 여러개 일 경우 3번째 포맷 인자에 맞추어 계속 변수들을 쓸 수 있습니다.
예를 들면..
TCHAR* pszSanE = "산이";
StringCchPrintf(pszDest, _countof(pszDest), _T("%s물과 백두%s"), _T("동해"), pszSanE);
라고 한다면 pszDest에는
동해물과 백두산이\0
가 들어가게 됩니다.
6. StringCbVPrintf, StringCchVPrintf
HRESULT StringCbVPrintf(
OUT LPTSTR pszDest, // 문자열을 저장할 버펴 변수
IN size_t cbDest, (sizeof(TCHAR)) // 저장할 버퍼의 총 크기 (pszDest + 1 NULL 포함)
IN LPCTSTR pszFormat, // 문자열 포맷 (ex : _T("%s")
IN va_list argList // 저장 할 문자열의 리스트
)
HRESULT StringCchVPrintf(
OUT LPTSTR pszDest, // 문자열을 저장할 버펴 변수
IN size_t cchDest, (_countof(pszDest)) // 저장할 문자열의 총 길이 (pszDest + 1 NULL 포함)
IN LPCTSTR pszFormat, // 문자열 포맷 (ex : _T("%s"))
IN va_list argList // 저장 할 문자열 변수나 문자열의 리스트 (ex: _T("Hello!"))
)
가변 인자를 처리하는 안전한 printf 함수입니다.
vsprintf, vswprintf, _vsprintf....를 대체합니다.
가변 인자에 대해 짤막하게 설명해드리면, 리스트 형태로 인자의 갯수를 변경할 수 있다는 것을 말합니다.
인자가 1개, 2개, 3개... 가변적으로 변한다는 이야기입니다.
실제로 printf에서 문자열을 출력할 때 문자열의 포맷과 변수의 갯수를 제한없이 정해줄 수 있는 것과 같은 개념으로 이해하시면 됩니다. (int printf(const char* _Format, ......) <- 이 ...이 바로 가변 인자입니다.)
가변 인자로 함수를 만들려면 가변 인자를 사용해야하는데요
가변 인자 매크로들은 va_로 시작하며 stdarg.h에 정의되어 있습니다. (내용이 길어지기 때문에 다른 포스팅에서 가변인자에 대해 정리하도록 하겠습니다.)
7. StringCbGets, StringCchGets
HRESULT StringCbGets(
OUT LPTSTR pszDest, // 문자열을 저장할 버펴 변수
IN size_t cbDest, (sizeof(TCHAR)) // 저장할 버퍼의 총 크기 (pszDest + 1 NULL 포함)
)
HRESULT StringCchGets(
OUT LPTSTR pszDest, // 문자열을 저장할 버펴 변수
IN size_t cchDest, (_countof(pszDest)) // 저장할 문자열의 총 길이 (pszDest + 1 NULL 포함)
)
fgets, gets의 안전한 문자열 함수로 공백을 포함한 문자열을 입력받고 버퍼에 저장합니다.
cchDest, cbDest를 초과한 입력은 잘려서 pszDest로 저장됩니다.
8. StringCbLength, StringCchLength
HRESULT StringCbLength(
IN LPCTSTR psz, // 문자열의 크기를 체크할 버펴 변수
IN size_t cbMax, // psz로 입력받을 수 있는 최대 버퍼 크기
OUT size_t *pcb // psz의 문자열 버퍼 크기 리턴
)
HRESULT StringCchLength(
IN LPCTSTR psz, // 문자열의 크기를 체크할 버펴 변수
IN size_t cchMax, // psz로 입력받을 수 있는 최대 문자열의 길이 (MAX_PATH, MAX_FILENAME 등등)
OUT size_t *pch // psz의 문자열 길이 리턴
)
strlen, wcslen, _tcslen 함수를 대체하는 문자열 길이 리턴 함수입니다.
두번째 인자(cchMax, cbMax)는 psz의 길이가 아니라 개발하시면서 정의해둔 문자열의 최대 길이를 의미합니다.
예를 들면...
#define MAX_PATH 255 // 입력받는 경로의 최대 길이
void pathlength(LPCTSTR lpszPath)
{
size_t len = 0;
StringCchLength(lpszPath, MAX_PATH, &len);
}
요런 형태로 사용하실 수 있습니다.
'Programming Language > C, C++' 카테고리의 다른 글
C++ 기본 자료형 (0) | 2016.05.06 |
---|---|
네임드 파이프 예제 (0) | 2016.04.30 |
C 데이터, 스택, 힙 영역 (0) | 2014.12.08 |
C++ Volatile 키워드 (0) | 2014.11.15 |
C++ 포인터와 레퍼런스의 차이 (0) | 2014.11.12 |