C07

김원호·2023년 8월 1일

42seoul

목록 보기
9/10

동적 할당을 처음으로 사용하는 문제들

Exercise 00 : ft_strdup
• Reproduce the behavior of the function strdup (man strdup).

#include <stdlib.h>

char	*ft_strdup(char *src)
{
	char	*dest;
	int		len;
	int		i;

	len = 0;
	while (src[len] != '\0')
		len++;
	dest = (char *)malloc(sizeof(char) * (len + 1));
	if (dest == 0)
		return (0);
	i = 0;
	while (i < len)
	{
		dest[i] = src[i];
		i++;
	}
	dest[i] = '\0';
	return (dest);
}

strdup함수를 구현하는 함수
strcpy는 복사 받을 dest를 매개변수로 받지만, strdup는 src만을 매개변수로 받아 동적할당을 사용하여 dest를 직접 만들고 사용하기 때문에 더 안정적이다. 하지만 free를 꼭 사용해야한다.

Exercise 01 : ft_range
• Create a function ft_range which returns an array of ints. This int array should contain all values between min and max.

#include <stdlib.h>

int	*ft_range(int min, int max)
{
	int	*result;
	int	range;
	int	i;

	range = max - min;
	if (range <= 0)
		return (0);
	result = (int *)malloc(sizeof(int) * range);
	if (result == 0)
		return (0);
	i = 0;
	while (min < max)
		result[i++] = min++;
	return (result);
}

min값과 max값이 주어지면 배열에 min부터 max 이전 숫자까지 넣어줘야 한다.

Exercise 02 : ft_ultimate_range
• Create a function ft_ultimate_range which allocates and assigns an array of ints. This int array should contain all values between min and max.

#include <stdlib.h>

int	ft_ultimate_range(int **range, int min, int max)
{
	int	*ptr;
	int	rg;
	int	i;

	rg = max - min;
	if (rg <= 0)
		return (0);
	ptr = (int *)malloc(sizeof(int) * rg);
	if (ptr == 0)
		return (0);
	i = 0;
	while (min < max)
		ptr[i++] = min++;
	*range = ptr;
	return (rg);
}

01번과 같지만, 반환값이 max-min값이고, 01번에서 만드는 것과 같은 배열을 range라는 이중포인터를 매개변수로 받아, 그 매개변수로 해당 배열을 전달해줘야 한다. 즉 이 이중포인터는 2차원 배열이라기보다는 배열 자체의 주솟값을 사용하여 그 배열을 함수의 매개변수에 저장하는 역할을 한다.

포인터 공부는 해도해도 너무 어렵다...

Exercise 03 : ft_strjoin
• Write a function that will concatenate all the strings pointed by strs separated by sep.

#include <stdlib.h>

int	str_len(char *str)
{
	int	len;

	len = 0;
	while (*str)
	{
		len++;
		str++;
	}
	return (len);
}

char	*concat(char *str1, char *str2)
{
	int	i;
	int	len;

	i = 0;
	len = 0;
	while (str1[len] != '\0')
		len++;
	while (str2[i] != '\0')
		str1[len++] = str2[i++];
	str1[len] = '\0';
	return (str1);
}

char	*ft_strjoin(int size, char **strs, char *sep)
{
	int		i;
	int		len;
	char	*result;

	i = 0;
	len = 0;
	while (i < size)
		len += str_len(strs[i++]);
	if (size > 0)
		len += (size - 1) * str_len(sep);
	result = (char *)malloc(sizeof(char) * (len + 1));
	result[0] = '\0';
	if (result == 0)
		return (0);
	i = 0;
	while (i < size)
	{
		result = concat(result, strs[i]);
		if (i < size - 1)
			result = concat(result, sep);
		i++;
	}
	result[len] = '\0';
	return (result);
}

문자열로 이루어진 배열과 구분자 문자열이 주어지면, 문자열 배열에 있는 문자열 사이사이에 구분자를 집어넣어 하나의 큰 문자열을 만드는 함수
문자열을 붙여주기 전에 먼저 전체 길이를 알아야 하기 때문에 str_len함수 사용. 그리고 문자열을 이어붙여주는 concat함수 사용.
! 주의할 점

  • 동적할당 후에 첫 자리에 널문자를 삽입해줘야 한다. 그렇지 않으면 concat함수의 작동 원리상, 붙임을 당하는 str1 문자열의 널문자 위치 그 다음에 붙여줘야 하는데, 널문자가 없는 맨 처음에 concat이 일어나지 않고 오류가 발생한다. (sigabort)

Exercise 04 : ft_convert_base

ft_convert_base.c

#include <stdlib.h>

int	is_space(char c);
int	base_check(char *base);
int	find_base(char *base, char c);
int	nbr_len(int nbr, int len);

int	ft_atoi(char *str, char *base, int len)
{
	int	result;
	int	temp;

	result = 0;
	temp = 0;
	while (*str != '\0')
	{
		temp = find_base(base, *str++);
		if (temp < 0)
			break ;
		result = result * len + temp;
	}
	return (result);
}

int	ft_atoi_base(char *nbr, char *base)
{
	int	num;
	int	sign;
	int	len;

	num = 0;
	sign = 1;
	len = base_check(base);
	while (is_space(*nbr))
		nbr++;
	while (*nbr == '-' || *nbr == '+')
	{
		if (*nbr == '-')
			sign *= -1;
		nbr++;
	}
	num = ft_atoi(nbr, base, len);
	return (num * sign);
}

char	*ft_putnbr_base(int num, char *base, char *result, int len)
{
	int	i;
	int	temp;
	int	base_len;

	base_len = base_check(base);
	if (num == 0)
		result[0] = base[0];
	if (num < 0)
		result[0] = '-';
	i = len - 1;
	while (num != 0)
	{
		temp = num % base_len;
		if (temp < 0)
			temp *= -1;
		result[i] = base[temp];
		num /= base_len;
		i--;
	}
	result[len] = '\0';
	return (result);
}

char	*ft_convert_base(char *nbr, char *base_from, char *base_to)
{
	int		num;
	int		len;
	char	*result;

	if (base_check(base_from) < 2 || base_check(base_to) < 2)
		return (0);
	num = ft_atoi_base(nbr, base_from);
	len = nbr_len(num, base_check(base_to));
	result = (char *)malloc(sizeof(char) * (len + 1));
	if (result == 0)
		return (0);
	result = ft_putnbr_base(num, base_to, result, len);
	return (result);
}

ft_convert_base2.c

int	is_space(char c)
{
	char	*space;

	space = " \n\t\v\f\r";
	while (*space != '\0')
	{
		if (c == *space)
			return (1);
		space++;
	}
	return (0);
}

int	base_check(char *base)
{
	int	len;
	int	i;
	int	j;

	len = 0;
	i = 0;
	j = 0;
	while (base[len] != '\0')
	{
		if (is_space(base[len]) || base[len] == '-' || base[len] == '+')
			return (0);
		len++;
	}
	while (i < len - 1)
	{
		j = i + 1;
		while (j < len)
		{
			if (base[i] == base[j])
				return (0);
			j++;
		}
		i++;
	}
	return (len);
}

int	find_base(char *base, char c)
{
	int	i;

	i = 0;
	while (base[i] != '\0')
	{
		if (base[i] == c)
			return (i);
		i++;
	}
	return (-1);
}

int	nbr_len(int nbr, int len)
{
	int	cnt;

	cnt = 0;
	if (nbr == 0)
		return (1);
	if (nbr < 0)
		cnt++;
	while (nbr != 0)
	{
		cnt++;
		nbr /= len;
	}
	return (cnt);
}

base_from에 해당하는 진수인 nbr을 base_to에 해당하는 진수로 바꿔주는 함수
결국 전체 과정은 nbr(base_from) -> int -> nbr(base_to) 과정
즉 atoi_base 이후 putnbr_base를 해줘야 한다.
두 과정이 쓰는 함수가 비슷해서 파일 하나로 만드려고 고집부리다가 시간낭비함

두번째 파일에 기본적인 공백확인, base길이 및 적법성 확인, base에서 해당 index를 찾기, 마지막으로 putnbr_base할 시 필요한 문자열의 길이를 재는 nbr_len을 구현했다. nbr_len은 음수가 나올 경우 -부호를 넣어야 하므로 자리 하나를 더 추가했다.

그리고 복잡한 과정을 거치는 것 처럼 보이지만 앞서 말한 nbr에서 해당 진수의 index를 찾아 base의 길이를 곱하면서 int형으로 변환하고, 다시 변환할 base의 길이를 나누면서 그 갚의 인덱스를 결과값 배열에 뒷자리부터 추가한다.

Exercise 05 : ft_split

#include <stdlib.h>

int	is_sep(char c, char *sep)
{
	while (*sep != '\0')
	{
		if (*sep == c)
			return (1);
		sep++;
	}
	return (0);
}

char	*str_dup(char *str, char *charset)
{
	int		len;
	char	*dest;
	int		i;

	len = 0;
	while (str[len] != '\0' && !is_sep(str[len], charset))
		len++;
	dest = (char *)malloc(sizeof(char) * (len + 1));
	i = 0;
	while (i < len)
	{
		dest[i] = str[i];
		i++;
	}
	dest[i] = '\0';
	return (dest);
}

int	element_count(char *str, char *charset)
{
	int	cnt;

	cnt = 0;
	while (*str != '\0')
	{
		if (!is_sep(*str, charset))
		{
			cnt++;
			while (!is_sep(*str, charset) && *str != '\0')
				str++;
		}
		else
			str++;
	}
	return (cnt);
}

char	**ft_split(char *str, char *charset)
{
	int		num;
	int		i;
	char	**result;

	num = element_count(str, charset);
	result = (char **)malloc(sizeof(char *) * (num + 1));
	if (result == 0)
		return (0);
	i = 0;
	while (*str != '\0')
	{
		if (!is_sep(*str, charset))
		{
			result[i++] = str_dup(str, charset);
			while (!is_sep(*str, charset) && *str != '\0')
				str++;
		}
		else
			str++;
	}
	result[i] = 0;
	return (result);
}

구분자(charset)가 주어지면 해당 str을 그 구분자로 split해주는 함수
앞서 풀었던 strjoin의 반대방향이라고 생각하면 된다. 먼저 구분자를 제외한 element들의 개수를 세고, 그 개수 만큼의 문자열이 들어갈 배열, 즉 이중포인터 배열을 할당한다. 그 후 00번의 strdup와 비슷하게, 문자열의 끝이거나 구분자를 만났을 시 거기까지 복사해주는 함수를 사용한다. 마지막으로 element의 개수를 세는 방식과 유사한 방식으로 element가 나올때까지 포인터를 이동 후 복사하고, 또 나올때까지 이동하고... 를 반복한다.

1개의 댓글

comment-user-thumbnail
2023년 8월 1일

개발자로서 성장하는 데 큰 도움이 된 글이었습니다. 감사합니다.

답글 달기