본문 바로가기

old_Coding/C/C++

[C/C++] 문자열 다루기(1) - 초기화



C/C++로 작업을 하면서 가장 신경 쓰이는 부분 중 하나가 아마문자열과 문자를 다룰 때 같습니다. NULL 포인터면 포인터가 죽고,작은 실수로 인하여 자칫 잘못하면 strcpystrlen 등 문자열 함수를 부르면 죽어버리는 경우가 흔하죠. 특히나 C/C++을 이제 막 시작하신 분들이나 자칫 잘못 이해하고 계신 분들은 이런 함수들을 부르기가 꺼려지실꺼라고생각됩니다.(그 중에 한명이 저구요.) 그래서 정리하는 겸, 정리 한번 하고 넘어가려고 합니다.

 

환경

O.S - Windows 7

Tool - Visual Studio 2008 + sp1.

Type - Win32 Console Project

(대부분의 경우, 글로벌변수로 char형 배열 인스턴스를 잡고, 함수내에서는 char* 로 조작하도록 하였습니다. 제 경우 포인터 접근이 조금더 까다로워서 연습삼아^^)

 


제가 제일 중요하게 생각하는 부분. 초기화 입니다. 먼저 초기화가 안돼있는 경우를 보시죠.


#include <stdio.h> 

char TempA[100] ; 
char TempB[50] ; 
int main() 
{ 
        return 0 ; 
} 

이 상태로 코딩한 뒤에 디버그 모드로 내부 변수 값을 확인해 보았습니다.

 초기화1.JPG초기화2.JPG
? 난 초기화시킨적 없는데, 이미 다 0이란 값이 들어가있군요. 그럼 저렇게 배열 변수를 정의하면 저절로 초기화 시켜주는군요! 그럼 뭐 신경쓸것도 없겠네! 라고 생각하고 싶습니다 저도..

 

간단하게 아래 코드를 다시 보시죠. 어떤 경우가 문제가 될지.

#include <stdio.h>

int main()

{

        char TempA[100] ;

        char TempB[50] ;

 

        return 0 ;

}

이 상태로 메모리 상태를 확인하겠습니다.

 

초기화3.JPG초기화4.JPG
아무래도 성급하게 판단해서는 안될 것 같네요. 바뀐건 글로벌->로컬(지역) 변수로 밖에 안 바꿨는데.. 값이 다르게 찍힙니다. 일명 쓰레기 값이 들어가 있는걸 확인할 수 있겠네요. 처음 이상태로 사용한다는 것은 조금 무리가 있을 듯 싶어요. 저의 경우는 저 상태로 사용하다가 프로그램이 그냥 죽어버리는 상황을 많이 보아왔습니다.(특히 로그찍을 때..^) 그럼 초기화를 시켜주죠. 저는 ZeroMemory()라는 함수를 애용합니다만, 알아보는 김에 셋트메뉴들을 다 알아보죠.

 

memset() - wmemset() 함수

그럼 차분히.. MSDN을 활용하여 원형을 구경해보도록 합시다. 자세한건 좌표를 드리죠.: http://msdn.microsoft.com/en-us/library/1fdeehz6(VS.90).aspx

좌표를 직접 확인하시는 것을 추천합니다. 그 중에 일부만 살펴보면,

 

초기화5.JPG함수 설명을 보면 버퍼를 특정 char값으로 설정한다.” 라고 하면 되겠네요.(저질번역입니다.) 리턴 값은 void니 없고, 파라메터가 3개있네요. 링크를 직접 가보시면 가장 처음 파라메터는 “Pointer to destination.” 이라고 되어있습니다. 목적지 포인터라는 것이겠죠. 두번째 파라메터는 “Character to set” 설정할 캐릭터형 변수값 정도가 될꺼구요. 마지막 파라메터는 “Number of characters” 캐릭터의 개수라고 할수있겠습니다. 그런데, 조금 당황스러운 것은 캐릭터의 개수가 조금 애매한 점이 있습니다. 왜냐하면, 멀티바이트에서 사용하는 “char”변수는 하나에 1바이트입니다. 그런데 유니코드상 사용하는 “wchar_t”변수는 하나에 2바이트거든요.(자세한 것은 관련 서적을 참고하시참고 좋을 것 같습니다.) 그럼 여기서 칭하는 캐릭터의 개수를 바이트를 의미하는 것일까요? 아니면 문자가 몇 개있는지를 의미하는 것일까요? 이 부분이 중요할 수 밖에 없는 것이 마지막 파라메터는 얼마만큼을 초기화 시킬것인가에 대한 부분이기 때문에 정확한 값보다 작다면 덜 초기화 될것이고, 많으면 이런 에러를 뱉게됩니다. 흑흑..
 초기화6.JPG

자 본론으로 돌아오면 memset이나 wmemset에서 표기하는 Number of characters는 바이트 크기가 아니라 배열의 개수를 뜻합니다. 즉 제가 wchar_t wTemp[100]; 이라고 정의해서 이녀석을 초기화 시킨다면 wmemset(wTemp, 0, 100); 이라고 넣으시면 됩니다. 간혹 wmemset(wTemp, 0, 100*sizeof(wchar)); 라고 넣으실수 있는데, 이렇게되면 결과값은 200이 됩니다.(wchar_t 형은 한 개한 2바이트니까요.) 그러면 저 에러를 다시 보게 되실꺼구요..

 

, 다시 본론으로 돌아올께요. 위에 좀 장황하게 적은건 size_t count 파라메터의 이해가 그만큼중요하다고 생각되서 길게 적었습니다. 그럼 아까 코드에서 좀 사용해보죠.

#include <stdio.h>

int main()

{

        char TempA[100] ;

        char TempB[50] ;

 

        ::memset(TempA, 0, 100);

        ::memset(TempB, 0, 50);

 

        return 0 ;

}

이렇게 치고 빌드를 돌려보니 에러가 4개네요.. 뭘 빼먹은 걸까요? 에러메세지는 다음과 같습니다.

1>c:\users\gordon\desktop\stringtest\stringtest\stringtest.cpp(7) : error C2039: 'memset' : is not a member of '`global namespace''

1>c:\users\gordon\desktop\stringtest\stringtest\stringtest.cpp(7) : error C3861: 'memset': identifier not found

1>c:\users\gordon\desktop\stringtest\stringtest\stringtest.cpp(8) : error C2039: 'memset' : is not a member of '`global namespace''

1>c:\users\gordon\desktop\stringtest\stringtest\stringtest.cpp(8) : error C3861: 'memset': identifier not found

대충 보니 식별자를 찾을수 없다. , 그게 뭐냐 는 식인데 이건 함수가 어느 헤더파일 안에 들어있는지 확인해 봐야겠군요. 가장 처음 MSDN좌표에 따르면, memset() <memory.h>안에 wmemset() <wchar.h>안에 등록되 있습니다. 그럼 해더를 추가해서 찍고 확인해보세요.

아래와 같은 코드는 잘 컴파일 되네요.

#include <memory.h>

#include <wchar.h>

 

int main()

{

        char TempA[100] ;

        char TempB[50] ;

        wchar_t wTempA[100] ;

 

        ::memset(TempA, 0, 100);

        ::memset(TempB, 0, 50);

 

        ::wmemset(wTempA, 0, 100);

 

        return 0 ;

}

이런식으로 사용하시면 쓰레기 값이 들어간 채로 두는 것이 아니라 실제 메모리가 초기화 되어있는 상태에서 사용할 수 있겠죠? 그럼 다른 초기화 방법인 ZeroMemory()에 대해 알아봅니다.

 

 

- ZeroMemory() 메크로

이 함수는 제가 주로 사용하는 함수인데요. 위의 코드와 다른점은 간단히 출처입니다. Memset의 경우는 memory.h, wchar.h 라는 해더에 선언되어 있지만, ZeroMemory()winbase.h라는 곳에 정의되어 있습니다. 이는 리눅스나 유닉스 등 다른 운영체제에서 호완되지 않는 windows 운영체제에 국한되는 경우인데요. 호완성면에서는 다소 결여되지만, 어떤경우에는 이 코드는 windows운영체제에 맞게 제작되었다 라는 것을 명시할 수 있는 케이스 입니다. 이 함수도 MSDN 좌표부터 시작하죠. Link: http://msdn.microsoft.com/en-us/library/aa366920(VS.85).aspx

초기화7.JPG

이 함수는 메모리 블록을 0들로 채운다라고 설명되어 있네요. 두번째 줄 설명인 일단 패스하죠.

 

첫번째 파라메터는 memset과 동일하게 목적지 포인터가 되겠고, 두번째는 SIZE_T Length라고 되어있는데 설명을 보면, “the size of the block of memory to fill with zeros, in bytes”라고 적혀있습니다. 제가 하고싶은 말이 대충 뭔지 알겠죠? 이녀석은 바로 바이트 단위의초기화를 시킵니다. wchar_t 배열의 경우를 보면,

#include <wchar.h>

#include <windows.h>

 

int main()

{

        wchar_t wTempA[100] ;

        ::ZeroMemory(wTempA, 100);

 

        return 0 ;

}

이런식의 순진한 코딩을 하시면 곤란하단 거죠.. ? 문제점이 뭘까요?

 

메모리를 들여다 보죠.

초기화8.JPG

.. 정말 100바이트만큼만 초기화 합니다. “wchar_t” 2바이트니까.. 100 = 2*x x=50이 되는거죠. 50개의 “wchar_t”블록만 초기화 됐습니다. , ZeroMemory()를 사용하려면 이경우 바이트단위 200을 넣어야 하겠죠.

 

ZeroMemory() 함수는 windows.h헤더를 포함하시면 사용할수 있습니다.(winbase.h를 직접 포함하시면 컴파일러가 에러를 뱉어낼껍니다. 이 부분에 관한 자세한 내용이 궁금하시면 구글링 고고싱.)

 

 

, 조금 클래버 한 방법으로 코딩할 차례군요. 보통 시간이 좀 지나면 코드에서 숫자를 찾아보기 힘든데요. (왜냐면 그게 편하니까요) 위의 방법보단 조금 똑똑한 방법의 예제입니다.

 

Memset

 

#include <memory.h>

#include <wchar.h>

 

int main()

{

        const int TempLength = 100 ;

 

        wchar_t wTempA[TempLength] ;

        ::wmemset(wTempA, 0, TempLength);

 

        return 0 ;

}

 

ZeroMemory

 

#include <wchar.h>

#include <windows.h>

 

int main()

{

        wchar_t wTempA[100] ;

        ::ZeroMemory(wTempA, sizeof(wTempA));

 

        return 0 ;

}

저는 이런식으로 사용합니다. 그러나 ZeroMemory의 경우 wTempA가 아니라 포인터라면 어떨까요?

 

#include <wchar.h>

#include <windows.h>

 

int main()

{

        wchar_t wTempA[100] ;

        wchar_t* pTemp = wTempA ;

        ::ZeroMemory(wTempA, sizeof(pTemp));

 

        return 0 ;

}

이런 경우는 wTempA 200바이트 메모리 중에 4바이트만 초기화됩니다. (모든 타입의 포인터는 4바이트입니다) 이 경우를 주의해서 작성해야합니다. 혹은 memset()의 경우처럼 코딩하면,

 

#include <wchar.h>

#include <windows.h>

 

int main()

{

        const int TempLength = 100 ;

        wchar_t wTempA[TempLength] ;

        ::ZeroMemory(wTempA, TempLength*sizeof(wchar_t));

 

        return 0 ;

}

뭐 이런식의 코드가 됩니다.

 

 

오늘 문자열 초기화에 대해 알아보았는데, memset()이나 wmemset()은 문자열 배열에 특화된 경우가 될것이고, ZeroMemory()의 경우는 문자열 뿐만 아니라 구조체나 다른 변수들에도 사용되는 범용적인 함수입니다.

 

앞으로 계속 추가될 문자열 관련 포스팅은 size, length의 차이점을 주로 두고 할 예정입니다. 헤더, 파라메터, 리턴타입의 경우는 각 함수당 MSDN 좌표를 따라 주세요.

 

그럼 첫번째 문자열 관련 포스팅을 마치고 전 이만 자러..

 

 

  

Ps. 글을 보시고 참고하시다가 혹여 제가 잘못 알고있는 부분이 있다면댓글로 적어주시기 바랍니다. 초보인 저에게는 많은 도움이 됩니다

'old_Coding > C/C++' 카테고리의 다른 글

[effective C++] 7장. 템플릿과 일반화 프로그래밍-41, 42  (3) 2011.08.08
[STL::Map] Error occured(*)  (0) 2010.03.18
[C] File I/O  (0) 2010.03.11
[Scrap] Struct SYSTEM_INFO (*)  (0) 2010.01.04
[C++] Function Pointer(3) (Solution)  (0) 2009.12.27