🧠
Hi, Daehuyn Lee
  • Fork-my-brain
  • Network
    • 7. "데이터가 전달되는 원리" OSI 7계층 모델과 TCP:IP 모델
    • [Netwhat] 연습문제 정리
    • 11. IP 라우팅(routing) 동작 과정
    • 3. IP address 란?
    • 2. 컴퓨터 구조를 통해 이해하는 파일(File)과 소켓(Socket)
    • 10 "더 편리한 인터넷을 위해" DHCP && DNS 프로토콜
    • 9. 데이터? 세그먼트? 패킷? 헷갈릴 땐 PDU를 알아보자
    • 8. TCP 와 UDP 차이를 자세히 알아보자
    • 5. 서브넷팅(subnetting)으로 네크워크를 효율적으로 관리하자
    • 4. 넷마스크(Netmask)와 서브넷마스크(Subnetmask)
    • 1. 비유로 이해하는 컴퓨터 구조
    • 6. 공인(Public) && 사설(Private) IP의 차이점
  • Django
    • [Django 1] 가상환경에 Django 설치하기
    • [Django 3] Hello World 웹사이트 만들기
    • [Django 9] static 으로 css 로드하기
    • [Django 10] 한 템플릿에서 복수의 css 파일 적용하기
    • [Django 11] URL app별로 관리하기
    • [Django 8] 템플릿 상속
    • [Django 4] MTV 패턴
    • [Django 6] 블로그 model 만들기
    • [Django 2] Django는 어떻게 작동할까
    • [Django 7] '새 글 작성' 기능 만들기
    • [Django 5] 템플릿 언어
  • Projects
    • 예발자닷컴
      • 4. 프론트엔드의 역할은 어디까지 - 더미데이터 만들기
      • 7. [React 리팩토링] CSS Inline Styling에 Props 사용하기
      • 6. [React 리팩토링] JSX에서 조건문 사용해 렌더링하기
      • 3. 예발자닷컴 프론트서버 업데이트 하기
      • 8. [React 리팩토링] 예발자 프로젝트에 Redux 적용하기
      • 5. [React 리팩토링] JSX로 HTML 렌더링하기
      • 1. 👨‍👨‍👦‍👦 Github로 협업 프로젝트 관리하기
      • 2. React Component를 활용한 웹페이지 디자인 연습
  • Git
    • [Git] Interactive Rebase 실습
    • 오픈소스 개발 참여에 필요한 Git 명령어 정리
    • 개발자가 오픈소스를 읽는 방법
    • 오픈소스 프로젝트 시작하기
    • SSH agent ; Passphrase 입력 없이 Push하기
    • SSH로 원격저장소 접속하기
    • [Github] 개인 저장소를 팀 저장소로 변경하기
    • GitHub Dependabot
    • Git add, commit, push 취소하기
    • 깃헙 잔디 관리 팁
    • 원격저장소 여러개 연결하기
    • Typora(마크다운 에디터) 사용법
  • C
    • C Piscine
      • 메모리 구조를 알아보자
      • Makefile 만들기
      • GCC로 정적 라이브러리 파일 만들기
      • 외부 라이브러리 GCC로 컴파일 하기
      • 정적(Static) 변수
      • 저수준 파일 입출력
      • Makefile 자주 사용하는 문법 정리
      • segmentation fault 해결하기
      • C의 구조체 개념
      • 연결 리스트(linked list)에서 이중 포인터 사용하기
      • 로컬에 Norminette 설치하기
    • GetNextLine
      • [GetNextLine] 과제소개-Reading a line on a fd is way too tedious
      • [GetNextLine] 삽질의 기록
      • [GetNextLine] 리팩토링-프로그램의 목적을 고려한 코드
    • ft_printf
      • 1. 과제소개
      • 2. 가변인자 (Variadic Arguments)
      • 3. 형식태그와 서식지정자 printf 함수의 옵션 알아보기
    • Libft
      • [Libft] Bonus
      • [Libft] Test Program
      • [Libft] 나만의 C 라이브러리 만들기
      • [Libft] Part 2
      • [Libft] Part 1
  • UNIX shell
    • [minishell] 4. 종료상태와 에러메세지 처리
    • [minishell] 1. 과제소개 및 선행지식
    • [minishell] 2. 프로그램 구조 및 개발 기록들
    • [minishell] 5. 파이프(Pipe) 처리
    • [minishell] 3. 시그널(Signal) 처리하기
    • [minishell] 6. 리다이렉션(Redirection) 처리
  • Web
    • Next.js
      • [Next.js] CSS모듈과 복수의 class 사용하기
    • Node.js
      • [Node.js] 웹페이지에 파일 띄우기
      • [Node.js] URL에서 쿼리스트링 추출하기
      • [Node.js] '새 글 작성' 페이지 만들기
    • React
      • [React] 2. 컴포넌트(Component) 생성 및 파일별로 분리하기
      • [React] 1. 파일 구조 이해하기
      • [React] 4. 컴포넌트의 State 란
      • [React] 3. 컴포넌트의 Props 란
    • Javascript
      • Click, Enter 두 개의 이벤트 동시에 등록하기
      • Click eventListener 등록하기
      • JavaScript & C 문법 비교
      • JavaScript 객체 지향의 특징
    • CSS
      • [CSS] box-model, display, position
  • Docker
    • ft_server
      • 2. 도커 설치부터 워드프레스 구축까지
      • 1. 선행지식-Docker? Debian Buster? Nginx? ...
      • 3. Dockerfile 만들기
  • Kubernetes
    • 🌌[쿠버네티스 아키텍처] 3. API 호출
    • 🌌[쿠버네티스 아키텍처] 1. 구성 및 설계
    • 🌌[쿠버네티스 아키텍처] 2. 오브젝트 (Objects)
  • Operating System
    • Philosophers
      • [Philosophers] 예시예제로 보는 뮤텍스와 세마포어의 차이
      • [Philosophers] 식사하는 철학자 문제 소개
  • CPP
    • [CPP-08] STL containers, iterators, algorithms
    • [CPP-06] CPP 형변환 연산자
    • [CPP-04 ex02] 인터페이스(Interface) 클래스
    • [CPP-04 ex00] 다형성(Polymorphism) 및 가상함수
    • [CPP-02] Canonical 클래스 복사 생성자와 대입 연산자 오버로딩
    • [CPP-07] Templates
    • [CPP-01] this 포인터와 문자열 스트림(stringstream)
    • [CPP-01] 클래스의 정적할당과 동적할당 new, delete
    • [CPP-01] 파일 입출력 및 문자열 치환하기
    • [CPP-01] 참조자(reference)와 포인터는 다르다
    • [CPP-02] 정수부동소수값 - 고정소수값 변환
    • [CPP-04 ex01] 추상 클래스의 필요성 순수 가상함수
    • [CPP-00] Megaphone! CPP 표준입출력
    • [CPP-03] (ClapTrap이 뭐지) 다중 상속과 가상 상속
    • [CPP-05] 예외 처리 (exception handling)
    • [CPP-00] 객체지향의 관점으로 클래스 이해하기
    • [CPP-01] 랜덤값 얻기
  • IBM Cloud
    • [IBM Cloud] 1. 클라우드 컴퓨팅 개요
    • [IBM Cloud] 5. 클라우드 컴퓨팅의 구성 요소
    • [IBM Cloud] 3. 클라우드 서비스 모델 및 배포 모델
    • [IBM Cloud] 2. 클라우드를 활용하는 새 기술들
    • [IBM Cloud] 4. 떠오르는 클라우드 트렌드
    • [IBM Cloud] 6. 클라우드 스토리지 유형 및 CDN
  • Assembly
    • [libasm] 어셈블리 프로그램 구조와 x64 레지스터 이해하기
    • [libasm] strlen 함수를 어셈블리어로 짠다면
    • [libasm] 어셈블리 명령어(opcode) 정리
Powered by GitBook
On this page
  • 1. ft_memset
  • 2. ft_bzero
  • 3. ft_memcpy
  • 4. ft_memccpy
  • 5. ft_memmove
  • 6. ft_memchr
  • 7. ft_memcmp
  • 8. ft_strchr
  • 9. ft_strrchr
  • 10. ft_strnstr
  • 11. ft_strncmp
  • 12. ft_atoi
  • 13. ft_isalpha
  • 14. ft_toupper
  • 15. ft_calloc
  • 16. ft_strdup

Was this helpful?

  1. C
  2. Libft

[Libft] Part 1

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

Previous[Libft] Part 2NextUNIX shell

Last updated 3 years ago

Was this helpful?

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

1. ft_memset

    #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 로 접근하도록 요구하고 있습니다.

    출처 :

  • 그리고, 바이트 단위로 초기화 하기 때문에 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.

    출처 :

  • size_t 자료형은 '이론상 가장 큰 사이즈를 담을 수 있는 unsigned 데이터 타입'으로 정의된다.

2. ft_bzero

    #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

    #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

    #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

    #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);
    }

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

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

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

    1. 한 배열 안에서 복사를 수행할 때

    2. 그러면서 src 시작 주소가 dest 시작 주소보다 앞에 있을 때

      오버랩의 가능성이 생긴다.

  • overlap 해결 방안은?

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

6. ft_memchr

    #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;
  • 의문점 2

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

  • 인덱스로 표현하는 게 가독성이 더 좋을 것 같아서 수정함.

      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

    #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 바이트의 데이터를 비교한다.

8. ft_strchr

    #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

    #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

    #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님

  • 남은 size의 길이가 l_len 길이 보다 작다면, 비교할 의미가 없으므로 NULL을 리턴

11. ft_strncmp

    #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

    #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

    #include "libft.h"

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

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

14. ft_toupper

    #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);
    }

15. ft_calloc

    #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 과의 차이점은?

16. ft_strdup

    #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

참고 :

참고 :

참고 :

여기
https://kldp.org/node/75686
https://blog.naver.com/chogahui05/221484049429
memmove 사용법 및 구현 - C 메모리 이동
https://igotit.tistory.com/entry/char-const-char-char-const
https://pang2h.tistory.com/253
https://guimong.tistory.com/entry/printf%EC%9D%98-Segmentationg-fault-%EC%B0%A8%EC%9D%B4
https://bunhere.tistory.com/255