memset, bzero, memcpy, memccpy, memmove, memchr, memcmp

개요

memory의 간략한 구조와 메모리 관련 함수를 살펴보겠습니다.


메모리 구조


.code 영역


소수의 예외도 있지만 모든 프로그램은 main문이 있어야 실행이 가능하다.

.code영역은 실행할 프로그램의 main문 안의 코드들을 저장한다.



.data 영역


main문을 제외하고의 코드를 의미한다. 전역변수를 말하는 것이며

static을 사용하여 정적 변수로 선언해도 같다.



.stack 영역


흔히 말하는 스택 프레임에 해당 하는 영역이다.

프로그램이 함수 호출 같은 때에 자동으로 사용하는 임시 저장공간이라고 생각하면 편하다.

함수가 호출되면 ret주소를 저장하고 base pointer와 stack pointer를 설정하며

함수의 코드에 따라 스택 프레임 안에서 실행한 다음 저장했던 리턴주소로 pop한다.

자세한건 어셈블리언어 포스팅에서 살펴보겠다.



.heap 영역


사용자가 malloc을 이용하여 원하는 만큼 할당하여 사용할 수 있는 영역이다.




memset


SYNOPSIS

  • 함수 원형 : void *memset(void *b, int c, size_t len)
  • 함수 라이브러리 : #include
  • void *b : 세팅할 메모리의 주소
  • int c : 세팅할 문자
  • size_t len : 세팅할 길이
  • 리턴값 : 세팅한 메모리의 첫번째 주소


DESCRIPTION

이 함수는 1바이트 단위로 세팅하기 때문에 c로 들어온 4바이트 중에서 위에 3바이트는 고려하지 않는다.

0같은 경우는 1바이트를 0으로 넣든 4바이트를 0으로 넣든 상관이없기 때문에 작동이 잘된다.

파라메터로 들어온 메모리의 주소부터 len으로 들어온 길이만큼 들어온 문자로 초기화


memset CODE

1
2
3
4
5
6
void	*ft_memset(void *b, int c, size_t len)
{
	while (len--)
		*((unsigned char *)b + len) = (unsigned char)c;
	return (b);
}

size_t<stdlib.h> 헤더를 포함하면 사용할 수 있는 자료형이다.

이론상 가장 큰 사이즈를 담을 수 있는 unsigned 데이터 타입으로 정의할 수 있습니다.

32비트에서는 32비트 unsigned 정수형(int가 아님) 64비트에서는 64비트 unsigned 정수형이다.




bzero


SYNOPSIS

  • 함수 원형 : void bzero(void *s, size_t n)
  • 함수 라이브러리 : #include
  • void *b : 세팅할 메모리의 주소
  • size_t len : 세팅할 길이
  • 리턴값 : 없음

DESCRIPTION

파라메터로 들어온 메모리의 주소부터 len으로 들어온 길이만큼 0으로 초기화

bzero CODE

1
2
3
4
void	ft_bzero(void *s, size_t n)
{
	ft_memset(s, 0, n);
}

memset을 이용하여 0으로 초기화




memcpy


SYNOPSIS

  • 함수 원형 : void *ft_memcpy(void *dst, const void *src, size_t n)
  • 함수 라이브러리 : #include
  • void *dst : 복사가 될 메모리
  • const void *src : 복사할 값이 들어있는 메모리
  • size_t n : 복사할 개수
  • 리턴값 : dst의 시작주소

DESCRIPTION

src로 들어온 값을 n으로 들어온 값만큼 dst메모리에 복사

memcpy CODE

1
2
3
4
5
6
7
8
void	*ft_memcpy(void *dst, const void *src, size_t n)
{
	if (!dst && !src)
		return ((void *)0);
	while (n-- > 0)
		*((unsigned char *)dst + n) = *((unsigned char *)src + n);
	return (dst);
}

선언부에서 특이한게 있다면 const가 보인다.

const는 간단하게 얘기해서 변수를 상수로 만들어준다.

즉 변경할 수 없는 값으로 만들어준다는 얘기이다.

상수로 만들어주는 문법인 #define과 같다고 보면된다. #define보다는 const를 사용하는 것이 더 좋은 코딩습관이다.

우선 복사할 값과 복사받을 메모리가 모두 널 값으로 들어온다면 함수를 실행시킬 이유가없다.

dst의 시작주소를 반환해야하기 때문에 포인터를 변경하지 않게 코드를 작성했다.

unsigned char *형으로 형변환을 시켜준이유는 한 바이트 단위로 복사를 시켜주기 때문이다.




memccpy


SYNOPSIS

  • 함수 원형 : void *memccpy(void *dst, const void *src, int c, size_t n)
  • 함수 라이브러리 : #include
  • void * dst : 복사가 될 메모리
  • const void * src : 복사할 값이 들어있는 메모리
  • size_t n : 복사할 개수
  • 리턴값 : c를 만나면 그 다음 주소 못 만나면 NULL리턴

DESCRIPTION

src로 들어온 값을 n으로 들어온 값만큼 dst메모리에 복사하는데 복사 도중에 c를 만나면 종료

memccpy CODE

1
2
3
4
5
6
7
8
9
10
11
12
13
14
void	*ft_memccpy(void *dst, const void *src, int c, size_t n)
{
	size_t	i;

	i = 0;
	while (i < n)
	{
		*((unsigned char *)dst + i) = *((unsigned char *)src + i);
		if (*((unsigned char *)src + i) == (unsigned char)c)
			return (dst + i + 1);
		i++;
	}
	return (0);
}

딱히 설명할 부분은 없어보인다.




memmove


SYNOPSIS

  • 함수 원형 : void *memmove(void *dst, const void *src, size_t len)
  • 함수 라이브러리 : #include
  • void *dst : 복사가 될 메모리
  • const void *src : 복사할 값이 들어있는 메모리
  • size_t n : 복사할 개수
  • 리턴값 : dst의

DESCRIPTION

src로 들어온 값을 n으로 들어온 값만큼 dst메모리에 복사하는데 src와 dst의 메모리가 겹쳐도 수행해야한다.

memmove CODE

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
void	*ft_memmove(void *dst, const void *src, size_t len)
{
	char		*dst_tmp;
	const char	*src_tmp;

	if (!dst && !src)
		return ((void *)0);
	dst_tmp = dst;
	src_tmp = src;
	if (dst <= src)
		while (len--)
			*dst_tmp++ = *src_tmp++;
	else
	{
		dst_tmp += len;
		src_tmp += len;
		while (len--)
			*--dst_tmp = *--src_tmp;
	}
	return (dst);
}




memchr


SYNOPSIS

  • 함수 원형 : void *ft_memchr(const void *s, int c, size_t n)
  • 함수 라이브러리 : #include
  • const void *s : 비교를 진행할 메모리
  • int c : 비교할 값
  • size_t n : 비교할 개수
  • 리턴값 : c를 만나면 해당 주소 못 만나면 NULL리턴

DESCRIPTION

s가 가리키고 있는 메모리부터 시작해서 n개만큼 c가 있는지 검사한다.

memchr CODE

1
2
3
4
5
6
7
8
9
10
11
12
13
void	*ft_memchr(const void *s, int c, size_t n)
{
	size_t	i;

	i = 0;
	while (i < n)
	{
		if (*((unsigned char *)s + i) == (unsigned char)c)
			return ((void *)s + i);
		i++;
	}
	return ((void *)0);
}

딱히 설명할 부분은 없어보인다.




memcmp


SYNOPSIS

  • 함수 원형 : int ft_memcmp(const void *s1, const void *s2, size_t n)
  • 함수 라이브러리 : #include
  • const void *s1 : 비교를 진행할 메모리
  • const void *s2 : 비교를 진행할 메모리
  • size_t n : 비교할 개수
  • 리턴값 : 검사 진행중에 다른 값이 나오면 *s1에서 *s2를 뺀 값, 검사 후에도 안나오면 0반환

DESCRIPTION

s1메모리와 s2메모리를 한 바이트씩 비교해서 다른 값이 나올 경우 *s1에서 *s2를 뺀 값을 리턴하고 종료

만약 끝까지 비교를 해도 다른 값을 찾지 못했다면 0을 반환한다.

memcmp CODE

1
2
3
4
5
6
7
8
9
10
11
12
13
int	ft_memcmp(const void *s1, const void *s2, size_t n)
{
	size_t			i;

	i = 0;
	while (i < n)
	{
		if (*((unsigned char *)s1 + i) != *((unsigned char *)s2 + i))
			return (*((unsigned char *)s1 + i) - *((unsigned char *)s2 + i));
		i++;
	}
	return (0);
}

strcmp와 똑같은 원리이다.

다른 점은 memcmp는 중간에 0이 있어도 계속해서 검사를 진행하지만 strcmp는 함수를 종료한다.

그렇기 때문에 문자열을 비교한다면 strcmp를 문자열이 아닌 요소를 비교한다면 memcmp를 사용한다.




3줄 요약

1. 메모리를 잘 파악하는 것은 생각보다 엄~청나게 중요하다

2. 메모리 1바이트 하나하나 고려를 해야한다.

3. mem*** 함수들을 한번 구현해봄으로써 사용할 때 정확히 알고 사용할것같다. 좋은 경험이다.