일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- 서울42
- 와인선별방법
- 42 libft
- str함수
- get_next_line
- printf
- 알고리즘 기초
- 지베르니
- 지베르니 계절 추천
- 42
- 에꼴42
- 파리 피크닉
- pipex
- ft_printf
- push swap
- libft
- 이지젯
- 42 so_long
- ecole42
- so_long
- pipex 42
- get next line
- 굿노트 스티커
- 와인 고르기
- push swap 설명
- 포르투갈 여행
- gnl
- 지베르니 가을
- 42 pipex
- 지베르니 여름
- Today
- Total
뇌 마음 반반저장소
[42_libft] Part 1 (str 함수들2 strlcpy, strlcat) 본문
본격적으로 들어가기에 앞서 두 함수를 살펴보자. strlcpy와 strlcat는 굉장히 비슷하지만 조금 다른 함수이다. 그래서 매뉴얼에서도 같이 묶어서 설명한다. 한번 매뉴얼을 살펴보자.
Lb libbsd 라이브러리 포함 (이것은 나중에 내장 함수랑 비교해서 테스트할 때 컴파일이 안된다. 해결은 아래에.)
strlcpy strlcat - size-bounded string copying and concatenation
크기를 조정해 문자열 복사 및 연결.
The Fn strlcpy and Fn strlcat functions copy and concatenate strings respectively. They are designed to be safer, more consistent, and less error prone replacements for strncpy(3) and strncat(3). Unlike those functions, Fn strlcpy and Fn strlcat take the full size of the buffer (not just the length) and guarantee to NUL-terminate the result (as long as Fa size is larger than 0 or, in the case of Fn strlcat , as long as there is at least one byte free in Fa dst ). Note that a byte for the NUL should be included in Fa size. Also note that Fn strlcpy and Fn strlcat only operate on true ``C'' strings. This means that for Fn strlcpy Fa src must be NUL-terminated and for Fn strlcat both Fa src and Fa dst must be NUL-terminated.
strlcpy 함수 및 strlcat 함수는 각각 문자열을 복사하고 연결합니다.
오류가 발생하기 쉬운 strncpy(3) 및 strncat(3)의 경우 보다 안전하고 일관적이기 때문에 대체품으로 설계되었습니다.
strn들의 기능과 달리 strl은
1. 버퍼의 전체 크기(길이뿐만 아니라)를 가져와서
2. 결과를 NUL-종료하도록 보장합니다.
3. NUL의 바이트('\0')는 총 크기에 포함되어야 합니다.
strlcpy 및 strlcat는 "C" 문자열에서만 작동합니다.
즉, strlcpy src의 경우 NUL 종단 처리해야 하고
strlcat의 경우 src와 str 모두 NUL 종단 처리해야 합니다.
The strlcpy function copies up to size - 1 characters from the NUL-terminated string src to dst , NUL-terminating the result.
strlcpy 함수는 NUL 종료 문자열 src에서 dst로
(최대 크기 - 1자)를 복사하여 결과를 NUL 종료합니다.
The strlcat function appends the NUL-terminated string src to the end of dst. It will append at most size - strlen(dst) - 1 bytes, NUL-terminating the result.
strlcat 함수는 NUL 종료 문자열 src를 dst의 끝에 추가합니다.
(최대 크기 - strlen(dst) - 1바이트)를 추가하여 결과를 NUL로 종료합니다.
반환값 RETURN VALUES
The strlcpy and strlcat functions return the total length of the string they tried to create. For strlcpy that means the length of src. For strlcat that means the initial length of dst plus the length of src. While this may seem somewhat confusing, it was done to make truncation detection simple. Note, however, that if strlcat traverses size characters without finding a NUL, the length of the string is considered to be size and the destination string will not be NUL-terminated (since there was no space for the NUL). This keeps strlcat from running off the end of a string. In practice this should not happen (as it means that either size is incorrect or that dst is not a proper ``C'' string). The check exists to prevent potential security problems in incorrect code.
strlcpy 및 strlcat 함수는 생성하려는 문자열의 총 길이를 반환합니다.
strlcpy의 경우 src의 길이를 의미합니다.
strlcat의 경우 dst는 초기 길이에 src의 길이를 더한 값을 의미합니다.
이것은 다소 혼란스러워 보일 수 있지만 잘라내기 탐지를 단순화하기 위해 수행되었습니다.
strlcat가 NUL을 찾지 않고 크기 문자를 통과하는 경우, 문자열의 길이는 크기로 간주되고 대상 문자열은 NUL로 종료되지 않습니다(NUL을 위한 공간이 없었기 때문). 이렇게 하면 strlcat가 문자열 끝에서 실행되지 않습니다. 실제로는 이런 일이 발생해서는 안 됩니다(크기가 잘못되었거나 dst가 적절한 "C" 문자열이 아님을 의미하기 때문입니다). 이 검사는 잘못된 코드의 잠재적인 보안 문제를 방지하기 위해 존재합니다.
일단 strlcpy와 strlcat을 만들려면 둘의 차이를 확실하게 짚고 넘어가야겠다.
Cpy함수!
strlcpy가 반환하는 숫자는 src의 길이이다. 기본적으로 strlcpy는 src의 문자열을 dst에다가 복사! 하는 것이다. 예를 들어 흑맥주 dst와 블랑 맥주 src가 있다. strlcpy를 적용해 dst에다가 src를 size만큼 복사하면 아래와 같이 생각할 수 있다.
Cat 함수!
strlcat의 구조를 보려면 우리는 strcat이 어떻게 진화되었는지 보는 게 훨씬 이해가 빠르다.
strcat
일단 strcat을 보자. strcat은 strcpy처럼 붙여 넣기 하는 게 아니라 dst의 꽁무니에 src를 연결! 하는 것이다. src 맥주잔을 dst 맥주잔에 옮긴다. 그리고 nul을 붙인다.
하지만 cat함수들은 자리를 미리 만들어놓고 붙이는 함수가 아니라, 그냥 dst에 붙이는(따르는) 함수이기 때문에 dst의 공간이 충분하지 않다면 붙여지면 남은 메모리들이 넘치게 된다. 이런 현상을 오버플로우가 되었다고 한다.
위에서 보듯, dst의 크기는 정해져 있는데 둘이 합치면 공간이 없어 맥주가 넘치게 된다. (아까워ㅜㅜ)
코드를 돌려보면 돌아가긴 하는데"스택에 강한 스매시가 감지됐어! 안 해!"라고 뜬다. strcat만 쓰면 상관없겠지만 우리는 다양한 함수를 섞어서 사용하기 때문에 실전에서는 그냥 코드가 멈춰 버릴 것이다.
before -> src: Hello
before -> dst: World
strcat : WorldHello
after -> dst: WorldHello
*** stack smashing detected ***: terminated
Aborted
strncat
그래서 생긴 함수가 strncat이다. 뒤에 사이즈를 적어줘서 어느 만큼 붙일 건지 미리 알려주는 것이다. 이때 size에는 nul도 포함되어 있다.
src에 얼마나 있던 size만큼만 dst에 붙이는데 size값에는 nul값도 포함된 것을 잊지 말자! (맥주를 덜 나눌 수 있다ㅋ)
(strl*** 함수는 보완하기 위해 새로 생긴 함수이기 때문에 기존 표준 라이브러리에 포함되어있지 않다. 그래서 Lb libbsd 라이브러리를 참고해야 컴파일이 된다. 그래서 아래와 같이 헤더에 추가해 주고, 컴파일을 할 때도, 끝에 -lbsd를 포함해준다.)
#include <stdio.h>
#include <string.h>
int main(void)
{
char src[] = "Hello"; //붙이고 싶은 문자
char dst[] = "World"; //붙이고 싶은 곳
printf("before -> src: %s\n", src); //원래 src
printf("before -> dst: %s\n", dst); //원래 dst
printf("strncat : %s\n", strncat(dst, src, 5));
return 0;
}
dst[5]에 src[5]를 넘치게 붙였으니 결과는 아래와 같이 오류가 뜨며 나온다. (매.. 맥주가 넘치고 있어요..!)
$ gcc test_strncat.c
$ ./a.out
before -> src: Hello
before -> dst: World
strncat : WorldHello
*** stack smashing detected ***: terminated
Aborted
하지만 dst의 공간을 이렇게 여유 있게 준다면?
char dst[11] = "World";
아래와 같이 오류 없이 잘 나오게 된다. dst의 글자가 몇 글자이던 우리는 dst의 공간을 넉넉하게 주어야 한다. 그래야 코드가 멈추지 않는다!
$ gcc test_strncat.c
$ ./a.out
before -> src: Hello
before -> dst: World
strncat : WorldHello
그러면 strncat은 dst뒤에 src를 size만큼 붙이는 것이라는 걸 알았다.
strlcat
자 여기까지 잘 따라왔다면... 허리 한번 피고...! 드디어 strlcat을 살펴보자. 요놈이 이해가 안돼서 2일째 이것만 붙잡고 있는데.. 드디어 이해한 것 같다...!
이 함수는 흘러넘치는 오버플로우를 방지하기 위해서 만들어졌다. 그래서 메모리를 먼저 계산해서! 흘러넘치면 그냥 알아서 안 붙이고, 안 넘치고 들어가면 붙이는 함수인 것이다. (올.. 똘똘하군) 그래서 이 친구는 위의 함수들과 달리 먼저 계산해봐서 아닌 것 같으면 실행조차 하지 않는 것이다!
크게 이 함수는 성공과 실패가 있다고 쉽게 생각해 보자.
성공은 "메모리의 크기가 충분할 때", 실패는 "공간이 충분하지 않을 때"이다.
strlcat가 반환하는 숫자는 크게 두 가지로 나뉜다. 일단 성공부터 살펴보자.
1. 원하는 size가 dst의 길이보다 클 때 (size > dst의 길이)
: (src의 길이 + dst의 길이 + nul값)을 반환한다. dst는 바뀐다. 붙이기 성공.
이렇게 말하면 너무 헷갈리니까 아래의 코드로 예를 들면서 봐보자.
#include <stdio.h>
#include <bsd/string.h>
int main()
{
char src[] = "Hello";
char dst[100] = "World";
printf("before -> src: %s\n", src);
printf("before -> dst: %s\n", dst);
printf("strlcat : %ld\n", strlcat(dst, src, 10));
printf("after -> dst: %s\n", dst);
return (0);
}
나는 100짜리 dst의 공간에 있는 5개의 문자열 World뒤에
👉 src의 공간에 있는 5개의 문자열 Hello를 붙일 건데
👉 (size(10) - dst사이즈(5) - 1) a.k.a (4) 만큼만 붙여주세요!라는 뜻이다.
아래의 그림은 strlcat이 생각하는 dst와 src와 nul의 총합이다.
자 그러면 이제 결과를 보자.
size가 dst길이보다 크기 때문에 dst가 잘리지 않고 안전하게,
내가 원하는 src의 길이만큼 (size - dst길이 - nul자리 : 10 - 5 - 1) 잘 나온다.
$ gcc -o test2 test2.c -lbsd
$ ./test2
before -> src: Hello
before -> dst: World
strlcat : 10
after -> dst: WorldHell
그렇다면 dst의 사이즈인 5보다 큰 숫자를 넣으면 잘 붙어서 나올 것이다.
size가 6일 경우: (dst길이 5 보다 size가 6으로 더 크니 성공)
최종 사이즈는 10으로 잘 나오고,
5개의 dst를 붙이고, 0개의 src를 붙이고 나머지 1은 \0을 붙여줘서 총 6자리 완성.
strlcat : 10
after -> dst: World
size가 7일 경우: (dst길이 5 보다 size가 7로 더 크니 성공)
최종사이즈는 10으로 잘 나오고,
5개의 dst를 붙이고, 1개의 src를 붙이고 나머지 1은 \0을 붙여줘서 총 7자리 완성.
strlcat : 10
after -> dst: WorldH
size가 11일 경우: (dst길이 5 보다 size가 11로 더 크니 성공)
최종사이즈는 10으로 잘 나오고,
5개의 dst를 붙이고, 5개의 src를 붙이고 나머지 1은 \0을 붙여줘서 총 7자리 완성.
strlcat : 10
after -> dst: WorldHello
2. 원하는 size가 dst의 길이보다 작을 때 (size < dst의 길이)
: (src의 길이 + size)를 반환한다. dst는 바뀌지 않는다. 붙이기 실패.
#include <stdio.h>
#include <bsd/string.h>
int main()
{
char src[] = "Hello"; //붙이고 싶은 문자
char dst[100] = "World"; //붙이고 싶은 곳
printf("before -> src: %s\n", src); //원래 src
printf("before -> dst: %s\n", dst); //원래 dst
printf("strlcat : %ld\n", strlcat(dst, src, 3));
printf("after -> dst: %s\n", dst); //바뀐 dst
return (0);
}
size는 3으로 (dst 문자열의 길이 + src 문자열의 길이 + nul문자 길이 = 10) 보다 적다.
컴퓨터가 미리 계산을 해보니 3개밖에 안 되는 공간에 11개나 되는 문자열을 붙일 수 없으니까 GG 한다.
그래서 그냥 src문자열의 길이 [5]와 가지고 싶었던 공간의 size [3]을 더해서 반환해 준다.
그리고 붙이기는 실패한다!
$ gcc -o test2 test2.c -lbsd
$ ./test2
before -> src: Hello
before -> dst: World
strlcat : 8 #src + size
after -> dst: World #붙이기 실패
최종 변론(?)
size - dst길이 - 1(nul)가 0보다 커야 strlcat은 문자열을 붙여준다!
((size) - (dst_len) - 1 > 0)
작다면 그냥 src + size로 값을 반환!
이렇게 strlcat 함수는 붙일 수 있다면 붙여주고 메모리가 안되면 안 붙여주는 아주 안전한 친구인 것이다!
그러면 strlcat 함수를 내가 만들어서 libft에 넣어준다면 다른 특별한 헤더에서 불러올 필요 없이, -lbsd 명령어 없이도 불러와서 사용할 수 있다.
자 그럼 이제 다 이해했으니 함수 원형을 만들어 보자..
3-5. strlcpy
함수 목적
dst에 (size - 1)만큼 src를 붙여주고 마지막은 '\0'을 붙인다.
함수 선언 원형
#include <string.h>
size_t strlcpy(char *dst, const char *src, size_t dstsize);
함수 원형 구현 과정
1. dstsize만큼 src의 부분을 가져와서
2. dst에 붙인다.
3. 마지막엔 '\0'을 붙여준다.
4. 리턴 값은 src의 길이이다.
!테스트 결과!
copy src to dst
dst : hello #dst의 문자열
src : coucou world #src의 문자열
how many words do you wanna copy? : 4 #붙이고 싶은 숫자
strlcpy : 12 #src의 길이
result of strlcpy : cou #src의 길이 3 + nul 1 의 결과
ft_strlcpy : 12 #내가 구현한 함수의 결과
result of ft_strlcpy : cou
3-6. strlcat
함수 목적
1. (size <= dst의 길이)라면 : 실패 -> (src길이 + size길이) 반환
2. (size > dst의 길이) : 성공
size - dst의 길이 - nul자리를 빼준 만큼 src를 붙인다.
-> (src길이 + dst길이) 반환
함수 선언 원형
#include <string.h>
size_t strlcat(char *dst, const char *src, size_t dstsize);
!테스트 결과!
(size = dst의 길이) 일 때 ft_strlcat(dst, src, 5) : 붙이기 실패
before -> src: Hello
before -> dst: World
strlcat : 10
after -> dst: World
ft_strlcat : 10
after -> dst: World
(size < dst의 길이) 일때 ft_strlcat(dst, src, 3) : 붙이기 실패
before -> src: Hello
before -> dst: World
strlcat : 8
after -> dst: World
ft_strlcat : 8
after -> dst: World
(size > dst의 길이) 일때 ft_strlcat(dst, src, 8) : 붙이기 성공
before -> src: Hello
before -> dst: World
strlcat : 10
after -> dst: WorldHe
ft_strlcat : 10
after -> dst: WorldHe
(size > dst의 길이) 일때 ft_strlcat(dst, src, 13) : 붙이기 성공
before -> src: Hello
before -> dst: World
strlcat : 10
after -> dst: WorldHello
ft_strlcat : 10
after -> dst: WorldHello
도움을 주고 싶으신 내용이나
틀린 내용이 있다면 댓글로 남겨주시고,
참고하신다면 꼭 출처를 밝혀주세요!
도움이 되었다면 공감 한 번씩 부탁드립니다❤️
'42 > libft' 카테고리의 다른 글
[42_libft] Part 1 (mem 함수들) (1) | 2022.12.09 |
---|---|
[42_libft] Part 1 (str 함수들2 strnstr, strdup, atoi) (0) | 2022.12.08 |
[42_libft] Part 1 (str 함수들1 strlen, strchr, strrchr, strncmp) (0) | 2022.12.06 |
[42_libft] Part 1 (is와 to 함수들) (1) | 2022.12.06 |
[42_libft] 시작하며 (0) | 2022.11.27 |