CS/C

strlcat 반환값에 대한 고찰

들어가며.
이 글은 42서울의 라피신 과정중 가장 고민했던 함수였던 strlcat에 대한 설명이고, 특히나 '반환값'에 대한 고찰이 담겨있습니다.
strlcat에 대한 설명은 man strlcat 이나 다른 블로그에도 잘 설명이 되어있습니다. 참고하시길 바랄게요.
중간중간 이해하기 어려워도 끝까지 정독하신 후 또 다시 읽어보시고 궁금한 점이나 틀린 부분은 댓글남겨주시면 감사하겠습니다.

 

 

왜 이 글을 쓰는지?

strlcat을 짜는 것은 어렵지 않았습니다.

하지만 제가 가장 어려웠던 것은 strlcat의 반환값을 왜 그렇게 짜야하는지, 반환값이 의미하는 바가 무엇인지가 가장 궁금했습니다.

그리고 공식문서와 동료들과의 토론, 제 고찰이 담긴 결론을 냈습니다.

이 글은 이 결론을 설명하는 글입니다.

 

 

 

 

strlcat

말 그대로 두 string을 연결한 길이를 반환하는 함수입니다.

 

하지만 strlcat은 항상 두 string이 연결된 정확한길이를 반환하지 않습니다.

 

* (strncat은 strlcat과 달리 origin에서 n만큼의 길이만 떼어내 dest에 이어붙입니다. 그리고 이어붙인 문자열 자체를 반환합니다.)

 

 

 

반환값의 정의

자 그럼, man strlcat에서 반환값에 대한 정의를 확실하게 확인해봅시다.

해석) "strlcat의 반환값은 dst에 str를 더한 길이를 의미합니다. 좀 혼란스러울수 있겠지만, '잘림방지'를 위해 간단히 수행되었습니다."

 

여기서 "잘림방지"에 주목해야합니다. 이 키워드를 머리속에 가지고 있고, strlcat의 구현코드를 한번 보시죠.

특히, 아래 return부분 코드에 주목해주세요.

 

아래는 제가 실제로 통과한 strlcat의 코드입니다.

unsigned int	ft_strlcat(char *dest, char *src, unsigned int size)
{
	unsigned int	p_dest;
	unsigned int	p_src;
	unsigned int	dest_len;
	unsigned int	src_len;

	dest_len = ft_strlen(dest);
	src_len = ft_strlen(src);
	p_dest = dest_len;
	p_src = 0;
	while (src[p_src] && (p_src + dest_len + 1) < size)
	{
		dest[p_dest] = src[p_src];
		p_dest++;
		p_src++;
	}
	dest[p_dest] = '\0';
	if (dest_len < size)
	{
		return src_len + dest_len;
	}
	else
	{
		return src_len + size;
	}
}

(피신과정이시면 제 코드를 그대로 사용하는 것 보다 이해한 뒤 본인의 손으로 직접만들어보세요.)

 

 

  • dest_len이 size(세번째 매개변수)보다 작을때 (src_len + dest_len) 을 반환
  • dest_len이 size(세번째 매개변수)보다 클때 (src_len + size)을 반환

 

의문점

의문점이 있었습니다.

  1. 공식문서에 나온 반환값의 의미는 dest_len + src_len 이니까 이 값을 그대로 반환 하면 될텐데 왜 저렇게 구분지어 반환을 해야할까?
  2. 특히 왜 src_len에 size를 더해야할까?

 

size

이 의문을 해결하기 위해서는 "size(세번째 매개변수)"의 의미를 알고있어야합니다.

 

두개의 string과 함께 전달되는 세번째 매개변수 size는 두 문자열이 합쳐질 공간의 크기를 의미합니다.

개발자는 strlcat를 이용할때 두 문자열과 함께 문자열이 합쳐질 공간의 길이를 미리 정해놓고 값을 넘겨주는 것이죠.

 

 

상황별 예시

상황1)

그런데 만약 문자열 dest의 길이가 size보다 크다면 어떻게 될까요?

쉽게 풀어쓴다면, strlcat은 정해진 길이의 공간(size)에 dest + src 를 해야하는 함수인데 이 공간이 dest조차도 담기지 않는 공간이면 어떻게 될까요?

dst_len = 5
size = 4 / dst전체가 담기지 않는 공간이므로 수행안함

"아무런 수행을 하지 않습니다."

 

 

상황2)

만약 문자열 dest의 길이가 size보다 작다면 어떻게 될까요? (dest_len < size)

dst_len = 5, src_len = 5 
src의 문자가 붙은 dst. size = 9

 

size에 dest는 무조건 다 들어갈 수 밖에 없고 이제 dest와 size의 차이만큼 중 '\0'자리(1개)를 뺀 만큼, 즉 (size-dest-1) 만큼의 src의 글자가 dest에 붙을 것입니다.

dest의 길이가 size보다 1만큼 더 작다면 그 자리는 '\0'자리이기 때문에 아무런 수행을 하지 않을 것이고,

2이상부터는 src의 문자열중 한개의 문자라도 dest뒤에 붙을 것입니다.

 

여기서, "한개의 문자라도 dest에 붙는다는 것"이 중요합니다.

 

dest_len < size 인 순간, dest는 src의 문자가 붙기 때문에(차이가 1인 경우는 편의상 제외하고) dest는 "오염"되기 시작합니다.

개발자에게 있어 문자열이 오염되어있는지, 아무런 수행을 하지 않았는지는 매우 "중요한 일"입니다.

그리고 공교롭게도 return을 구분할때 if (dest_len < size) 라는 조건이 들어가죠.

 

 

 

반환조건

다시한번 리턴값의 대한 공식문서의 설명을 보겠습니다.

해석) "strlcat의 반환값은 dst에 str를 더한 길이를 의미합니다. 좀 혼란스러울수 있겠지만, '잘림방지'를 위해 간단히 수행되었습니다."

 

여기서의 "잘림"는 제가 말한 dest의 "오염"을 의미합니다.

 

 

다시한번 반환조건을 보겠습니다.

... 생략
    if (dest_len < size)
	{
		return src_len + dest_len;
	}
    else
	{
		return src_len + size;
	}
... 생략
  • dest_len < size : dest는 다 들어가있고 src의 문자가 하나라도 옮겨지게 될 상황에서는 (src_len + dest_len) (실제 두 문자열이 잘림없이 정상적으로 합쳐지게 될 때의 길이)를 반환합니다.
  • dest_len > size : 아무런 수행도 하지 않는 상황에서는 src_len + size를 반환합니다.
  • dest_len == size : 어차피 dest_len과 size가 같기때문에 위 두 조건 어디에 속해있어도 값이 같습니다. 그래서 src_len + size를 반환해도 무방합니다.

 

 

 

반환값들의 의미

그렇다면 이제 이 반환값들의 "의미" 즉, 이 함수를 만든사람은 개발자가 이 반환값들을 어떻게 사용하라고 이렇게 정의를 해 놓은 것일까요?

(여기서부터는 제 개인적인 고찰이 담겨있습니다. 이것보다 더 좋은 결론을 찾을 수 없었습니다.)


개발자는 이제 아래의 코드처럼 이용하면 dest에 아무런 수행을 하지 않았는지, 아니면 한문자라도 옮겨갔는지 확인이 가능합니다.

...생략
    if (ft_strlcat(dst, src, 10) <= ft_strlen(src) + ft_strlen(dst))
	{
		// 아무런 수행을 하지 않음.
		// dst가 오염되어있지 않음.
	}
	else
	{
		// dst가 오염됨을 확인가능.
	}
...생략

 

ft_strlcat에 넘겨주는 인자만으로 dest가 어떤 상황인지 쉽게 확인이 가능하고 개발자들은 이에따른 예외처리도 쉽게 가능합니다.

 

아래의 조건이 가능한 이유는

ft_strlcat(dst, src, 10) <= ft_strlen(src) + ft_strlen(dst)

dest_len > size 일때 src_len + size를 반환한다면, 무조건 (src_len + size) < (src_len + dest_len)일 수 밖에 없기 때문입니다.

 

strlcat 함수 제작자는 strlcat에 넘겨주는 3개의 인자를 이용해 오염됨을 쉽게 판단하기 위해서는 이와같은 반환값조건을 선택해야했을 것입니다.

 

 

 

 

 

 

 

마치며.
"반환값들의 의미" 파트 전까지는 팩트를 기반으로 작성되었습니다. 하지만 반환값들의 의미 파트에는 제 생각이 들어가 있습니다.
"잘림방지"를 위해 반환값을 설정해놓았다는 공식문서의 내용을 바탕으로 개발자가 이 반환값을 어떻게 사용하면 좋을 지에 대해 고찰해 보았습니다.
다소 난해한 부분이 있을 수 있습니다. 한번 더 정독해보시고 궁금한 점이나 틀린 부분은 댓글로 남겨주세요.