Libft

slee2·2022년 5월 23일
0

42Seoul

목록 보기
1/15
post-thumbnail

Reference

자주 사용하게 되는 메서드들을 구현하는 프로젝트이다.
이후에 진행할 프로젝트들에서 실제 메서드들을 사용할 수 없으니
이번 프로젝트에서 구현한 메서드들을 사용한다는 느낌으로 진행하였다.


Mandatory

메서드설명
int ft_isalpha(int c);c가 알파벳이 아니면 0, 대문자면 1, 소문자면 2 반환
int ft_isdigit(int c);c가 숫자가 아니면 0, 숫자면 0이 아닌 숫자 반환
int ft_isalnum(int c);c가 알파벳과 숫자 모두 아니면 0, 알파벳 또는 숫자면 0이 아닌 수 반환
int ft_isascii(int c);c가 아스키 코드면 1, 아니면 0 반환
int ft_isprint(int c);c가 출력할 수 없는 값이면 0, 출력할 수 있는 문자면 0이 아닌 수 반환

이 메서드들은 설명을 간단히 할 수 있으므로 표로 만들었다.

strlen

size_t	ft_strlen(const char *str);
  • 매개변수
    • const char *str 문자열
  • 반환값
    • 문자열의 길이

문자열의 길이를 반환하는 메서드이다.

memset

void	*ft_memset(void *ptr, int value, size_t num)
  • 매개변수
    • void *ptr 세팅하고자 하는 메모리의 시작 주소
    • int value 메모리에 세팅하고자 하는 값
    • size_t num 메모리의 크기(sizeof(int) * length)
  • 반환
    • 실패하면 NULL
    • 성공하면 ptr 주소

value 값을 num 길이만큼 ptr에 넣어주는 메서드입니다.

bzero

void	ft_bzero(void *ptr, size_t size);
  • 매개변수
    • void *ptr 메모리 주소
    • size 0으로 채울 길이
  • 반환없음

ptr을 처음부터 size만큼 0으로 채워주는 메서드.

memcpy

void	*ft_memcpy(void *dest, const void *src, size_t num)
  • 매개변수
    • void *dest 복사 받을 메모리
    • const void *src 복사할 메모리
    • size_t num 복사 길이
  • 반환
    • 성공시 *dest
    • 실패시 NULL

원본(src)을 길이(num)만큼 dest에 복사하는 메서드.
주의점 : 마지막에 \0는 따로 넣어줘야한다. 이로 인해 길이도 +1을 해줘야한다.

memmove

void	*ft_memmove(void *dest, const void *src, size_t num);
  • 매개변수
    • void *dest 복사 받을 메모리
    • const void *source 복사할 메모리
    • size_t num 복사 길이
  • 반환
    • 성공시 *dest
    • 실패시 NULL

memcpy와 매개변수와 반환이 똑같지만 내부 로직이 다르다.
memcpy는 속도가 빠르지만 안전하지 않고,
memmove는 속도가 느리지만 안정성이 보장되어 있다.

안전하지 않다는 것이 어떤 의미인지 설명해보겠다.
memcpy를 사용할 경우 맨 앞에서부터 복사를 하기 시작하므로,

s r c
     d e s t

이런식으로 복사한 결과값이 될 dest의 메모리가 src의 중간에 있다면 어떻게 될까?
만약에 src12345라고 하고 dest의 첫번째 메모리가 src3부터 있다고 가정해보자.

1 2 3 4 5
       1 2 1 2 1

dest는 이와 같이 될 것이다. 왜 이렇게 되는지 알겠는가?
이는 맨 앞에서부터 복사를 하기 시작하므로 생겨난 일인데, 처음에 src12까지는 dest가 똑같이 12를 복사하게 된다.
그런데 src의 세번째 인덱스값 3dest의 첫번째가 덮어버려서 3이 아니라 1이 들어있게 된다. 이 숫자 1을 복사하게 되니, dest의 세번째 인덱스값은 1이 되는 것이다.
이와 같이 복사를 계속하게 되면 12121이 나오게 된다.

하지만, memmove는 이 경우에도 dest12345로 만들어준다.
어떤 로직을 사용해야 할까?

나는 이를 구현할때
src의 주소값이 dest보다 앞에 있다면 뒤부터 값을 복사한다.
src의 주소값이 dest보다 뒤에 있다면 앞부터 값을 복사한다.
이 방법을 사용하였다.

이렇게 된다면 src의 값은 수정되겠지만, dest 값은 변하지 않고 처음 src 값이 들어가게 된다.

strlcpy

size_t	ft_strlcpy(char *dest, const char *src, size_t size);
  • 매개변수
    • void *dest 복사 받을 메모리
    • const void *source 복사할 메모리
    • size_t size 복사 길이
  • 반환
    • dest 길이

sizesrc의 길이 이하일때는 size - 1만큼 복사(size 길이에 \0포함)
이외는 srcdest로 복사

strlcat

size_t	ft_strlcat(char *dst, const char *src, size_t size)
  • 매개변수
    • void *dest 기본 메모리
    • const void *source dest뒤에 붙일 메모리
    • size_t size 총 길이
  • 반환
    • 마지막 dest 길이

3가지 경우로 나뉘게 된다.

  • sizedest길이보다 작은 경우
    • 아무 작업을 하지 않고 dest길이 반환
  • sizedest길이보다 크고 dest + src 길이 이하인 경우
    • dest길이가 size - 1이 될 때까지 dest뒤에 src를 붙이고 최종 dest 길이 반환
  • sizedest + src 길이보다 큰 경우
    • dest 뒤에 src를 전부 붙인 후, 최종 dest 길이 반환

strncmp

int	ft_strncmp(const char *s1, const char *s2, size_t n)
  • 매개변수
    • const char *s1 비교할 문자열1
    • const char *s2 비교할 문자열2
    • size_t n 비교할 길이
  • 반환
    • 비교값이 같으면 0
    • s1이 크면 양수
    • s2가 크면 음수

비교할 길이(n)만큼 각 문자열의 아스키 코드를 비교한다.
더 큰 쪽이 있다면 어느쪽이 큰지 비교 후 반환한다.
ex) s1, s2, n

  • abcd, abce, 10 -> -1
  • abcd, abce, 2 -> 0
  • abce, abcd, 10 -> 1

calloc

void	*ft_calloc(size_t nmemb, size_t size)
  • 매개변수
    • size_t nmemb 동적할당 길이
    • size_t size 동적할당 이후 0으로 채울 길이
  • 반환
    • 동적할당해준 변수

malloc과의 차이점
말록은 매개변수가 '동적할당 길이' 하나이다. 그리고 안에는 쓰레기값으로 채워져있다.
칼록의 경우 두번째 매개변수(size)만큼 0으로 채워준다.

strdup

char	*ft_strdup(const char *s)
  • 매개변수
    • const char *s 새롭게 동적할당 해줄 문자열
  • 반환
    • 새롭게 동적할당 해준 문자열

문자열 s의 길이만큼 새롭게 동적할당을 한다.
거기에 s 문자열을 복사한다.
그걸 반환한다.

substr

char	*ft_substr(char const *s, unsigned int start, size_t len)
  • 매개변수
    • const char *s 자르려는 문자열
    • unsigned int start 자르려는 시작점
    • size_t len 자르려는 길이
  • 반환
    • 성공시 새로 동적할당된 문자열
    • 동적할당 실패시 NULL
    • 특수한 경우에 빈 문자열 동적할당

빈 문자열을 동적할당하여 반환하는 경우

  • s의 길이가 start보다 작을때
  • len0일 때
  • s가 NULL일 때

마지막 s가 NULL일때 그냥 NULL 반환하는 경우도 있다. 여러 이야기가 있기 때문에 확실하지 않는 정보이다.

이외에는 start 지점부터 len 길이만큼 문자열 s의 내용을 새로운 동적할당에 복사한다. 그리고 이 문자열을 반환한다.

strjoin

char	*ft_strjoin(char const *s1, char const *s2)
  • 매개변수
    • const char *s1 문자열1
    • const char *s2 문자열2
  • 반환
    • 두 문자열을 합친 새로운 동적할당 변수

두 문자열 길이 합만큼 새로 동적할당을 한다.
s1 + s2로 새로 값을 넣어준다.
해당 문자열을 반환한다.

strtrim

char	*ft_strtrim(char const *s1, char const *set)
  • 매개변수
    • char const *s1 양쪽을 잘라낼 원본 문자열
    • char const *set 제거할 문자열
  • 반환
    • s1set으로 제거한 새로운 문자열

s1의 양쪽에 set이 있다면 각각 제거해준다.
이를 malloc을 이용해 새롭게 동적할당을 한 뒤에 반환해준다.

split

char	**ft_split(char *s, char c)
  • 매개변수
    • char *s 원본 문자열
    • char c 문자열을 나누는 기준
  • 반환
    • s 문자열을 c로 나눈 이중 문자열을 반환

문자열 sc 기준으로 나눈다.
abcdce라는 문자열을 c라는 문자열로 나눈다면,

ab
d
e

이렇게 이중 배열을 새롭게 동적할당해주고 반환한다.

42에서는 이 split이 중요한 메서드로 불리기도 하는데, 동적할당을 중간에 실패했을 시에 어떻게 프로그램을 완성할 것인가가 중요한 메서드였다.
쉽게 말해 처음에 2중 말록을 통해 동적할당을 해준 후, 배열을 돌며 각 문자열을 동적할당하는 방식이 되는데
동적할당을 실패하게 되면, 보통 NULL을 반환하게 되지만, split 메서드의 경우 동적할당을 계속 만들어주므로 중간에 동적할당을 실패하게 되면 이전까지 할당해줬던 값들을 free를 통해 해제해준 다음에 NULL을 반환해줘야한다.
메서드가 실패했을 경우, 예외 처리를 완벽히 해줘야 하는 것이다.

이 문제에 대해 여러 이야기가 오갔는데, 만약 NULL 반환이 아니라 해당 이중배열을 반환하게 되면 메모리 누수가 발생하지 않는다. 변수로 반환된 값을 받기 때문이다.
하지만, 할당 실패의 경우 NULL을 반환하게 되므로, 이전의 동적할당 값들이 split이라는 메서드 안에서 남겨지게 된다. 이 값들을 찾을 방도가 없는 것이다. 그렇기 때문에 메모리 누수가 발생하게 되므로, 동적할당을 해제하고 NULL을 반환해주는 것이 맞다.

이것이 첫 프로젝트의 매운맛 버전이였다. 메서드의 동작 설명 뿐만이 아니라 이렇게 만들게 된 이유를 정확히 설명하는 것이 이 프로젝트의 목적이다.

0개의 댓글