일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- 와인 고르기
- 지베르니 계절 추천
- push swap 설명
- 파리 피크닉
- push swap
- gnl
- 지베르니 가을
- 에꼴42
- 42 so_long
- printf
- get next line
- pipex 42
- 지베르니
- 와인선별방법
- str함수
- pipex
- 이지젯
- 굿노트 스티커
- 알고리즘 기초
- so_long
- 서울42
- 42 libft
- libft
- 42 pipex
- get_next_line
- ecole42
- 42
- 지베르니 여름
- 포르투갈 여행
- ft_printf
- Today
- Total
뇌 마음 반반저장소
[42_libft] Part 1 (mem 함수들) 본문
*mem함수의 흥미로운 점!
mem함수는 대부분 void로 들어가고 리턴 값이 void이다. 그런데 또 구현 함수 내부에서는 void가 unsigned char로 바꿔서 구현된다. 왜 그럴까? (무슨 말인지 모르겠다면 밑의 함수를 조금 구현하고 돌아와 보자.)
찾다가 어떤 블로그에서 아주 기가막히게 설명한 것을 발견했다.
일단 mem함수와 str의 함수는 크게 차이가 없는 것 같지만, str은 문자열을 만져주는 함수이기 때문에 '\0'를 종료시점으로 함수를 수행한다. 하지만 mem함수는 숫자, 구조체, 문자열등에 다양하게 사용하는 메모리를 만져주는 함수이며 size를 통해서 종료시점을 알아낸다. 그래서 size가 필수적!
그렇다면 mem은 왜 unsigned char의 형태로 바꾸어 사용할까?
아래의 대화내용을 읽어보자.
쓰니
책을 읽다가 예문에 대한 코드에 대해서 궁금한 것이 생겼습니다
아래 코드를 보면 일반 포인터로 넘겨받은 포인터를 대상체의 값에 접근하기 위해서
새로 정의한 unsigned char형의 포인터에 주소를 복사 후 이것을 이용해서 접근하는데요
책의 설명에는 메모리에 접근할때에는 signed char형이 아닌 unsigned char형을 사용해야 한다는데
그 이유가 나와있지 않아서 궁금증을 일으킵니다.(부들부들! 궁금하다 궁금해!)
답글
unsigned char는 모든 bit 를 투명하게 볼 수 있는 특성을 제공합니다. 즉, 다른 type 은 내부 비트의 일부를 값을 표현하기 위한 용도가 아닌 다른 용도로 사용할 수 있으나 unsigned char 는 이것이 허락되지 않습니다.
따라서, 임의의 메모리에 바이트 단위로 접근해 값을 다룰 때에는 반드시 unsigned char를 사용해야 full portability를 얻을 수 있는 것입니다. 또한, 그와 같은 이유로 signed char 가 표현할 수 있는 값의 개수보다 unsigned char 가 표현할 수 있는 값의 개수가 많다는 사실에도 유의할 필요가 있습니다. signed char <-> unsigned char 사이의 값 변환이 1:1로 이루어질 수 "없는" 경우도 있음을 의미합니다.이와 같은 이유로, 표준이 바이트 값에 접근해야 하는 경우나 문자에 접근해야 하는 경우 (예: mem*(), str*() 함수들) 에는 모두 unsigned char로 접근하도록 요구하고 있습니다. 책에서는 memcpy 함수를 흉내 내는 것이기 때문에 그런 사항을 반영해 unsigned char를 사용했습니다. 예전에 이곳에서도 비슷한 논의가 있었던 것으로 기억합니다만, 꼭 padding
bit 가 아니어도 null character의 정의에 의해 문제가 될 수 있는 경우가 있습니다. signed char 는 1s' complement 표현이나 sign-magnitude 표현에서 -0 을 가질 수도 있습니다 - 이 경우 sign bit 가 1 이 됩니다. 하지만, null character 의 정의는 (sign bit 포함해) 모든 비트가 0 이길 요구하고 있습니다. 즉, -0 은 null character 가 될 수 없는 것입니다. 따라서 이를 올바르게 구분해내기 위해서는 unsigned char로 접근해야 하는 것입니다.p.s. 제 기억으로는 최소한 padding bit에 대한 내용, unsigned char 가
padding bit를 갖지 않는다는 내용은 책에서 다룬 것으로
기억합니다만...
그리고 등장한 매뉴얼
C99 7.21.1 String function conventions
3 For all functions in this subclause, each character shall be interpreted as if it had the type unsigned char (and therefore every possible object representation is valid and has a different value).
C997.21.1 문자열 함수 규칙입니다.
이 하위 절의 모든 기능에 대해, 각 문자는 부호 없는 문자 유형을 가진 것처럼 해석되어야 합니다(따라서 가능한 모든 개체 표현은 유효하고 다른 값을 가집니다).
한마디로 정리하면 가능한 모든 개체 표현을 유효하게 하기 위해서 unsinged char을 쓴다는 점!
출처: 메모리에 접근할 때는 signed char을 사용할 수 없다!?
패딩 바이트 설명: [42] 왕초보의 구조체(struct) 뿌시기 : *흥미로운 점 : 구조체와 변수들.. 데이터들은 어디에 저장이 되는 것일까?)
12. memset
memset이란?
memset - fill memory with a constant byte
메모리를 상수 바이트(이미 정해진 값)로 채우기
The memset() function fills the first n bytes of the memory area pointed to by s with the constant byte c.
memset() 함수는 s가 가리키는 메모리 영역의 처음 n바이트를 상수 바이트 c로 채웁니다.
반환 값 RETURN VALUE
The memset() function returns a pointer to the memory area s.
memset() 함수는 메모리 영역에 대한 포인터를 반환합니다.
memset 함수를 만들어 놓으면 while문을 돌려서 값을 채우는 것보다 훨씬 빠르게 사용할 수 있다!
함수 선언 원형
#include <string.h>
void *memset(void *s, int c, size_t n);
! 테스트 결과!
$ ./main.out
hello world
cover 6 first place wirh 1
memset : 0x7ffde09e2a50
111111world
ft_memset : 0x7ffde09e2a5c
1111111orld
13. bzero
bzero란?
bzero, explicit_bzero - zero a byte string
바이트 문자열을 0으로 모두 바꾸기.
The bzero() function erases the data in the n bytes of the memory starting at the location pointed to by s, by writing zeros (bytes containing '\0') to that area.
bzero() 함수는 s가 가리키는 위치에서 시작하는 메모리의 n바이트에 있는 데이터를 해당 영역에 0('\0'을 포함하는 바이트)을 써서 지웁니다.
반환 값은 없다..!
man에서 요즘은 안 쓴다고 쓰지 마라 한다.
함수 선언 원형
#include <strings.h>
void *bzero(void *s, size_t n);
!테스트 결과!
-> 반환되는 값이 없이 텅 빈 화면이 나온다 @_@ (그대는 지우개..)
14. memcpy
memcpy란?
memcpy - copy memory area
메모리 영역을 복사
The memcpy() function copies n bytes from memory area src to memory area dest. The memory areas must not overlap. Use memmove(3) if the memory areas do overlap.
memcpy() 함수는 n바이트를 메모리 영역 src에서 메모리 영역 대상으로 복사합니다. 메모리 영역이 겹치지 않아야 합니다. 메모리 영역이 겹칠 경우 memmove(3)를 사용합니다.
반환 값 RETURN VALUE
The memcpy() function returns a pointer to dest.
dest포인터를 반환합니다.
함수 선언 원형
#include <string.h>
void *memcpy(void *dest, const void *src, size_t n);
!테스트 결과!
$ ./main.out
dst : love world
src : copy
memset : copy world
ft_memset : copy world
15. memmove
memmove란?
memmove - copy memory area
The memmove() function copies n bytes from memory area src to memory area dest. The memory areas may overlap: copying takes place as though the bytes in src are first copied into a temporary array that does not overlap src or dest, and the bytes are then copied from the temporary array to dest.
memove() 함수는 n바이트를 메모리 영역 src에서 메모리 영역 대상으로 복사합니다. 메모리 영역이 겹칠 수 있습니다. 복사는 마치 src의 바이트가 src 또는 dest와 겹치지 않는 임시 배열로 먼저 복사된 다음 바이트가 임시 배열에서 dest로 복사되는 것처럼 수행됩니다.
반환 값 RETURN VALUE
The memmove() function returns a pointer to dest.
dest포인터를 반환합니다.
memcpy와 memmove의 차이는 무엇일까?
위의 memcpy의 man을 보면 "메모리 영역이 겹치지 않아야 합니다. 메모리 영역이 겹칠 경우 memmove(3)를 사용합니다."라고 쓰여있다. 이 말인즉슨 dst와 src가 같은 영역에 있어서는 안 되고, 겹 칠경 우 대안 없이 그냥 가져다 붙인다는 뜻이다. 실제로는 메모리의 번지가 인접해 겹치면서 문제가 생긴다고 한다. 이러한 경우는 대부분 dst의 사이즈가 클 경우 많이 생긴다.
이를 보완한 함수가 바로 memmove. 여기서는 메모리가 겹쳐도 잘 붙여진다. 왜 잘 붙여질까?
그 이유는 만약 dst의 크기가 src의 크기보다 크다면 가장 마지막 메모리부터 붙여주기 때문이다!
함수 선언 원형
#include <string.h>
void *memmove(void *dest, const void *src, size_t n);
!테스트 결과!
$ ./main.out
before src: 123456789
after src: 1123456789
16. memchr
memchr란?
memchr, memrchr, rawmemchr - scan memory for a character
The memchr() function scans the initial n bytes of the memory area pointed to by s for the first instance of c. Both c and the bytes of the memory area pointed to by s are interpreted as unsigned char.
memchr() 함수는 s가 가리키는 메모리 영역의 초기 n바이트를 c의 첫 번째 인스턴스에 대해 스캔합니다. c와 s가 가리키는 메모리 영역의 바이트는 모두 부호 없는 문자로 해석됩니다.
반환 값 RETURN VALUE
The memchr() and memrchr() functions return a pointer to the matching byte or NULL if the character does not occur in the given memory area.
memchr() 및 memrchr() 함수는 문자가 지정된 메모리 영역에서 발생하지 않는 경우 일치하는 바이트 또는 NULL로 포인터를 반환합니다.
이 함수는 s에서 n번째 구간 안에 처음으로 나타나는 c를 찾아주는 함수이다.
함수 선언 원형
#include <string.h>
void *memchr(const void *s, int c, size_t n);
!테스트 결과!
메인 구현
#include "libft.h"
#include <stdio.h>
#include <ctype.h>
#include <string.h>
int main()
{
char src[] = "123456789";
printf("before src: %s\n", src);
printf("memchr src: %s\n", (char *)memchr(src, '4', 10));
printf("ft_after src: %s\n", (char *)ft_memchr(src, '4', 10));
return (0);
}
결과는?
$ ./main.out
before src: 123456789
memchr src: 456789
ft_after src: 456789
17. memcmp
memcmp란?
memcmp - compare memory areas
메모리 비교하기
The memcmp() function compares the first n bytes (each interpreted as unsigned char) of the memory areas s1 and s2.
memcmp() 함수는 메모리 영역 s1과 s2의 처음 n바이트(각각 부호 없는 문자로 해석됨)를 비교합니다.
반환 값
The memcmp() function returns an integer less than, equal to, or greater than zero if the first n bytes of s1 is found, respectively, to be less than, to match, or be greater than the first n bytes of s2. For a nonzero return value, the sign is determined by the sign of the difference between the first pair of bytes (interpreted as unsigned char) that differ in s1 and s2. If n is zero, the return value is zero.
memcmp() 함수는 s1의 첫 번째 n바이트가 s2의 첫 번째 n바이트보다 작거나 같거나 큰 경우 0보다 큰 정수를 반환합니다. 0이 아닌 반환 값의 경우 부호는 s1과 s2에서 다른 첫 번째 바이트 쌍(부호 없는 문자로 해석됨) 간의 차이의 부호에 의해 결정됩니다. n이 0이면 반환 값은 0입니다.
s1 이 가리키는 처음부터 n 바이트 까지의 데이터와
s2 가 가리키는 처음부터 n 바이트 까지의 데이터를 비교하여
이들이 같다면 0을 리턴하고
다르다면 0 이 아닌 값을 리턴한다!
함수 선언 원형
#include <string.h>
int memcmp(const void *s1, const void *s2, size_t n);
!테스트 결과!
메인 구현
#include "libft.h"
#include <stdio.h>
#include <ctype.h>
#include <string.h>
int main()
{
char s1[] = "123456789";
char s2[] = "123455789";
printf("before src: %s\n", s1);
printf("before src: %s\n", s2);
printf("memcmp src: %d\n", memcmp(s1, s2, 5));
printf("ft_after src: %d\n", ft_memcmp(s1, s2, 5));
return (0);
}
결과!
$ ./main.out
before src: 123456789
before src: 123455789
memcmp src: 0 #5번째 바이트 안에서 찾을때
ft_after src: 0
$ ./main.out
before src: 123456789
before src: 123455789
memcmp src: 1 #6번째 바이트 안에서 찾을때
ft_after src: 1
22. calloc
calloc이란?
calloc — a memory allocator
\The calloc() function allocates memory for an array of nmemb elements of size bytes each and returns a pointer to the allocated memory. The memory is set to zero. If nmemb or size is 0, then calloc() returns either NULL, or a unique pointer value that can later be successfully passed to free().
calloc() 함수는 크기가 각각 바이트인 nmemb 요소 배열에 메모리를 할당하고 할당된 메모리로 포인터를 반환합니다. 메모리가 0으로 설정됩니다. nmemb 또는 size가 0이면 calloc()은 NULL을 반환하거나 나중에 free()로 성공적으로 전달할 수 있는 고유 포인터 값을 반환합니다.
반환 값
The malloc() and calloc() functions return a pointer to the allocated memory that is suitably aligned for any kind of variable. On error, these functions return NULL. NULL may also be returned by a successful call to malloc() with a size of zero, or by a successful call to calloc() with nmemb or size equal to zero.
malloc() 및 calloc() 함수는 할당된 메모리에 대한 포인터를 반환하여 모든 변수에 적합하게 정렬합니다.
오류가 발생하면 이러한 함수는 NULL을 반환합니다. NULL은 크기가 0인 malloc()에 성공적으로 호출하거나 nmemb 또는 크기가 0인 calloc()에 성공적으로 호출하여 반환될 수도 있습니다.
malloc은 앞 포스팅에서 개념을 짚고 넘어간 적이 있다.
malloc과 calloc의 가장 큰 차이점은
👉malloc은 메모리 할당만 해준다. (메모리 터치 안 함!)
👉calloc은 메모리 할당과 동시에 0으로 다 채워서 초기화해준다. (메모리를 초기화해 줌!)
참 쉽죠?
둘은 이렇게 사용된다! arr라는 변수 포인터에 5개 저장할 만큼 공간을 할당한다면?
arr = (char *)malloc(sizeof(char) * 5);
arr = (char *)calloc(5, sizeof(char));
함수 선언 원형
#include <stdlib.h>
void *calloc(size_t nmemb, size_t size);
도움을 주고 싶으신 내용이나
틀린 내용이 있다면 댓글로 남겨주시고,
참고하신다면 꼭 출처를 밝혀주세요!
도움이 되었다면 공감 한 번씩 부탁드립니다❤️
'42 > libft' 카테고리의 다른 글
[42_libft] Part 2 (strtrim) (0) | 2022.12.11 |
---|---|
[42_libft] Part 2 (substr, strjoin) (0) | 2022.12.11 |
[42_libft] Part 1 (str 함수들2 strnstr, strdup, atoi) (0) | 2022.12.08 |
[42_libft] Part 1 (str 함수들2 strlcpy, strlcat) (1) | 2022.12.07 |
[42_libft] Part 1 (str 함수들1 strlen, strchr, strrchr, strncmp) (0) | 2022.12.06 |