---
description: >-
  libft 프로젝트의 part1 함수들을 구현하면서 메모했던 내용들을 정리해두었다. 이 라이브러리의 함수들은 꾸준히 업데이트 되고 있기
  때문에 가장 최신의 코드는 여기 깃허브 저장소를 참고...
---

# \[Libft] Part 1

libft 프로젝트의 part1 함수들을 구현하면서 메모했던 내용들을 정리해두었다. 이 라이브러리의 함수들은 꾸준히 업데이트 되고 있기 때문에 가장 최신의 코드는 [여기](https://github.com/hidaehyunlee/Libft) 깃허브 저장소를 참고...

## 1. **ft\_memset**

```c
    #include "libft.h"

    void        *ft_memset(void *dest, int c, size_t n)
    {
        unsigned char   *new_dest;
        unsigned char   src;
        size_t  i;

        new_dest = dest;
        src = c;
        i = 0;
        while(i++ < n)
            *new_dest++ = src;
        return (dest);
    }
```

**dest 위치부터, n개의 바이트 만큼, c 라는 값으로 초기화** 하는 함수.

*   이 때 초기화 할 값인 c는 인자로는 int로 전달되지만 함수 내부적으로는 `unsigned char`로 형변환 되어서 사용된다.

    **메모리에 접근할 때에는 signed char형이 아닌 unsigned char형을 사용해야하는 이유**

    unsigned char 는 모든 bit를 투명하게 볼 수 있는 특성을 제공합니다. 즉, 다른 type 은 내부 비트의 일부를 값을 표현하기 위한 용도가 아닌 다른 용도(부호 비트)로 사용할 수 있으나 unsigned char 는 이것이 허락되지 않습니다.

    따라서, 임의의 메모리에 바이트 단위로 접근해 값을 다룰 때에는 반드시 unsigned char 를 사용해야 full portability 를 얻을 수 있는 것입니다. 또한, 그와 같은 이유로 signed char 가 표현할 수 있는 값의 개수보다 unsigned char 가 표현할 수 있는 값의 개수가 많다는 사실에도 유의할 필요가 있습니다. signed char <-> unsigned char 사이의 값 변환이 1:1 로 이루어질 수 "없는" 경우도 있음을 의미합니다.

    이런 이유로, 표준이 바이트 값에 접근해야 하는 경우나 문자에 접근해야 하는 경우 (예: mem(), str() 함수들) 에는 모두 unsigned char 로 접근하도록 요구하고 있습니다.

    출처 : [https://kldp.org/node/75686](https://kldp.org/node/75686)
*   그리고, 바이트 단위로 초기화 하기 때문에 int형 배열을 초기화 할 때는 주의해야 한다.

    예를 들면 ft\_memset(arr, 1, sizeof(arr))을 호출하면 arr 배열이 모두 1로 초기화 된다고 생각하는데, 실제로는 배열이 모두 `16843009`로 초기화 된다. 16843009의 2진수 표현은 `0001 00000001 00000001 00000001` 이다. 즉, arr 배열은 1바이트(8비트)당 1로 초기화 된 것이다. _바이트가 딱 떨어지게 초기화 되지 않기 때문에 int형 배열의 요소를 1로 초기화 할 수 없다._ 다른 정수도 모두 마찬가지다. 딱 4개의 값만 가능하다고 한다. `0`, `-1`, `0x3f`, `0x7f`.

    출처 : [https://blog.naver.com/chogahui05/221484049429](https://blog.naver.com/chogahui05/221484049429)
* `size_t` 자료형은 '이론상 가장 큰 사이즈를 담을 수 있는 unsigned 데이터 타입'으로 정의된다.

## 2. **ft\_bzero**

```c
    #include "libft.h"

    void    ft_bzero(void *b, size_t n)
    {
        unsigned char   *dest;
        size_t          i;

        dest = b;
        i = 0;
        while(i++ < n)
            *dest++ = 0;
        return (b);
    }
```

**b 위치부터, n개의 바이트 만큼 0으로 초기화** 하는 함수.

* 이 함수는 더 이상 사용되지 않는다. memset을 사용하자.

## 3. **ft\_memcpy**

```c
    #include "libft.h"

    void                *ft_memcpy(void *dest, const void *src, size_t n)
    {
        unsigned char   *new_dest;
        unsigned char   *new_src;
        size_t          i;

        new_dest = dest;
        new_src = (unsigned char *)src;
        i = 0;
        while (i++ < n)
            *new_dest++ = *new_src++;
        return (dest);
    }
```

* `src` 가 가리키는 곳 부터 `n` 바이트 만큼을 `dest` 이 가리키는 곳에 복사한다.
* 이 함수는 `source` 의 널 종료 문자(null terminating character) 을 검사하지 않는다. 언제나 정확히 `num` 바이트 만큼을 복사한다.
* 만일 두 메모리 블록이 겹쳐져 있다면 memmove 함수를 이용해야 한다. src의 원본 값이 이전 src로 바뀐 상태에서 복사를 해버리기 때문이다.
* `const void* src`를 함수 내에서 `unsigned char *`로 강제 형변환을 하려고 했더니 경고가 떴다. const char _타입으로 받은 인수를 그냥 char_ 타입으로 다른 변수에 넘겨주려할 때 발생하는 경고라고 한다. 즉 const라는 qualifier(수식어,한정어)가 떨어져 나갔다는 뜻. `const unsigned char *`로 형변환 해줘야 함.

## 4. **ft\_memccpy**

```c
    #include "libft.h"

    void                  *ft_memccpy(void *dest, const void *src, int c, size_t n)
    {
        unsigned char     *new_dest;
        unsigned char     *new_src;
        unsigned char     find;
        size_t            i;

        new_dest = dest;
        new_src = (unsigned char *)src;
        find = c;
        i = 0;
        while(i < n)
        {
            new_dest[i] = new_src[i];
            if(new_src[i] == find)
                return (dest + (i + 1));
            i++;
        }
        return (0);
    }
```

* n바이트의 데이터를 dest에 복제할 때에 src 데이터에서 문자 c를 만나면 c까지 복제하고 복제를 중단한다.
* 복제된 dest변수에서 복제가 끝난 다음 번지를 return 한다. 만약 문자 c를 만나지 않았다면, n바이트를 복제하고 NULL을 return 한다.
*   의문점

    강제 형변환에 대해서.

    형변환을 명시적으로 써주지 않으면 워닝이 뜬다? → const 변수에 대해서만 그런걸로. gcc -Wall 플래그를 설정하면 에러로 처리해 컴파일이 되지 않는다.

## 5. **ft\_memmove**

```c
    #include "libft.h"

    void                    *ft_memmove(void *dest, const void *src, size_t n)
    {
        unsigned char       *new_dest;
        unsigned char *new_src;

        if (dest == src || n == 0)
            return (dest);
        if (dest < src)
        {
            new_dest = (unsigned char *)dest;
            new_src = (unsigned char *)src;
            while (n--)
                *new_dest++ = *new_src;
        }
        else
        {
            new_dest = dest + (n - 1);
            new_src = src + (n - 1);
            while (n--)
                *new_dest-- = *new_src--;
        }
        return (dest);
    }
```

[memmove 사용법 및 구현 - C 메모리 이동](https://hand-over.tistory.com/47)

src메모리 영역에서 dest 메모리 영역으로 n 바이트 만큼 복사한다. 이 때, src 배열은 src와 dest의 메모리 영역과 겹치지 않는 메모리 영역부터 먼저 복사한다.

*   메모리 영역이 overlap 되는 경우

    사실 잘 상상이 안됐었는데...

    1. 한 배열 안에서 복사를 수행할 때
    2.  그러면서 src 시작 주소가 dest 시작 주소보다 앞에 있을 때

        오버랩의 가능성이 생긴다.
*   overlap 해결 방안은?

    src의 마지막 주소 -> dest의 마지막 주소, 즉 뒤에서 부터 한 바이트 씩 복사한다.

## 6. **ft\_memchr**

```c
    #include "libft.h"

    void    *ft_memchr(const void *b, int c, size_t n)
    {
        unsigned char *new_b;
        unsigned char find;

        new_b = (unsigned char *)b;
        find = c;
        while (n--)
        {
            if (*new_b++ == find)
                return ((void *)(new_b - 1));
        }
        return (0);
    }
```

메모리 블록에서의 문자를 찾는다.

`ptr` 이 가리키는 메모리의 처음 `num` 바이트 중에서 처음으로 `value` 와 일치하는 값의 주소를 리턴한다.

* 메모리 블록에서 `value` 와 일치하는 값이 있다면 그 곳의 주소를 리턴하고 값을 찾지 못한다면 `NULL` 을 리턴한다.
*   의문점 1

    ```
      warning: return discards ‘const’ qualifier from pointer target type [-Wdiscarded-qualifiers]
                   return ((new_b - 1));
    ```

    컴파일 시 이런 경고가 떠서 return 값을

    ```
      return ((void *)(new_b - 1));
    ```

    이렇게 바꿨더니 해결됐다.

    혹은 아래 처럼 선언부에서 const 한정자를 사용하지 않아도 해결된다.

    ```
      unsigned char* new_b = (unsigned char *)b;
    ```

    참고 : [https://igotit.tistory.com/entry/char-const-char-char-const](https://igotit.tistory.com/entry/char-const-char-char-const)
*   의문점 2

    반환형이 void _면 반환할 때 다시 void_ 로 형변환을 한 뒤에 반환해야 하나? malloc에서는 안그랬던 것 같은데...

    [https://pang2h.tistory.com/253](https://pang2h.tistory.com/253)
*   인덱스로 표현하는 게 가독성이 더 좋을 것 같아서 수정함.

    ```
      void                *ft_memchr(const void *b, int c, size_t n)
      {
          size_t          i;
          unsigned char   *new_b;
          unsigned char   find;

          new_b = (unsigned char *)b;
          find = c;
          i = 0;
          while (i < n)
          {
              if (new_b[i] == find)
                  return ((new_b + i));
              i++;
          }
          return (0);
      }
    ```

## 7. **ft\_memcmp**

```c
    #include "libft.h"

    int     ft_memcmp(const void *b1, const void *b2, size_t n)
    {
        unsigned char *s1;
        unsigned char *s2;
        size_t i;

        if ((b1 == 0 && b2 == 0) || n == 0)
            return (0);
        else if (s1 == 0 || s2 == 0)
            return (s1 == 0 ? -1 : 1);
        s1 = (unsigned char *)b1;
        s2 = (unsigned char *)b2;
        i = 0;
        while (n--)
        {
            if (s1[i] != s2[i])
                break ; 
            i++;
        }
        return (s1[i] - s2[i]);
    }
```

`ptr1` 이 가리키는 처음 `num` 바이트의 데이터와 `ptr2` 가 가리키는 처음 `num` 바이트의 데이터를 비교한다.

* [ ] 두 개의 메모리 블록의 관계에 따라 아래와 같이 정수 값을 리턴한다.
  * 만일 두 메모리 블록이 정확히 같다면 0 을 리턴한다.
  * 만일 두 메모리 블록이 다를 경우, `ptr1` 과 `ptr2` 가 가리키는 메모리 블록에서 앞에서 부터 처음으로 다른 바이트를 살펴 보는데, 그 바이트를 `unsigned char` 로 해석하였을 때, 그 값이 `ptr1` 이 더 크면 0 보다 큰 값을, 아니면 0 보다 작은 값을 리턴한다.
*   [ ] 오버플로우는 처리를 못해주는 것 같다. 같은 문자열인데도 n값을 크게 주면 0이 아니라 쓰레기값끼리 비교한 값을 리턴한다.

    -> 항상 n은 버퍼 사이즈보다 같거나 작게.
* [ ] `strncmp`와의 차이점?
  * 두 문자열 중 하나가 끝나더라도(널 값이 나오더라도) 상관없이 _서로 다른 값이 나오거나_, _n개가 될 때 까지_ 비교를 수행한다.
  * `strncmp`는 s1과 s2가 모두 NULL값이 나오면 남은 카운트에 관계없이 0을 반환했던 것과 차이가 있다.
* [x] strlen
* [x] strlcpy
* [x] strlcat

## 8. **ft\_strchr**

```c
    #include "libft.h"

    char    *ft_strchr(const char *s, int c)
    {
        char find;

        find = c;
        while (*s++ != '\0')
        {
            if (*s == find)
                return ((char *)s);
        }
        return (0);
    }
```

**문자열에서 특정 문자를 찾을 때 사용**하는 함수.

* 문자열 s에서 첫 번째로 찾은 문자 c의 포인터를 리턴한다.
* 만일 찾는 문자가 없다면 NULL포인터를 리턴한다.
* 이 때 마지막 `NULL` 문자도 C 문자열의 일부로 간주하기 때문에 이 함수는 문자열의 맨 끝 부분을 가리키는 포인터를 얻기 위해 사용할 수 도 있다.
* int c는 검색할 문자로, `int` 형태로 형변환 되어서 전달되지만 함수 내부적으로는 다시 `char` 형태로 처리된다.

## 9. **ft\_strrchr**

```c
    #include "libft.h"

    char            *ft_strrchr(const char *s, int c)
    {
        char        *last;
        char        find;
        size_t      i;

        last = (char *)s;
        find = (char)c;
        i = ft_strlen(s);
        while (i > 0)
        {
            if (last[i] == find)
                return (last + i);
            i--;
        }
        if (last[i] == find)
            return (last);
        return (0);
    }
```

문자열 s에서 마지막으로 있는 문자 c의 포인터를 리턴한다.

*   예외처리

    ```
      if (last[i] == find)        
              return (last);
    ```

    s가 빈문자열일 때, s의 첫 글자만 c일 때 NULL이 아니라 s의 첫글자를 반환해야 함.

## 10. **ft\_strnstr**

```c
    #include "libft.h"

    char        *ft_strnstr(const char *big, const char *little, size_t len)
    {
        size_t  l_len;
        size_t  b_len;
        size_t  size;

        if (*little == '\0')
            return ((char *)big);
        l_len = ft_strlen(little);
        b_len = ft_strlen(big);
        if (b_len < l_len || len < l_len)
            return (0);
        size = b_len > len ? len : b_len;
        while (size-- >= l_len)
        {
            if (ft_memcmp(big, little, l_len) == 0)
                return ((char *)big);
            big++;
        }
        return (0);
    }
```

**문자열을 검색**한다.

* len 이하의 `big` 에서 `little` 를 검색하여 가장 먼저 나타나는 곳의 위치를 리턴한다.
* 이 때 일치하는 문자열이 없다면 널 포인터를 리턴하게 된다.
* 검색에서 마지막 널 문자는 포함하지 않는다.
* core dumped 에러
  * printf("%s\n", NULL) 이라고 했을 경우에 gcc컴파일러는 segfault를 띄우더라고요..구글링 좀 해보니까 gcc컴파일러가 그렇다고 하더라고요. -slack donglee 님
  * 저도 계속 seg fault가 떠서 테스트를 해볼 때 printf 내부에서 %s 앞에 ft : 라던지 뭐든 구분할 수 있는 문자를 넣어줍니다!! -slack joockim님
  * 참고 : [https://guimong.tistory.com/entry/printf%EC%9D%98-Segmentationg-fault-%EC%B0%A8%EC%9D%B4](https://guimong.tistory.com/entry/printf%EC%9D%98-Segmentationg-fault-%EC%B0%A8%EC%9D%B4)
* **남은 size의 길이가 l\_len 길이 보다 작다면, 비교할 의미가 없으므로 NULL을 리턴**

## 11. **ft\_strncmp**

```c
    #include "libft.h"

    int                 ft_strncmp(const char *s1, const char *s2, size_t n)
    {
        size_t          i;
        unsigned char   *b1;
        unsigned char   *b2;

        b1 = (unsigned char *)s1;
        b2 = (unsigned char *)s2;
        if (b1 == 0 && b2 == 0)
            return (0);
        else if (b1 == 0 || b2 == 0)
            return (b1 == 0 ? -1 : 1);
        if (n == 0)
            return (0);
        i = 0;
        while ((b1[i] != '\0') && (b2[i] != '\0') && i < (n - 1))
        {
            if (b1[i] != b2[i])
                break ;
            i++;
        }
        return (b1[i] - b2[i]);
    }
```

*   의문점

    return 할 때 unsigned char로 형변환 하는 코드를 봤다. 필요한가?

    *   2020.03.10 해결

        `ft_strncmp("test\200", "test\0", 6)` 같은

        128 이상의 아스키코드 수도 계산하기 위해서 unsigned char \*로의 형변환이 필요했다.

## 12. **ft\_atoi**

```c
    #include "libft.h"

    int     ft_isspace(char c)
    {
        if (c == ' ' || c == '\n' || c == '\t' ||
                c == '\v' || c == '\f' || c == '\r')
            return (1);
        else
            return (0);
    }

    int     ft_atoi(const char *str)
    {
        int nbr;
        int sign;
        int i;

        nbr = 0;
        sign = 1;
        i = 0;
        while ((str[i] != '\0') && ft_isspace(str[i]) == 1)
            i++;
        if (str[i] == '-')
            sign = -1;
        if ((str[i] == '-') || (str[i] == '+'))
            i++;
        while ((str[i] != '\0') && ('0' <= str[i]) && (str[i] <= '9'))
        {
            nbr = (nbr * 10) + (str[i] - '0');
            i++;
        }
        return (sign * nbr);
    }
```

## 13. **ft\_isalpha**

```c
    #include "libft.h"

    int     ft_isalpha(int c)
    {
        return (((65 <= c) && (90 >= c)) || ((97 <= c) && (122 >= c)));
    }
```

연산자도 참(1)과 거짓(0)을 반환한다.

* [x] isdigit
* [x] isalnum
* [x] isascii
* [x] isprint

## 14. **ft\_toupper**

```c
    #include "libft.h"

    int     ft_islower(int c)
    {
        return((97 <= c) && (122 >= c));
    }

    int        ft_toupper(int c)
    {
        if (ft_islower(c))
            return (c - 32);
        return (c);
    }
```

* [ ] 인자가 소문자일 경우 대문자로 바꿔 반환한다.
* [ ] 소문자가 아닌 인자가 들어왔을 때는 그대로 반환한다.
* [x] tolower

## 15. **ft\_calloc**

```c
    #include "libft.h"

    void    ft_bzero(void *b, size_t n)
    {
        unsigned char   *dest;
        size_t          i;

        dest = b;
        i = 0;
        while(i++ < n)
            *dest++ = 0;
    }

    void    *ft_calloc(size_t nmemb, size_t size)
    {
        void *mem;

        if (!(mem = malloc(nmemb * size)))
            return (NULL);
        ft_bzero(mem, (nmemb * size));
        return(mem);
    }
```

* size 크기의 변수를 nmemb개 만큼 저장할 수 있는 메모리 공간을 할당한다.
* 그리고 동적으로 할당한 메모리의 모든 비트를 0으로 설정한다.
* malloc 과의 차이점은?
  * 참고 : [https://bunhere.tistory.com/255](https://bunhere.tistory.com/255)

## 16. **ft\_strdup**

```c
    #include "libft.h"

    char    *ft_strdup(const char *str)
    {
        int i;
        int len;
        char *new_str;

        len = 0;
        while (str[len])
            len++;
        new_str = (char *)malloc(sizeof(char) * (len + 1));
        if (!(new_str))
            return (NULL);
        i = 0;
        while (str[i])
        {
            new_str[i] = str[i];
            i++;
        }
        new_str[i] = '\0';
        return (new_str);
    }
```

* 문자열 str 길이 + 1 크기를 malloc으로 할당 후 문자열 str을 복사한 후 반환한다.
* 반환받은 메모리는 반드시 free를 통하여 메모리를 해제해야 한다.
* malloc + strcpy
