[C] strlen / strcpy / strncpy / strlcpy 사용법과 구현
본문 바로가기

C

[C] strlen / strcpy / strncpy / strlcpy 사용법과 구현

728x90
반응형

가끔 C언어를 하다보면 문자열 길이가 궁금할 때도 있고 문자열을 복사시키고 싶을 때가 있습니다.

그럴 때 보통 string.h 헤더파일에 있는 함수들을 사용하죠

 

저희가 이번 포스트에서 볼 함수들은 문자열 길이를 구해주는 strlen 함수와

문자열을 복사시켜주는 strcpy 계열의 함수!

 

strlen 함수 먼저 보시죠

 

 

strlen

#include <string.h>

size_t	strlen(const char *s)


#include <stdio.h>

int main(void)
{
    char str[13] = "hello world!";
    printf("%zu\n", strlen(str));      // 12
}

매개변수로 들어오는 s 문자열의 길이를 size_t 자료형으로 반환해 주는 함수입니다.

 

예를 들어 "hello world!" 이런식으로 문자가 들어오면 "hello", " ", "world!" 이런식으로 분류를 할 수 있겠는데요

그럼 문자의 길이는 총 12이고 이 12을 반환해 주는 함수라고 보시면 되겠습니다.

 

strlen 구현

size_t	ft_strlen(const char *s)
{
	size_t	i;
    
    i = 0;
    while (s[i] != '\0')
    	i++;
    return (i);
}

strlen 함수는 구현하기 굉장히 쉬운편에 속하는 데요

단순히 문자열의 끝이 NUL만 확실하다면 while 문으로 반복시켜주기만 하면 되기 때문입니다.

 

while문에서의 (s[ i ] != '\0')  이 조건은 "문자열의 끝일 때 까지 실행해!" 라는 말과 같은 의미입니다.

아스키 코드상 '\0'은 숫자 0을 의미하고 조건문은 항상 참(1) 일때 실행, 거짓 (0) 일때 종료 하기 때문에

조건문을 사실상 s[ i ] 만 적어도 상관은 없죠

 

strcpy  

#include <string.h>

char	*strcpy(char *dst, const char *src)


#include <stdio.h>

int main(void)
{
    char s1[6] = "hello";
    char s2[6] = "world";
    
    strcpy(s1, s2);
    printf("%s\n", s1); // "world" 출력
}

src 문자열을 dst에 복사한 후, 복사한 dst를 반환하는 함수입니다.

보시면 s1문자열에 "hello", s2문자열에 "world" 가 담겨있습니다.

이 두개를 strcpy에 각각 넣어보면 s1은 s2와 같은 값인 "world" 문자열을 갖게 됩니다.

이런식으로 "왼쪽 문자열의 값을 오른쪽 문자열로 만들겠다" 라고 생각하면 편하시겠습니다.

 

strcpy 구현

char	*ft_strcpy(char *dst, const char *src)
{
    size_t	i;
    
    i = 0;
    while (src[i])
    {
    	dst[i] = src[i];
        i++;
    }
    dst[i] = '\0';
    return (dst);
}

구현 난이도는 굉장히 쉬운편 입니다.

 

하지만 strcpy에는 치명적인 단점이 있습니다.

dst의 문자열의 크기가 src 문자열의 크기보다 작다면

문자열의 끝인  '\0' 값이 들어가지 않기 때문입니다.

 

이런경우 dst를 직접 출력해보면 오류가 나는것을 알 수 있습니다.

 

"그렇다면 어떻게 해야 되나요??"

이를 보완하기 위해 사용자가 "src 문자열 n개만 dst에 복사해줘" 하는 함수가 나왔죠

바로 strncpy 입니다.

 

strncpy

#include <string.h>

char	*strncpy(char *dst, const char *src, size_t len)


#include <stdio.h>

int main(void)
{
    char s1[3] = "he";
    char s2[6] = "world";
    
    strncpy(s1, s2, 2);
    printf("%s\n", s1);    // "wo"
}

src 문자열을 n개만 dst에 복사시키는 strncpy 함수입니다.

main문을 보시면 strcncpy의 세번 째 인자로 2가 들어갔고 그럼 이 2는 함수에서 len으로 적용되서

len번 dst가 복사됩니다.

 

strcpy같은 경우였으면 복사된 s1의 문자열 값은 "wo" 까지는 복사가 됐어도 이후 rld를 받을 공간이 없어 오류가 날겁니다.

이 점을 보완한게 strncpy 라고 보시면 되겠습니다.

 

strncpy 구현

char	*ft_strncpy(char *dst, const char *src, size_t len)
{
    size_t	i;
    
    i = 0;
    while (src[i] && i < len)
    {
    	dst[i] = src[i];
        i++;
    }
    dst[i] = '\0';
    return (dst);
}

src가 '\0'이 아니면서 len번 만큼 dst에 복사를 해주게끔 구현하면 됩니다.

문제는 이 strncpy에도 치명적인 단점이 있다는거 아시나요?

 

바로 문자열의 끝을 알리는 '\0'을 확실하게 복사하지 않는다는 겁니다.

예시를 보여드리죠

#include <string.h>
#include <stdio.h>

int main(void)
{
    char s1[4] = "hel";
    char s2[6] = "world";
    strncpy(s1, s2, 4);
    printf("%s\n", s1);		//worl�I���
}

이런식으로 복사되는 s1문자열의 크기가 len보다 크지 않고, s2가 len 보다 큰 경우 s1은 worl까지 복사후 '\0'값이 없어

이후 쓰레기 값을 가지게 됩니다.

 

그래서 이를 보완하고자 나온 함수가 바로 strlcpy입니다.

 

strlcpy

#include <string.h>

size_t	strlcpy(char *restrict dst, const char *restrict src, size_t dstsize)


#include <stdio.h>
int main(void)
{
    char	s1[4] = "hel";
    char	s2[6] = "world";
    strlcpy(s1, s2, strlen(s1));
    printf("%s\n", s1); // "wo"
}

마지막에 '\0'값을 보장해주는 strlcpy 함수입니다.

dstsize - 1 만큼 dst를 복사한뒤 src의 길이를 반환하는 함수입니다.

 

물론 이 함수도 dstsize가 dst문자열의 길이보다 큰 경우 '\0' 값을 보장 못하긴 합니다.

그래서 이번 strlcpy 구현 부분에선 조금 더 보완해서 실제 strlcpy가 아닌 보완된 strlcpy를 구현해 보겠습니다.

 

strlcpy 구현

#include <string.h>
#include <stdio.h>

size_t	ft_strlcpy(char *dst, const char *src, size_t dstsize)
{
    size_t	i;

    i = 0;
    while (src[i] && i + 1 < dstsize)
    {
    	dst[i] = src[i];
        i++;
    }
    dst[i] = '\0';
    return (strlen(src));
}

int main()
{
	char s1[4] = "hel";
	char s2[8] = "worldsl";
	printf("%zu\n", ft_strlcpy(s1, s2, 50)); // 3
	printf("%s\n", s1);                      // worldsl
	printf("%zu\n", strlcpy(s1, s2, 50));    // 에러
	printf("%s\n", s1);
}

아마 기존 strlcpy의 경우 while 조건문에서 src[ i ] 이 부분을 빼먹었을 겁니다.

그래서 이 부분만 수정하면 '\0' 값을 확실하게 보장해 줄 수 있죠

 

위 코드를 직접 실행해 보시면 실제 strlcpy에도 결함히 있다는걸 알 수 있습니다.

하지만 저흰 while에서 한 조건을 추가했기 때문에 오류가 발생하지 않게끔 구현을 할 수 있습니다.

 

dstsize의 크기가 실제 dst문자열의 크기보다 큰 경우 오류가 나는것으로 보이는데요

이 부분을 조건문 하나만으로 수정할 수 있다는게 다시 생각해보니 신기하네요

 

참고로 while 조건문에서 dstsize - 1이 아닌 i + 1로 조건을 거시는게 좋습니다.

size_t 이다 보니 dstsize가 0인경우 -1을 하게 되면 언더플로우 현상 때문에 음수가 아닌

size_t 자료형의 최대값으로 변해 원하지 않는 결과를 초래할 수 있기 때문입니다.

 

반응형

 

728x90
반응형