calloc, strdup, ft_substr, ft_strjoin, ft_strtrim, ft_split, ft_itoa, ft_strmapi

개요

malloc함수와 동적할당 관련 함수를 살펴보겠습니다.


malloc/free


SYNOPSIS

  • 함수 원형 : void *malloc(size_t size) / void free(void *ptr)
  • 함수 라이브러리 : #include
  • 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
  • size_t count : size크기의 메모리를 몇개
  • size_t size : 할당할 자료형
  • 리턴값 : 성공하면 할당한 문자열의 주소 실패하면 널

DESCRIPTION

malloc과 같은 역할이다 다른점은 동적할당한 메모리를 모두 0으로 초기화한다는 점

calloc CODE

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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
  • const char *s1 : 복제할 문자열
  • 리턴값 : 성공하면 할당한 문자열의 주소 실패하면 널

DESCRIPTION

인자로 들어온 s1 문자열을 duplicate 즉 복제를 하는 함수이다.

strdup CODE

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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)) { } 이 기본 형식이다.

호출할 때 함수포인터의 형식에 맞춰서 선언해주면 된다. 모양이 낯설 뿐 일반적인 파라메터같이 생각하고 사용하면 된다.

예를들어 함수가

1
2
3
char add(unsigned int a, char b){
	return (c + 1);
}

이고 s가 “abc”라고 한다면 “bcd”가 리턴된다.



3줄 요약

1. malloc을 사용하는 것은 익숙해지면 할만하다.

2. split너무 어렵다.

3. 예외처리 해줄게 생각보다 많다. 이런 과제들조차도 이정도인데 시중에 나온 프로그램들은 어떨지 가늠이안간다.