blog
October 10, 2021
한 문자열을 다른 문자열 뒤에 복사해 붙이는 코드가 필요하다고 하자.
이를 라이브러리 함수 없이 구현한다면 아래와 같을 것이다.
(이 때, 문제에 집중하기 위해 target_buf
은 문자열 source_str
을 뒤에 붙이기(append) 에 충분한 메모리 공간을 할당받았다고 하자)
void append_str(char *target_buf, const char *source_str)
{
while (*target_buf != '\0')
++source_str;
while ((*target_buf++ = *source_str++) != '\0')
;
}
위의 구현에서는 while 문을 2 번 사용했지만, 사실 전체적으로 보면 target_buf
을 끝날 때까지 반복하고 있다.
하지만 이 경우는 작업의 순서가 정해져 있기 때문에 굳이 반복문 한 번으로 코드를 복잡하게 만들 필요 없이, 반복문을 2 번 사용하는 것으로 쉽게 구현이 가능하다.
더 생각해볼 만한 부분이 있다면, ‘가독성을 높이기 위해 두 번째 while 문을 조금 더 길게 쓸 것인가’ 와 ‘마지막 null character(‘\0’) 는 어디서 복사되는가?’ 일 것이다.
위의 방법은 while 의 조건문에서 값을 복사하기 때문에 마지막에 ‘\0’ 를 복사한 후 조건문을 검사한다. 그래서 추가적으로 ‘\0’ 를 마지막에 넣어 줄 필요가 없다. 그런데 만약 2 번째 while 문을 다음과 같이 작성한다면,
while ((*target_buf++ = *source_str++) != '\0')
;
|
V
while (*source_str != '\0')
*target_buf++ = *source_str++;
*target_buf = '\0';
값 복사를 while 문의 조건문(condition) 위치가 아닌 몸통(body)에서 진행하기 때문에, source_str
의 마지막 ‘\0’ 는 복사되지 않고 while 문을 빠져나오게 된다. 그래서 target_buf
의 마지막 ‘\0’ 는 직접 넣어 주어야 한다.
이번엔 조금 더 들어가 본다.
반복되는 와중에 처리 방식이 그때 그때 달라질 수 있는 코드를 보자.
예를 들어, source_str
을 target_buf
에 복사하는데, 만약 문자가 소문자면 대문자로 바꿔준다고 하자.
그럼 아래와 같이 어렵지 않게 구현될 것이다.
int is_lowercase(char ch)
{
return ('a' <= ch && ch <= 'z');
}
void strcpy_lower_to_upper(char *target_buf, const char *source_str)
{
while (*source_str != '\0')
{
if (is_lowercase(*source_str))
*target_buf++ = 'A' + (*source_str++ - 'a');
else
*target_buf++ = *source_str++;
}
*target_buf = '\0';
}
위의 while 문은 문자열 source_str
을 처음부터 끝까지 순차적으로 접근할 것이다.
그리고 매 상황(대문자인지 소문자인지) 에 따라 첫 번째 동작 이나 두 번째 동작을 수행한다.
이 때 각 문자는 직접 *source_str
값을 읽기 전까지는, 이전까지의 상황만으로 대문자일지 소문자일지 알 수 없다.
때문에 한 while 문 안에서, 상황에 따라 원하는 동작을 수행하도록 코드를 작성할 수밖에 없다.
이번 예시 역시 두 문자열 주소 source_str
과 target_buf
가 있다.
source_str
의 문자들을 하나씩 접근하면서, 만약 문자가 알파벳이었다면 알파벳이 아닐 때까지 문자를 target_buf
으로 복사해 온다.
그리고 복사해 온 단어 사이에는 space character 를 하나 추가한다.
int is_alpha(char ch)
{
return (('a' <= ch && ch <= 'z') || ('A' <= ch && ch <= 'Z'));
}
void cpy_all_word(char *target_buf, const char *source_str)
{
while (*source_str != '\0')
if (is_alpha(*source_str))
{
while (is_alpha(*source_str))
*target_buf++ = *source_str++;
*target_buf++ = ' ';
}
*target_buf = '\0';
}
제일 안쪽의 while 은 do while 을 사용하는게 더 효율적일 수 있겠지만 그 부분은 넘어가기로 하겠다. 더불어 문제에 집중하기 위해 마지막 단어 뒤에 ‘ ‘ 가 불필요하게 추가되는 것은 무시하도록 하겠다.
완성된 코드를 보면 복잡해보였던 설명에 비해 꽤나 간소하게 느껴진다.
2 중 while 문으로 구현되서 생각보다 짧은 코드로 구현이 되었다.
그런데 내가 오늘 말하고자 했던 핵심은 사실 바로 이 다음이다.
여기서 만약 안쪽의 while 부분을 따로 떼서 함수로
구현하고 싶다면 어떻게 될까?
문자열에서 알파벳이 아닌 문자가 나올 때까지, 연달은 알파벳을 다른 위치로 복사하는 기능은 함수로 구현해 놓는다면 어쩌면 다른 곳에서 또 쓰일지도 모른다.
그래서 한 단어를 복사하는 함수를 따로 작성해놓고, 우리의cpy_all_word()
가 그 함수를 호출해서 사용하는 것은 좋은 생각인 것 같다.
하지만 글이 길어지고 있으니 이번 포스트는 여기까지 하고 다음 포스트에 이어서 진행해 보도록 하겠다.