본문 바로가기
# Semiconductor/- Semicon Academy

[Harman 세미콘 아카데미] 19일차 - C언어(C표준함수, Buffer, malloc, Memory Dump)

by Graffitio 2023. 7. 13.
[Harman 세미콘 아카데미] 19일차 - C언어(C표준함수, Buffer, malloc, Memory Dump)
728x90
반응형
[C 표준 함수]

 

표준 라이브러리 라고도 한다. 

C언어 표준 규약에 의해 모든 컴파일러가 기본적으로 제공하도록 약속되어 있는 함수들을 말하며,

표준 함수들의 선언은 표준 헤더파일에 있으므로 사용하기 위해서는 함수가 선언된 표준 헤더파일을 #include 해줘야 한다.

기본 함수의 정의는 라이브러리 파일에 있으며, 링크 과정에서 연결되고 모두 외울 필요 없이 필요할 때마다 레퍼런스를 참조하여 사용하면 된다.

 

<헤더파일 종류>

헤더 파일 기능 관련 함수
stdio.h 표준 입출력 printf(), scanf(), getchar(), fopen(), fseek(), ...
string.h 메모리와 문자열의 처리 strcat(), strcmp(), strcpy(), strlen(), ...
ctype.h 문자 검사 및 변환 isalnum(), isalpha(), isgraph(), tolower(), ...
math.h 삼각함수 등의 수학 함수 sin(), cos(), tan(), log(), exp(), fmod(), sqrt(), abs(), ...
stdlib.h 메모리 동적 할당, 가상 난수 발생,
문자열 변환
malloc(),free(), rand(), strtod(), atof(), ...
malloc.h 메모리 할당에 관한 함수 calloc(), malloc(), free(), ...
time.h 날짜, 시간, 내부 클락 clock(), ctime(), asctime(), ftime(), time(), getdata(), ...

 

<문자열 처리 함수>

문자열을 조작하거나 복사, 연결, 비교, 검출하는 기능을 제공하는 함수

아래 함수들은 string.h에 선언되어 있다.

함수명 형식 기능
strlen() unsigned strlen(const char* str) str의 길이를 반환
strcat_s() errno_t strcat_s(char* str1, const char* str2) str1에 str2를 연결하여 str1에 저장
= 문자열 덧붙이기
strncat_s() ernno_t strncat_s(char* *str1, rsize_t byte,
const char* str2, rsize_t max)
str1에 str2의 선두 max개의 문자를 연결
strncat_s() ernno_t strncat_s(char* *str1, rsize_t byte,
const char* str2, rsize_t max)
str1에 str2의 선두 max개의 문자를 연결
strcmp() int strcmp(cahr* str1, const char* str2) str1과 str2의 문자코드를 대소 비교
str1>str2이면 양수값 반환
str1<str2이면 음수값 반환
str1 = str2이면 0을 반환
strncmp() int strncmp(char* str1, const char* str2, unsigned c) 두 문자열을 앞에서부터 c개 비교한다.
나머지는 strcmp()와 동일.
strcpy_s() errno_t strcpy_s(char* str1, const char* str2) str2를 str1으로 복사한다.
strncpy_s() char* strncpy_s(char* str1,, const char* str2, size_t c) 앞에서부터 c개의 문자를 str2에서 str1으로 복사한다.
strstr() char* strstr(const char* str1, const char* str2) str1내에 str2가 포함되어 있다면 
str1에 있는 str2의 첫글자의 위치를 반환한다. 
strerror() char* strerror(int errnum) 오류번호가 errnum인 오류메시지를 반환한다.
strupr() char* strupr(char* str) str의 문자열 중 소문자를 대문자로 변환

 


 

[Buffer]

 

입출력 스트림

버퍼에 대해 알기 전, 입력 스트림과 출력 스트림에 대해 이해할 필요가 있다.

우리가 키보드로 데이터를 입력하면, 입력 스트림을 통해 데이터가 이동하고 프로그램에 읽혀진다.

이렇게 읽혀진 프로그램은 출력 스트림을 통해 모니터과 같은 출력 장치로 출력된다.

즉, 스트림은 데이터를 모두 한 방향으로만 전달하는 다리 역할을 한다.
여기서 키보드와 모니터의 입출력을 콘솔 입출력(Console I/O)라고 하며, 표준 입출력이라고도 한다.

 

버퍼(Buffer)란?

버퍼의 사전적 정의는 완충, 완충역할을 하는 것이다.

컴퓨터 공학에서 사용되는 버퍼 또한, 장치와 장치간의 데이터 전송 시 완충작용을 하기 위한 임시 데이터 저장 공간을 의미한다.

 

집을 짓기 위해 벽돌을 옮긴다고 가정해보자.

1,000개의 벽돌이 있는데, 이 것을 다리 건너까지 옮겨야 한다.

이 때, 벽돌을 하나씩 옮기는 것과 트럭에 100개씩 담아서 옮기는 것 중 어느 것이 더 효율적인가?

당연히 후자가 더 효율적이며, 여기서 트럭에 해당되는 것이 버퍼이다.

즉, 입력을 하나하나 받아서 CPU로 연산하는 것보다 입력을 하나하나 받아서 임시로 저장한 뒤, CPU로 한 방에 연산하는 것이 훨씬 효율적이라는 뜻이다.

 

scanf() 함수를 이용할 때, 데이터를 쭉 입력하고 엔터를 누르기 전까지는 작동을 안 하는 것이 바로 버퍼때문이다.

프로그램은 버퍼로부터 데이터를 읽어들이는데, 엔터(\n)이 입력되기 전까지는 데이터가 CPU에 전달되지 않는다.

 


 

[malloc 함수]

 

메모리의 4영역

메모리의 4영역

메모리는 코드영역, 데이터영역, 힙영역, 스택 영역으로 총 4가지의 영역으로 나눠진다.

 

1. 코드 영역(code)

: 명령어(실행되는 프로그램 소스코드)들이 2진 코드 형태로 저장되는 메모리 공간이며, 여기에 저장된 명령어들이 한 문장씩 꺼내져 실행이 된다. 또한 상수도 코드 영역에 저장된다.

 

2. 데이터 영역(data)

: 프로그램 실행과 동시에 할당되어, 프로그램이 종료될 때까지 남아 있어야 하는 전역변수와 정적 변수들이 할당된다.

 

3. 힙 영역(heap)

: 프로그래머가 직접 관리할 수 있는 정적 메모리 영역이며, 나머지 메모리 공간의 영역들은 생성과 소멸이 컴파일러에 의해 결정된다면, 이 메모리 공간은 프로그래머가 직접 데이터를 할당시키고 소멸시킬 수 있다.

→ 프로그래머의 동적 할당

 

4. 스택 영역(stack)

: 지역변수함수의 전달인자와 같이 함수의 호출 시에만 필요한 변수를 저장하는 메모리 공간

동적 영역이며, 휘발성 데이터 영역으로도 불린다.

 

<메모리 활용 sequence>

   ① 프로그램이 실행되면 가장 먼저 데이터 영역의 메모리 공간에

        전역 변수와 정적 변수들이 초기화되어 저장된다.

   ② main 함수를 실행하면서 코드 영역에서 명령문들을 한 문장씩 실행

   ③ 코드를 실행하면서 필요한 지역 변수나 함수의 전달인자와 같은 정보가

        스택 영역에 할당되고 소멸되는 과정이 반복

 

Heap 영역

 

이제까지 배운 변수나 배열과 같은 데이터들은 데이터 영역과 스택 영역만으로 충분히 감당이 가능했다.

하지만 실무에서는 문자열과 같은 메모리의 주소값같이 크기가 방대한 데이터를 다루므로 힙 영역의 메모리를 할당받아 사용해야할 경우가 생기게 된다.

 

1. Heap 영역 사용법

#include <stdio.h>
#include <stdlib.h> // malloc 함수 사용을 위한 헤더파일

int main()
{	
    void *ptr1 = malloc(4); // heap 영역의 동적 할당을 위한 malloc 함수
    void *ptr2 = malloc(20);
    
    printf("%#x \n", ptr1);
    printf("%#x \n", ptr2);
    
    free(ptr1); // heap영역에 할당되었던 메모리 공간 소멸을 위한 free()함수
    free(ptr2);
    
    return 0;	
}

할당된 메모리 주소값 출력됨
malloc 함수 사용법

① malloc 함수

    : heap 영역에 유저가 지정한 크기의 메모리 공간을 할당함과 동시에 그 주소값을 반환(return)

      

② free함수

     : 전달 인자로 넣어준 포인터 변수가 가리키는 메모리 공간의 할당을 해제함.(void return)

 

<참고>

   - free 함수를 써주지 않아도 프로그램이 종료되면,

     heap 영역에 할당된 메모리 공간은 모두 소멸된다.

     하지만 프로그램이 종료되기 전까지 메모리를 낭비하는 격이므로

      free 함수를 써주는 것을 습관화 하자.

   - malloc과 비슷한 함수

     calloc(x, y) : 총 x개의 블록을 각각 y byte 크기로 heap 영역에 할당해준다.

     realloc : ptr=(int*)realloc(ptr, sizeof(int)*6)

                   malloc 함수로 할당한 포인터 ptr이 가리키는 메모리 공간의 크기를 24byte로 변경

 

 


 

[Memory Dump]

 

메모리 덤프란?

메모리 덤프는 특정 메모리 영역의 내용을 출력하는 것

일반적으로 프로그램의 디버깅이나 메모리 분석을 위해 사용된다.

 

  cf) Memory-mapped I/O
       - 입출력 장치를 메모리 주소 공간에 매핑하는 방식
       - 입출력 장치를 일반적인 메모리 접근 방식으로 다룰 수 있게 한다.
       - 즉, 입출력 장치의 레지스터나 상태를 읽거나 쓸 때 메모리 주소를 통해 접근 가능

 

<메모리 덤프가 제공하는 정보>

 

① 메모리의 값

    : 프로그램이 실행 중인 동안 메모리에 저장된 데이터의 실제 값

      이를 통해 프로그램 상태 확인 및 디버깅 가능

② 배열이나 구조체의 구성

     : 메모리 덤프를 통해 배열이나 구조체 요소들이 메모리에 어떻게 저장되는지 알 수 있다.
       이를 통해 메모리 레이아웃을 이해하고 프로그램이 예상대로 잘 동작하는지 확인 가능

③ 메모리 할당 및 해제

     : 동적으로 할당된 메모리의 할당과 해제 상태를 확인 가능

       메모리 누수나 잘못된 메모리 해제와 같은 문제를 찾을 수 있다.

 

메모리 덤프 출력 코드

 

메모리 덤프를 생성하기 위해서는

프로그램 실행 중 해당 메모리 영역의 값을 읽고 출력하는 코드를 작성해야 한다.

일반적으로 c언어에서는 포인터를 사용하여 메모리 주소에 접근하고 값을 읽어올 수 있다.

 

#include <stdio.h>
#include <stdint.h>

// 메모리 덤프 함수 //
// 주어진 메모리 주소부터 길이 만큼의 메모리를 덤프라여 출력
void memoryDump(const void *address, size_t length)
{
    const uint8_t *ptr = (const uint8_t *)address; // 주어진 메모리 주소를 uint8_t 타입의 포인터로 캐스팅하여 선언
    for (size_t i = 0; i < length; i++)
    {
        printf("%06X ", ptr[i]); // 바이트 값을 16진수로 출력

        if ((i + 1) % 16 == 0)
        {
            printf("\n"); // 매 16바이트마다 줄바꿈
        }
    }
}

int main()
{
    // 메모리 덤프를 수행할 메모리 주소와 길이
    uint32_t startAddress = 0x09000000; // 변수에 메모리 덤프를 수행할 시작 주소를 할당
    size_t length = 500; // 변수에 덤프할 길이인 500을 할당

    // 메모리 덤프 수행
    memoryDump((const void *)&startAddress, length); // 0x09000000 번지부터 500byte를 화면에 출력하라는 의미

    return 0;
}

0x09000000 번지부터 500byte만큼 화면에 출력

<수업 시간에 사용한 코드>

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <malloc.h>

char buf[100]; // buf라는 이름의 길이가 100인 문자열 선언
void MemoryDump(int start, int length); // 함수 프로토타입 선언

int main(int argc, char *argv[])
{
	int start = 0x09000000; // 스타트 번지와
	int length = 500; // length(크기)가 주어졌을 때,

	int i = 1;
	double d = 3.14;
	void *p = malloc(100); // 100byte 크기의 메모리 동적 할당하고 그 주소를 p에 저장

	*(double *)p = d; // p가 가리키는 주소에 d값을 저장
                      // *p는 void 타입이므로 d와 맞춰주기 위해 (double*) 로 캐스팅
	
	MemoryDump((int)buf, length); // buf의 값은 array이기 때문에 int로 캐스팅

	return 0;
}

void MemoryDump(int start, int length)
{
	//void *vp = (void *)start; // void 포인터 변수 vp 선언
	char *cp = buf; // cp는 buf 배열을 가리키는 char*(char 포인터)

	int i = 0; // index 변수의 초기값은 0

	// length만큼 뺑뻉이 돌려야 한다.
	while (i<length) // 수행 조건 : i < length
	{
		//char *cp = (char *)vp; // void 타입이면 char에서 사용 불가하므로 char * 형으로 캐스팅(형변환)해준다.
		//char c = *cp;
		//char c = *((char *)vp); // 위의 두 줄을 간략화한 것
		unsigned char c = *((char *)cp+i); // unsigned char로 캐스팅하여 cp와 i의 합산된 주소가 가리키는 값을 읽어온다.
		i++; // 포인터에 1을 더하는 것은 다음 순서의 메모리공간을 읽는다는 의미
		printf("0x%02x  ", c); // 2자리의 16진수로 출력, 2nibble(16진수 2개)
	}
}

 


 

728x90
반응형