문자열 관련 함수 : calloc, strdup, ft_substr, ft_strjoin, ft_strtrim, ft_split, ft_itoa, ft_strmapi
dqQQQ
·2023. 11. 25. 12:22
개요
malloc함수와 동적할당 관련 함수를 살펴보겠습니다.
malloc/free
SYNOPSIS
함수 원형
: void *malloc(size_t size) / void free(void *ptr)함수 라이브러리
: #include <stdlib.h>size_t size
: 할당할 길이void *ptr
: 동적해제할 주소리턴값
: 성공하면 할당한 메모리의 주소 실패하면 널
DESCRIPTION
malloc은 동적할당을 해주기 위한 함수이다. sizeof(자료형)은 size_t형이므로 arguement와 parameter의 자료형을 맞춰줄수있다.
일반 변수와 다르게 malloc을 이용하여 동적할당을 해주면 heap부분의 메모리를 사용한다.
동적할당을 해준다음 free로 동적해제를 시켜주지 않으면 메모리누수(memory leak)가 발생하므로 항상 짝지어서 사용하는 코딩습관을 기르자.
calloc
SYNOPSIS
함수 원형
: void *ft_calloc(size_t count, size_t size)함수 라이브러리
: #include <string.h>size_t count
: size크기의 메모리를 몇개size_t size
: 할당할 자료형리턴값
: 성공하면 할당한 문자열의 주소 실패하면 널
DESCRIPTION
malloc과 같은 역할이다 다른점은 동적할당한 메모리를 모두 0으로 초기화한다는 점
calloc CODE
void *ft_calloc(size_t count, size_t size)
{
void *ret;
if (!count || !size)
{
size = 1;
count = 1;
}
ret = malloc(count * size);
if (ret)
ft_bzero(ret, count * size);
return (ret);
}
count 또는 size에 0 인자가 들어오면 널값을 반환해야한다 그러기 위해서 1바이트의 메모리를 할당해서 리턴한다.
bzero함수를 이용해서 동적할당한 부분을 0으로 초기화시켰다.
strdup
SYNOPSIS
함수 원형
: char *ft_strdup(const char *s1)함수 라이브러리
: #include <string.h>const char *s1
: 복제할 문자열리턴값
: 성공하면 할당한 문자열의 주소 실패하면 널
DESCRIPTION
인자로 들어온 s1 문자열을 duplicate 즉 복제를 하는 함수이다.
strdup CODE
char *ft_strdup(const char *s1)
{
char *ret;
int len;
int i;
len = ft_strlen(s1);
i = 0;
if ((ret = (char *)malloc(len + 1)) == 0)
return (0);
*(ret + len) = 0;
while (i < len)
{
*(ret + i) = *(s1 + i);
i++;
}
return (ret);
}
strlen함수를 이용해서 복제할 문자열의 길이를 구한다음 NULL값까지 고려를 해주어 동적할당을한다.
복사를 진행하고 널값을 넣어주고 복제한 문자열의 주소를 리턴해준다.
ft_substr
SYNOPSIS
함수 원형
: char *ft_substr(char const *s, unsigned int start, size_t len)const char *s
: 복제할 문자열unsigned int start
: 복제를 시작할 인덱스size_t len
: 복제를 끝낼 인덱스리턴값
: 성공하면 할당한 문자열의 주소 실패하면 널
DESCRIPTION
인자로 들어온 s 문자열을 start인덱스부터 len인덱스까지 복제하여 리턴
ft_substr CODE
char *ft_substr(char const *s, unsigned int start, size_t len)
{
char *tmp;
size_t i;
if (!s)
return (0);
if (start >= ft_strlen(s))
return (ft_strdup(""));
if (len > ft_strlen(s))
{
len = ft_strlen(s);
tmp = (char *)malloc(len - start + 1);
}
else
tmp = (char *)malloc(len + 1);
if (!tmp)
return (0);
*(tmp + len) = 0;
i = 0;
while (i < len && *(s + start + i))
{
*(tmp + i) = *(s + start + i);
i++;
}
return (tmp);
}
복제할 문자열이 없다면 진행할 필요가 없기때문에 예외처리를 해준다.
만약 복제를 시작할 인덱스가 복제할 문자열의 길이보다 크다면 널값을 리턴해주어야한다.
return (0)을 진행한다면 프로그램을 작동하지 않게된다. 우리는 널값이 들어있는 문자열을 리턴해야하기 때문에 strdup("")를 사용했다.
할당을 진행하고 복사를 진행한 후 복사한 문자열을 리턴한다.
ft_strjoin
SYNOPSIS
함수 원형
: char *ft_strjoin(char const *s1, char const *s2)const char *s1
: 복제 문자열에 먼저 넣을 문자열const char *s2
: 복제 문자열에 나중에 넣을 문자열리턴값
: 성공하면 할당한 문자열의 주소 실패하면 널
DESCRIPTION
s1문자열과 s2문자열을 합친 문자열을 새로 만들어 리턴
ft_strjoin CODE
char *ft_strjoin(char const *s1, char const *s2)
{
size_t len_1;
size_t len_2;
char *ret;
if (!s1 && !s2)
return (0);
if (!s1)
return ((char *)s2);
if (!s2)
return ((char *)s1);
len_1 = ft_strlen(s1);
len_2 = ft_strlen(s2);
if ((ret = (char *)malloc(len_1 + len_2 + 1)) == 0)
return (0);
*(ret + len_1 + len_2) = 0;
ft_memcpy(ret, s1, len_1);
ft_memcpy(ret + len_1, s2, len_2);
return (ret);
}
malloc함수의 인자로 넣을 것이기 때문에 size_t형으로 s1, s2의 길이를 저장한다.
둘다 가리키는 곳이 없다면 그냥 종료/ s1만 가리키는 곳이 없다면 s2문자열 리턴 / s2만 가리키는 곳이 없다면 s1문자열 리턴
길이를 구한다음 동적할당을 해주고 memcpy함수를 이용하여 s1문자열을 우선 복사해주고 s2문자열을 이어준다.
ft_strtrim
SYNOPSIS
함수 원형
: char *ft_strtrim(char const *s1, char const *set)const char *s1
: set을 찾기 위한 문자열const char *set
: 찾을 문자열리턴값
: 성공하면 할당한 문자열의 주소 실패하면 널
DESCRIPTION
앞,뒤로 set문자열이 있으면 없을 때까지 해당 문자를 제거하여 복제한후 리턴
ft_strjoin CODE
char *ft_strtrim(char const *s1, char const *set)
{
size_t start;
size_t end;
char *ret;
if (!s1)
return (0);
if (!set)
return (ft_strdup(s1));
start = 0;
while (*(s1 + start) && ft_strchr(set, *(s1 + start)))
start++;
end = ft_strlen(s1);
while (*(s1 + end - 1) && (end - 1) && ft_strchr(set, *(s1 + end - 1)))
end--;
if (start > end)
return (ft_strdup(""));
if ((ret = (char *)malloc(end - start + 1)) == 0)
return (0);
ft_strlcpy(ret, s1 + start, end - start + 1);
return (ret);
}
s1이 가리키는 곳이 없다면 0을, 찾을 문자열이 없다면 s1을 그냥 리턴한다.
ft_split
SYNOPSIS
함수 원형
: char **ft_split(char const *s, char c)const char *s
: split할 문자열char c
: split할 구분자리턴값
: 성공하면 할당한 문자열의 주소 실패하면 널
DESCRIPTION
s문자열을 앞에서부터 검사하는데 도중에 c를 만나면 끊고 해당 위치까지 복제하는 것을 끝날때까지 한 결과를 이차원배열로 저장한다.
ft_strjoin CODE
static size_t cnt_word(char const *s, char c)
{
size_t i;
size_t cnt;
i = 0;
cnt = 0;
while (*(s + i))
{
if (*(s + i) == c)
i++;
else
{
cnt++;
while (*(s + i) != c && *(s + i))
i++;
}
}
return (cnt);
}
static void free_malloc(char **ret, size_t word)
{
size_t i;
i = 0;
while (i < word)
{
free(*(ret + i));
i++;
}
free(ret);
}
static int cut_str(char const *s, char c, char **ret, size_t word)
{
size_t idx1;
size_t idx2;
idx1 = 0;
while (*(s + idx1))
{
if (*(s + idx1) == c)
idx1++;
else
{
idx2 = 0;
while (*(s + idx1 + idx2) != c && *(s + idx1 + idx2))
idx2++;
if ((*(ret + word) = (char *)malloc(idx2 + 1)) == 0)
{
free_malloc(ret, word);
return (0);
}
ft_memcpy(*(ret + word), s + idx1, idx2);
*(*(ret + word) + idx2) = 0;
idx1 += idx2;
word++;
}
}
return (1);
}
char **ft_split(char const *s, char c)
{
size_t cnt;
char **ret;
if (!s)
return ((void *)0);
cnt = cnt_word(s, c);
if ((ret = (char **)malloc(sizeof(char *) * (cnt + 1))) == 0)
return (0);
*(ret + cnt) = 0;
if (!cut_str(s, c, ret, 0))
return (0);
return (ret);
}
cnt_word
함수는 split하였을 때 총 단어의 개수이다. 이차원배열로 동적할당을 얼만큼 해줄지 계산할 때 사용한다.
free_malloc
함수는 split하는 도중에 동적할당을 실패하고 그대로 종료했을 때 발생하는 메모리 누수(memory leak)을 방지하기 위해서 사용한다.
cut_str
함수는 말 그대로 구분자를 기준으로 문자열을 자르는 함수이다. split과제에서 제일 핵심적인 역할을 수행하는 함수이다.
ft_split
함수는 위에서 split을 하기위한 함수들을 잘 조합하여 split을 잘 수행하도록 하는 함수이다.
그림으로 split함수를 설명하면 다음과 같다.
ft_itoa
SYNOPSIS
함수 원형
: char *ft_itoa(int n)int n
: ascii로 바꿀 정수리턴값
: 성공하면 할당한 문자열의 주소 실패하면 널
DESCRIPTION
인자로 들어온 정수를 문자열로 바꾸어 리턴
ft_itoa CODE
static int cnt_digits(int n)
{
if (n == -2147483648)
return (10);
if (n < 0)
n *= -1;
if (n == 0)
return (0);
while (n != 0)
return (1 + cnt_digits(n / 10));
return (0);
}
static void get_digit(int *sign, long long *i, int *digit)
{
if (*i == 0)
*digit = 1;
else if (*i < 0)
{
*sign = -1;
*i *= -1;
*digit = cnt_digits(*i) + 1;
}
else
*digit = cnt_digits(*i);
}
char *ft_itoa(int n)
{
char *ret;
int digit;
int sign;
long long i;
i = n;
sign = 1;
get_digit(&sign, &i, &digit);
if ((ret = (char *)malloc(digit + 1)) == 0)
return (0);
*(ret + digit) = 0;
digit--;
while (digit >= 0)
{
*(ret + digit) = '0' + i % 10;
i /= 10;
digit--;
}
if (sign == -1)
*(ret + digit + 1) = '-';
return (ret);
}
cnt_digits함수는 재귀함수를 통해서 자릿수를 구하는 함수이다.
get_digit함수는 인자로 들어온 숫자가 0이냐 음수냐 양수냐에 따라서 할당해할 길이를 구한다.
itoa함수에서는 정수의 자릿수만큼 할당을 한후에 문자열로 변환해준 다음 리턴해준다.
ft_strmapi
SYNOPSIS
함수 원형
: char ft_strmapi(char const *s, char (f)(unsigned int, char))char const *s
: f함수를 적용할 문자열char (*f)(unsigned int, char)
: unsigned int와 char형을 파라메터로 가진 함수 f의 포인터리턴값
: 성공하면 할당한 문자열의 주소 실패하면 널
DESCRIPTION
s문자열의 복제 문자열에 f함수를 적용하여 리턴
ft_itoa CODE
char *ft_strmapi(char const *s, char (*f)(unsigned int, char))
{
char *ret;
size_t i;
size_t len;
if (!s || !f)
return (0);
len = ft_strlen(s);
if ((ret = (char *)malloc(len + 1)) == 0)
return (0);
*(ret + len) = 0;
i = 0;
while (i < len)
{
*(ret + i) = f(i, *(s + i));
i++;
}
return (ret);
}
특이한 점으로 ft_strmapi함수의 파라메터로 함수 포인터가 보인다.
함수자료형 함수이름(함수포인터반환값자료형 (*함수포인터이름)(함수포인터매개변수자료형1, 함수포인터매개변수자료형2))
{
} 이 기본 형식이다.
호출할 때 함수포인터의 형식에 맞춰서 선언해주면 된다. 모양이 낯설 뿐 일반적인 파라메터같이 생각하고 사용하면 된다.
예를들어 함수가
char add(unsigned int a, char b){
return (c + 1);
}
이고 s가 "abc"라고 한다면 "bcd"가 리턴된다.