본문 바로가기

it관련

C언어로 C++ string클래스 따라하기

반응형

C언어로 C++ string클래스 따라하기



 

 우선 문자열을 만드는 CreateStringObject함수 입니다.

[code]

// CreateStringObject

char *CreateStringObject(const char String[])

{

 unsigned int Length = GetLengthStringObject(String);

 char *Data = (char*)malloc(1);

 unsigned int x = 0;

 Data[0] = 0;

 for(x = 0; x < Length; x++)

 {

  PushStringObject(&Data, String[x], x);

 }

 return Data;

}

[/code]

 우선 Data 포인터 변수에 크기 1인 문자열을 하나 만들어주고, 그 문자열에 오직 널 문자만을 저장합니다. 그다음 for문을 이용하여 Data에 차례대로 문자열 정보를 Push해 주는 것입니다.

 이 함수에서는 문자열의 길이(널 문자 제외)를 구하는 GetLengthStringObject함수와 PushStringObject함수가 내부적으로 사용되었습니다. 이제 이 두 함수의 정의를 봅시다.

 

[code]

// GetLengthStringObject

unsigned int GetLengthStringObject(const char *Object)

{

 unsigned int Length = 0;

 while(1)

 {

  if(Object[Length] == 0)

  {

   break;

  }

  else

  {

   Length++;

  }

 }

 return Length;

}

[/code]

 문자열의 길이를 구하는 함수는 별로 어렵지 않게 구현할 수 있습니다. 문자열은 모두 마지막에 널 문자(\0 = 0)을 가지기 때문입니다. 널 문자가 생기기 전까지 카운트를 올려주다가 널 문자를 발견하면 카운트를 증가시키지 않고 종료하면 됩니다. 그렇게 되면 널 문자를 제외한 문자열의 길이를 구할 수 있습니다.

 

[code]

// PushStringObject

void PushStringObject(char *Object[], char Data, unsigned int Index)

{

 char PushData[2];

 int x = Index;

 unsigned int StrLen = GetLengthStringObject(*Object);

 if(Index > StrLen+1)

 {

  return;  // 배열의 범위를 벗어날 수 없음.

 }

 *Object = (char*)realloc(*Object, StrLen+2);

 PushData[0] = (*Object)[x];

 for(x = Index; x < StrLen; x++)

 {

  PushData[1] = (*Object)[x+1];

  (*Object)[x+1] = PushData[0];

  PushData[0] = PushData[1];

 }

 (*Object)[StrLen+1] = 0;

 (*Object)[Index] = Data;

 return;

}

[/code]

 이 함수는 CreateStringObject로 만든 문자열 내부에 Data를 Index에 삽입한 뒤, Index 뒤의 문자열들을 한칸 뒤로 미는 역할을 합니다. 그래서 Push(밀다)인 것이죠.

 보시면 인자로 char**(문자 더블 포인터)형을 받아들이고 있는데, 이는 realloc이 공간이 적절치 않을 경우 주솟값을 변경할 가능성이 있기 때문입니다. char*형을 매개변수로 선언하게 되면 결국 1차 주솟값을 가지는 매개 변수라서 주솟값이 바뀌게 되어도 대상의 주솟값은 바뀌지 않습니다. 따라서 이 1차 주솟값을 가지는 변수를 가리키는 더블 포인터를 매개 변수로 두는 것입니다.

 한칸 미는 동작은 별로 어렵지 않습니다. 우선 for문을 사용하여 Index부터 문자열의 길이까지 반복하도록 했습니다. 널 문자는 마지막에 추가하기 때문에 문자열의 길이 까지만 반복하면 되는 것입니다.

 PushData는 각각의 문자를 임시로 저장하는 공간으로 Index+1의 문자를 PushData[1]에 저장하고 Index+1의 문자를 PushData[0]으로 바꿉니다. 여기서 PushData[0]은 Index의 문자가 들어있게 됩니다.

 

 이 PushStringObject함수를 조금만 응용하면 문자열 정보도 Push할 수 있습니다. 응용하지 않아도 단지 문자열의 길이만큼 반복문을 돌려 Push해 주시면 문자열을 넣을 수 있습니다. CreateStringObject에서 하는 것처럼 말입니다.

 

 아래 함수, AppendStringObject는 CreateStringObject로 만들어진 문자열의 마지막에 문자열을 덧붙이는 기능을 합니다. 위의 응용 정보(문자열의 길이만큼 반복문을 돌려 Push해준다.)를 토대로 만들어낸 함수 입니다.

[code]

// AppendStringObject

void AppendStringObject(char *Object[], const char *String)

{

 unsigned int Index = GetLengthStringObject(*Object);

 unsigned int StrLen = GetLengthStringObject(String);

 int x = 0;

 while(x < StrLen)

 {

  PushStringObject(Object, String[x], Index+x);

  x++;

 }

 return;

}

[/code]

 위 함수에서 보이듯이 PushStringObject를 문자열의 길이에 맞게 여러번 호출해 주면 얼마든지 문자열을 Push할 수 있습니다.

 

 문자열을 추가하는 Push가 있으면 문자열을 지워주는 Erase도 있어야 할 것입니다. 이 EraseStringObject함수는 Push함수와 똑같이 전달된 Index의 문자를 지웁니다. 그리고 그 뒤의 문자열을 모두 앞으로 당깁니다. 널 문자 까지도요. 그리고 realloc함수의 호출을 통해 할당 길이를 딱 맞춥니다.

[code]

// EraseStringObject

void EraseStringObject(char *Object[], unsigned int Index)

{

 int x = Index;

 unsigned int StrLen = GetLengthStringObject(*Object);

 if(Index > StrLen+1)

 {

  return;  // 배열의 범위를 벗어날 수 없음.

 }

 for(x = Index; x < StrLen; x++)

 {

  (*Object)[x] = (*Object)[x+1];

 }

 (*Object)[StrLen] = 0;

 *Object = (char*)realloc(*Object, StrLen);

 return;

}

[/code]

 역시 조금만 응용하면 Clean과 같은 함수도 만들 수 있습니다. 다음은 그 Clean함수를 만들어 본 예제입니다.

[code]

// CleanStringObject

void CleanStringObject(char *Object[])

{

 while((*Object)[0] != 0)

 {

  EraseStringObject(Object, 0);

 }

 return;

}

[/code]

 위 함수는 정말 간단합니다. 그저 문자열의 첫번째를 계속 지우다가 첫번째가 널 문자가 되면(문자열이 텅 비면) 종료합니다.

 

 

 CreateStringObject로 만든 문자열은 동적 할당으로 만들어진 문자열이기 때문에 반드시 해제해 주어야 합니다. 그렇다고 해제 코드를 free함수를 이용해 사용자들이 직접 쓰게 할 수는 없습니다. 상당히 부자연 스럽기 때문이죠.

 비록 하는 일이라곤 free함수의 호출 뿐이지만 이것 역시 우리가 정의해 주어야 합니다. 유저들은 이 문자열을 사용 완료한 뒤 제거하기 위해서 어떤 동작을 해야하는지 알 수 없기 때문이죠.

[code]

// DestroyStringObject

void DestroyStringObject(char *Object)

{

 free(Object);

 return;

}

[/code]

 아까도 말했듯이 하는 일은 free함수의 호출 뿐입니다. 하지만, 유저들은 문자열을 제거하기 위해 어떤 행동을 취해야 하는지 알 수 없으므로 이렇게 알기 쉽게 StringObject라는 접미어를 단 Destroy함수를 만들어 주어 사용할 수 있도록 하는 것입니다.

 

 

 마지막으로 이 함수들의 사용 예를 보이겠습니다.

[code]

// main

int main(void)

{

 char *Data = CreateStringObject("MyStringObject");

 puts(Data);

 PushStringObject(&Data, '1', 0);

 puts(Data);

 PushStringObject(&Data, '5', 5);

 puts(Data);

 EraseStringObject(&Data, 4);

 puts(Data);

 CleanStringObject(&Data);

 puts(Data);

 DestroyStringObject(Data);

 return 0;

}

[/code]

 실행 결과는 다음과 같습니다:

MyStringObject

1MyStringObject

1MySt5ringObject

1MyS5ringObject

// 빈 공간

 

 위에서 나온 함수들은 응용의 여지가 매우 많습니다. 위 함수들은 어디까지나 문자열 동적 할당의 기초를 함수화 시켜놓은 것들 뿐이며, 지금부터는 이 글을 보고있는 여러분들이 위 함수들을 응용하여 자신만의 것으로 만들어나가면 됩니다.

반응형

'it관련' 카테고리의 다른 글

인터넷전화 서비스 방식  (0) 2017.05.27
인텔 샌디브릿지,아이비브릿지  (0) 2017.05.22
float double 비교  (0) 2017.05.22
망중립성  (0) 2017.05.21
연결 리스트 맛보기  (0) 2017.05.21