libft 프로젝트의 part1 함수들을 구현하면서 메모했던 내용들을 정리해두었다. 이 라이브러리의 함수들은 꾸준히 업데이트 되고 있기 때문에 가장 최신의 코드는 여기 깃허브 저장소를 참고...
#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 데이터 타입'으로 정의된다.
#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으로 초기화 하는 함수.
#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
바이트 만큼을 복사한다.const void* src
를 함수 내에서 unsigned char *
로 강제 형변환을 하려고 했더니 경고가 떴다. const char 타입으로 받은 인수를 그냥 char 타입으로 다른 변수에 넘겨주려할 때 발생하는 경고라고 한다. 즉 const라는 qualifier(수식어,한정어)가 떨어져 나갔다는 뜻. const unsigned char *
로 형변환 해줘야 함. #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 플래그를 설정하면 에러로 처리해 컴파일이 되지 않는다.
#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 되는 경우
사실 잘 상상이 안됐었는데...
한 배열 안에서 복사를 수행할 때
그러면서 src 시작 주소가 dest 시작 주소보다 앞에 있을 때
오버랩의 가능성이 생긴다.
overlap 해결 방안은?
src의 마지막 주소 -> dest의 마지막 주소, 즉 뒤에서 부터 한 바이트 씩 복사한다.
#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
의문점 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);
}
#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
바이트의 데이터를 비교한다.
두 개의 메모리 블록의 관계에 따라 아래와 같이 정수 값을 리턴한다.
ptr1
과 ptr2
가 가리키는 메모리 블록에서 앞에서 부터 처음으로 다른 바이트를 살펴 보는데, 그 바이트를 unsigned char
로 해석하였을 때, 그 값이 ptr1
이 더 크면 0 보다 큰 값을, 아니면 0 보다 작은 값을 리턴한다.오버플로우는 처리를 못해주는 것 같다. 같은 문자열인데도 n값을 크게 주면 0이 아니라 쓰레기값끼리 비교한 값을 리턴한다.
-> 항상 n은 버퍼 사이즈보다 같거나 작게.
strncmp
와의 차이점?
strncmp
는 s1과 s2가 모두 NULL값이 나오면 남은 카운트에 관계없이 0을 반환했던 것과 차이가 있다.strlen
strlcpy
strlcat
#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);
}
문자열에서 특정 문자를 찾을 때 사용하는 함수.
NULL
문자도 C 문자열의 일부로 간주하기 때문에 이 함수는 문자열의 맨 끝 부분을 가리키는 포인터를 얻기 위해 사용할 수 도 있다.int
형태로 형변환 되어서 전달되지만 함수 내부적으로는 다시 char
형태로 처리된다. #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의 첫글자를 반환해야 함.
#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);
}
문자열을 검색한다.
big
에서 little
를 검색하여 가장 먼저 나타나는 곳의 위치를 리턴한다. #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 *로의 형변환이 필요했다.
#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);
}
#include "libft.h"
int ft_isalpha(int c)
{
return (((65 <= c) && (90 >= c)) || ((97 <= c) && (122 >= c)));
}
연산자도 참(1)과 거짓(0)을 반환한다.
#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);
}
인자가 소문자일 경우 대문자로 바꿔 반환한다.
소문자가 아닌 인자가 들어왔을 때는 그대로 반환한다.
tolower
#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);
}
#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);
}