Libft - Part 2
My first own library
🚀 protect vs. not-protect
part2에서 구현해야 하는 함수들은 특별히 원 함수가 있는 것이 아니라 특정한 기능을 수행하는 나만의 함수들을 구현하는 것이기 때문에 에러 입력에 관한 처리도 구현하는 사람 취향에 맞게 구현하면 된다.
이게 일반적인 용어인지, 아니면 Libft 테스터인 unit-test에서 임의로 만든 개념인지 모르겠지만(null protected function이라고 검색해 봤을 때 특별히 나오는 결과가 없어 보임) 포인터를 매개변수로 받는 함수에서 NULL포인터가 들어왔을 때 처리하는 방법에 따라서 protect, not-protect로 구분한다는 것 같다.
NULL이 매개변수로 들어왔을 때 segment fault, bus error… 등의 에러를 뱉어내면 not-protect된 함수NULL이 매개변수로 들어왔을 때NULL을 반환한다던가 해서 비정상적인 종료를 막으면 protect 된 함수
처음에는 비정상적인 입력이 들어오면 함수를 비정상적으로 종료해서 사용자에게 이 입력이 비정상적임을 알리고 함수 진행을 중단해야 한다고 생각해서 protect를 안하는 쪽으로 part 2의 함수들을 구현했었는데 잘못된 입력이 들어왔을때 함수 실행을 완전히 중단해 버렸을 때 오는 피해가 잘못된 입력에서 오는 피해보다 더 클 수도 있다는 다른 분의 말씀에 설득되어 버려서…ㅎㅎ 다시 protect를 하는 쪽으로 모두 수정했다.
이 부분은 디펜스를 잘 할 수만 있다면 자신이 원하는 방향으로 구현을 하면 될 것 같다..!
🚀 ft_substr
char *ft_substr(char const *s, unsigned int start, size_t len);s문자열의 start인덱스에서부터 최대 len바이트를 뽑아서(?) 새로운 부분 문자열을 만들고, 그 문자열의 시작 주소 반환하기
✨ 예외 처리
s가
NULL일 경우처리할 수 있는 문자열이 없으므로
NULL을 반환한다.s의 길이가 start보다 작거나 같은 경우 || len == 0 인 경우
처리는 가능하지만 유의미한 문자열을 생성할 수는 없기 때문에 빈 문자열을 반환한다.
malloc이 실패한 경우
NULL을 반환한다.
✨ 함수 진행 과정
- 예외사항 (1, 2) 처리
slen(s 문자열의 길이)를 구해준 후에min(len, slen - start) + 1만큼의 메모리 할당s 문자열이 끝나기 전 ||복사한 문자의 개수가 len개 이하까지 문자 복사해 넣기- 마지막 인덱스에
NULL문자 넣어주기
✨ 구현 시 고려사항
min(len, slen - start)의 이유len이 부분문자열의 최대길이이기 때문이다.
만약 생성할 수 있는 문자열의 길이가 len보다 작은 경우에는 그만큼만 할당해서 문자열을 생성해야 한다.
사실 비교하지 않고 넉넉하게 len만큼만 할당해줘도 통과하는 것 같긴 하던데 나는 뭔가 여유롭게 할당해주는게 조금 신경쓰여서 추가해줬다.
s문자열의 내용을 새로운 문자열에 복사하는 형태이므로
ft_strlcpy를 활용하면 보다 깔끔한 코드를 짤 수 있을 것 같다는 생각을 했다.
🚀 ft_strjoin
char *ft_strjoin(char const *s1, char const *s2);s1 문자열 뒤에 s2 문자열을 붙인 형태의 문자열을 생성해서 반환한다.
✨ 예외 처리
s1과 s2 모두
NULL일 경우처리할 수 있는 문자열이 없으므로
NULL을 반환한다.s1만
NULL인 경우s2만 복제해서 반환한다.
s2만
NULL인 경우s1만 복제해서 반환한다.
malloc 실패한 경우
NULL을 반환한다.
✨ 함수 진행 과정
- 예외 상황 (1, 2, 3) 처리
- s1과 s2의 길이를 잰다. (with
ft_strlen) - (s1의 길이 + s2의 길이 + 1)만큼의 메모리를 할당해 준다.
- 할당한 메모리에 s1을 복사한다. (with
ft_strlcpy) - 그 뒤에 s2를 붙인다. (with
ft_strlcat)
✨ 구현 시 고려사항
서브젝트에서도 언급되었듯이 part 1에서 구현해 둔 함수를 part 2에서 활용할 수 있다.
문자열을 복사하는 과정은
ft_strlcpy로 대체할 수 있고 문자열을 붙이는 과정은ft_strlcat으로 대체할 수 있다.평가를 다니다 보면 코드가 길어져서 가독성이 떨어진다는 고민을 하는 동료들이 많았는데 과제의 목적을 잘 기억해서 중복되는 코드를 작성하지 않게 하면 보다 효율적인 코드를 작성할 수 있을 것 같다.
ft_strlcat과ft_strlcpy의 마지막 매개변수에 주의하자.ft_strlcat의 마지막 매개변수는 함수가 반환할 문자열의 길이 + 1 (NULL문자 크기) 이다. 따라서 (s1의 길이 + s2의 길이 + 1)을 매개변수로 전달해주었다.ft_strlcpy의 마지막 매개변수는 복사할 문자의 개수 + 1 (NULL문자 크기) ㄴ이다. 따라서 (s1의 길이 + 1)을 매개변수로 전달해주었다.
🚀 ft_strtrim
char *ft_strtrim(char const *s1, char const *set);s1의 앞, 뒤에서 set에 포함되어 있는 문자들을 모두 제거한 형태의 문자열을 생성하여 반환한다.
예) s1: “aabcdabdcaab” set: “ab” => “cdbabdc”
✨ 예외 처리
s1이
NULL일 경우처리할 문자열이 없으므로
NULL을 반환한다.set이
NULL일 경우제거할 문자가 없으므로 s1을 복제하여 반환한다
s1의 모든 문자가 set에 포함된 경우
s1의 모든 문자가 제거되므로 빈 문자열을 반환한다.
malloc 실패할 경우
NULL을 반환한다.
✨ 함수 진행 과정
- 예외 상황 (1, 2) 처리
- s1 문자열의 시작과 끝으로 시작 포인터 (start), 끝 포인터 (end) 설정
- 문자열의 앞에서 set에 포함된 문자가 등장하지 않을 때 까지 start 이동시키기
- 예외 상황 (3) 처리
- 문자열의 뒤에서 set에 포함된 문자가 등장하지 않을 때 까지 end 이동시키기
- 문자열의 길이 계산하고 (
len = end - start + 1) 메모리 할당하기 - 할당한 공간에 start부터 len만큼의 문자 복사하기 (with
ft_strlcpy)
✨ 구현 시 고려사항
모든 문자가 삭제되는 상황 파악하기
처음에는 start > end 인 상황에서 모든 문자가 삭제됨을 확인하고 빈 문자열을 반환하는 방법을 했었는데 실제로 모든 문자가 삭제되는 상황에서 자꾸 abort가 떠서 원인을 찾아보았더니 문제는 end를 움직이는 상황에서 end를 포인터를 이용해서 움직이다보니 end가 문장 끝(문장의 시작부분)을 넘어가는 상황에서 반복을 멈추지 못하는 것이었다.
길이를 먼저 재서 길이를 줄여가면서 모든 문자가 삭제되었는지 확인해보는 등 여러가지 방법을 생각해보긴 했었는데 start를 움직이는데 있어서는 문제가 없었기 때문에 그냥 start를 움직인 다음에 모든 문자가 삭제되었는지 (start가
NULL에 도달했는지)를 확인해 본 후에 end를 움직이는 것이 가장 간단하게 확인할 수 있는 방법이었다.함수의 기능을 이해하는데 어려움이 있었다. set에 포함된 문자들을 모두 s1에서 제거하는 것이 아니라 앞 뒤에 있는 문자들 (set에 포함되어 있지 않은 문자 안쪽에 있는 문자는 고려할 필요가 없음)만 제거하면 되는 것이다. (위에 적어둔 예시 참고)
🚀 ft_split
char **ft_split(char const *s, char c);
s 문자열을 c 문자열을 기준으로 나누어서 만들어진 문자열의 배열을 반환하기.
✨ 예외 처리
s가
NULL인 경우NULL을 반환한다.문자열 배열의 메모리 할당이 실패한 경우
NULL을 반환한다.문자열의 메모리 할당이 실패한 경우
그동안 할당해 준 메모리들을 모두 해제해 준 다음에
NULL을 반환한다. (all_free함수)
✨ 함수 진행 과정
- 예외 상황(1) 처리
- 만들어질 문자열의 개수를 세어준다 (
get_cnt_strs) 3. s에서 c가 등장하지 않을 때까지 인덱스를 이동시킨다. 4. c가 아닌 문자가 등장한 경우 cnt를 늘려주고 c가 등장하지 않을 때까지 인덱스를 이동시킨다. (한 문자열) 5. c가 등장한 경우 인덱스를 1 증가시킨다. - (문자열의 개수 + 1) _ sizeof(char _) 만큼의 메모리를 할당한다. (예외 상황 (2) 처리)
- 문자열의 개수만큼 문자열을 생성하여 문자열 배열에 넣어준다. (with
get_str) 8. 문자열의 시작 포인터와 문자열의 길이를 전달하여 새로운 문자열의 시작 포인터와 문자열의 길이를 반환받는다. 9. 예외상황 (3) 처리 10. 문자열을 붙여넣는다. (withft_strlcpy) - 배열의 마지막은
NULL로 마무리한다.
✨ 구현 시 고려사항
피신 때도 비슷한 문제가 있었는데 그때 코드를 활용하니 함수 줄 제한을 지키는 데에 어려움이 있어서 다른 분의 코드를 참고했다. 어떤 곳에서 함수를 정의해주느냐가 관건인 것 같다.
🚀 ft_itoa
char *ft_itoa(int n);int로 주어지는 숫자를 문자열로 변환해준다.
✨ 예외 처리
malloc이 실패할 경우
NULL을 반환한다.
✨ 코드 진행 과정
- 부호 판단하기
- 절댓값 계산하기
- 문자열의 길이 계산하기
- 뒤에서부터 차례로 숫자 넣어주기 (10으로 나눈 나머지)
- 음수일 경우 인덱스 0 자리에 - 넣어주고 양수일 경우에는 나머지 숫자 넣어주기
✨ 구현 시 고려사항
int형 변수들을 처리할 때는 음수 최댓값의 절댓값이 양수 최댓값보다 크다는 사실을 꼭 기억하고 있어야 한다.
음수 최댓값이 양수 최댓값의 범위를 벗어나는 것을 고려해서 절댓값은 unsigned int 형으로 저장했다.
문자열의 길이를 생각할 때에 음수는 앞에 ’-‘가 들어가야 하기 때문에 문자열의 길이가 1부터 시작한다는 점을 기억해두어야 한다.
🚀 ft_strmapi
char *ft_strmapi(char const *s, char (*f)(unsigned int, char));s 문자열의 각각의 문자에 함수 포인터로 주어지는 함수 f를 적용한 새로운 문자열을 반환하기
✨ 예외 처리
s 또는 f가
NULL인 경우문자열을 처리 할 방법이 없으므로
NULL을 반환한다.malloc 실패한 경우
NULL을 반환한다.
✨ 코드 진행 과정
- 예외 상황 (1) 처리
- (문자열 길이 + 1)만큼 메모리 할당 (예외 상황 (2) 처리)
- 문자열의 문자 각각에 f 적용해주기
- 문자열 끝에
NULL종료해주기
✨ 구현 시 고려사항
- f의 매개변수에 대한 자세한 설명이 없어서 unsigned int 로 받는 매개변수 i가 무슨 의미인지 궁금했는데 결국에는 다른 분들의 코드를 보고 인덱스에 해당하는 것이구나.. 하는 것을 알게 되었다.
🚀 ft_putchar_fd
void ft_putchar_fd(char c, int fd);fd 로 주어지는 파일에 문자 c 출력하기
✨ 예외 처리
fd가 음수일 경우
출력하지 않고 반환한다.
✨ 구현 시 고려사항
fd가 음수일 경우를 고려해 주어야 한다.
파일 디스크립터는 모두 0 또는 양의 정수인줄 알았는데 파일 open이 정상적으로 되지 않았을 경우에는 파일 디스크립터가 음수도 나올 수 있다고 한다. fd가 음수일 경우에는 write에서 오류가 생기므로 (쓸 파일이 정상적으로 열리지 않았기 때문에) 출력하지 않고 그대로 반환해주어야 한다.
🚀 ft_putstr_fd
void ft_putstr_fd(char *s, int fd);fd로 주어지는 파일에 문자열 s 출력하기
✨ 예외 처리
s가
NULL이거나 fd가 음수인 경우출력이 불가능하므로 그냥 반환한다.
🚀 ft_putendl_fd
void ft_putendl_fd(char *s, int fd);fd 로 주어지는 파일에 문자열 s 를 출력하고 개행 출력하기
✨ 예외 처리
s가
NULL이거나 fd가 음수인 경우출력이 불가능하므로 그냥 반환한다.
🚀 ft_putnbr_fd
void ft_putnbr_fd(int n, int fd);fd로 주어지는 파일에 int n 출력하기
✨ 예외 처리
fd가 음수일 경우
출력하지 않고 반환한다.
✨ 구현 시 고려사항
- itoa를 사용하면 좀 더 깔끔한 코드를 짤 수 있었을 것 같은데 허용 함수에 free가 없어서 메모리 leak 발생으로 itoa를 사용할 수 없었다… 하하