Coding & Programming/C언어 기초(A-Z)

[C언어 기초코딩: 예제로 간단 정리] 12. 동적 메모리 할당과 파일 입출력(dynamic memory allocation, file input output)

mainCodes 2021. 3. 19. 20:01

JollyTree의 C언어 기초코딩: 예제로 간단 정리 - 12. 동적 메모리 할당과 파일 입출력(dynamic memory allocation, file input output)

동적 메모리 할당(dynamic memory allocation)

메모리를 할당하는 방법은 크게 정적(static) 할당과 동적(Dynamic) 할당이 있습니다. 정적 할당은 지난 8.배열(array)에서 정리한 배열이 대표적인 정적 할당 방법입니다.

배열과 같은 정적 할당은 프로그램이 시작되기 전에 미리 크기가 결정됩니다. 더 쉽게 표현하면 개발자가 크기를 예측하여 적절한 크기를 결정하여 코딩합니다.

    int num[10];
    char name[50]="Hong Gildong;
    float avg[5];

정적할당은 메모리의 크기가 미리 결정되기 때문에 프로그램 동작 중에는 변경이 불가능하여 예상했던 크기 보다 더 큰 입력이 있을 경우 처리를 하지못하거나 버퍼오버플로우 등 오류가 발생할 수 있으며 더 작은 입력이 있을 경우 한정되어 있는 메모리를 불필요하게 낭비하는 일이 발생합니다.

 

동적할당은 프로그램 실행 중에 필요한 메모리 공간을 할당하고 사용이 끝난 후에는 메모리를 반납할 수 있어 효율적인 프로그래밍이 가능합니다.

동적 메모리 할당 예제:

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
 
int alloc_func(char** param)
{
    char* pstr = NULL;
 
    pstr = (char*)calloc(256sizeof(char));
 
    strcpy(pstr, "maincodes blog\n");
 
    *param = pstr;
 
    return 0;
}
 
typedef struct EXAM_TAG
{
    int number;
    char name[10];
}EXAM;
 
int main()
{
    /* 1. 동적 메모리 할당과 해제#1(malloc, free)
    */
    {
        int* num;
        char* name = NULL;
        EXAM* exam_st;
 
        //char 형 포인터 변수에 메모리 할당
        name = (char*)malloc(sizeof(char* 100);
        strcpy(name, "Hong Gildong\n");
        printf("name = %s\n", name);
 
        //메모리 해제(반납)
        free(name); //free와 동시에 메모리 삭제 화면 추가
        printf("name = %s\n", name); //알 수 없는 내용 출력
 
        //정수형 포인터 변수에 메모리 할당
        num = (int*)malloc(sizeof(int* 3);
        printf("\n\nnum[0] = %d, num[1] = %d, num[2] = %d\n", num[0], num[1], num[2]);
 
        num[0= 100;
        num[1= 200;
        num[2= 300;
 
        printf("num[0] = %d, num[1] = %d, num[2] = %d\n", num[0], num[1], num[2]);
 
        //메모리 해제
        free(num);
 
        printf("\n");
        //구조체 포인터 변수에 메모리 할당
        exam_st = (EXAM*)malloc(sizeof(EXAM) * 2);
        exam_st->number = 10;
        strcpy(exam_st->name, "Mr. Kim");
 
        (exam_st + 1)->number = 20;
        strcpy((exam_st + 1)->name, "Mr. Lee");
 
        free(exam_st);
 
    }
    
    /* 2. 동적 메모리 할당과 해제#2(calloc, realloc, free)
    */
    {
        int* num;
 
        //정수형 포인터 변수에 메모리 할당
        num = (int*)calloc(3sizeof(int));
        printf("\n\nnum[0] = %d, num[1] = %d, num[2] = %d\n", num[0], num[1], num[2]);
 
        num[0= 100;
        num[1= 200;
        num[2= 300;
 
        printf("num[0] = %d, num[1] = %d, num[2] = %d\n", num[0], num[1], num[2]);
 
        //설명 : 할당했던 크기를 변경
        num = (int*)realloc(num, 5 * sizeof(int));
        num[3= 400;
        num[4= 500;
        printf("num[0] = %d, num[1] = %d, num[2] = %d, num[3] = %d, num[4] = %d\n",
            num[0], num[1], num[2], num[3], num[4]);
 
        free(num);
    }
 
    /* 3. 동적 메모리 할당과 해제#3(함수의 매개변수)
    */
    {
        char* str = NULL;
 
        alloc_func(&str);
 
        printf("str = %s\n", str);
 
        //메모리 해제
        free(str);
 
    }
    return 0;
}
cs



실행결과:


/* 1. 동적 메모리 할당과 해제#1(malloc, free)
*/

동적으로 메모리를 할당하는 방법는 기본적으로 malloc(), calloc() 함수가 있습니다. 예제와 같이 원하는 자료형의 포인터 변수를 선언하고 변수에 원하는 크기의 메모리를 동적으로 할당합니다. 할당할 때에는 자료형에 필요한 크기만큼을 곱하여 할당하고 할당된 메모리 사용이 끝나서 해제(반납)해야 할 경우에는 free() 함수를 이용합니다.

    name = (char*)malloc(sizeof(char) * 100);
    ...
    free(name);

    num = (int*)malloc(sizeof(int) * 3);
    ...
    free(num);

    exam_st = (EXAM*)malloc(sizeof(EXAM) * 2);
    ...
    free(exam_st);


참고로 free()된 메모리를 사용하면 어떤 일이 발생할까요 ? 예제에서는 의도적으로 free() 함수로 name변수에 할당된 메모리를 printf() 함수로 출력했습니다. 실행결과 그림에서도 볼 수 있듯이 메모리를 이미 해제했기 때문에 저장되어 있던 "Hong Gildong"이란 문자열 대신 알 수 없는 값들로 가득 채워져 있는 것을 볼 수 있습니다.


/* 2. 동적 메모리 할당과 해제#2(calloc, realloc, free)
*/

calloc() 함수도 malloc()함수와 같이 메모리를 할당할때 사용하는 함수로 malloc() 함수와 다른 점은 할당된 메모리를 모두 초기화 해준다는 것입니다.

예제에서는 malloc()과 calloc()으로 num변수에 메모리를 할당한 후 바로 printf()로 값을 출력하였습니다. 출력된 값들을 비교해 보면 malloc()함수 후에는 알 수 없는 값들이 출력되고 calloc()함수 후에는 모두 0으로 초기화 된것을 알 수 있습니다.

 


relloc()함수는 할당된 메모리의 크기를 변경할 때 사용되는 함수로 예제에서는 할당 메모리의 크기를 추가하여 num[3], num[4]에 각각 데이터를 저장하였습니다.


/* 3. 동적 메모리 할당과 해제#3(함수의 매개변수)
*/


예제는 alloc_func() 함수 안에서 메모리를 동적으로 할당한 후 함수 호출이 끝난 후 메모리를 해제하는 것을 보여줍니다.


파일 입출력(file input output)

파일 입출력과 관련된 예제는 여기 블로그에 포스팅된 자료가 있어 예제코드는 이전에 올려된 링크를 공유합니다.

 

[C/C++] 파일 읽고 쓰기(file read/write : open, read, write, close functions example)
maincodes.tistory.com/3?category=465510

 

[C/C++] 파일 읽고 쓰기(file read/write : fopen, fread, fwrite, fclose functions example)
maincodes.tistory.com/1?category=465510

전에 정리가 다소 부족했던 파일 입출력에 대한 기본 개념을 정리합니다. 파일은 크게 2가지로 나뉩니다. 사람이 읽을 수 있는 텍스트파일 그리고 읽을 수 없는 바이너리(이진)파일이 있습니다. 소스코드, 메모장 파일이 텍스파일에 해당되며, 실행파일(EXE), 그림파일(JPG, GIF 등), 파워포인트 파일(PPTX) 등은 바이너리 파일에 속합니다. 

단순히 텍스트인지 바이너리 파일인지 구분하는 방법은 메모장으로 열었을 때 텍스트 파일은 읽은 수 있지만 바이너리 파일은 알 수 없는 문자가 나오게 됩니다.

<텍스트 파일>
<바이너리 파일>

 
그럼 C언어에서는 파일을 쓰거나 읽을 때 어떤 방법이 있을까요 ? 크게 FILE 파인터를 이용한 함수군과 파일 IO(Input Output) 함수군을 이용한 방법이 있습니다.

FILE 포인터 함수군 파일 IO 함수군
fopen, fwrite, fread, fprintf, fgets, fseek, fclose 등 open, read, write, close 등


기본적인 파일 입출력은 파일 열기, 쓰고/읽기, 닫기의 절차로 이루어집니다. 

FILE 포인터를 이용한 절차

  1. fopen() 함수로 파일 오픈합니다. 파일을 열때 두번째 매개 변수의 mode는 아래와 같습니다.

모드 설명
"r" 파일을 읽기 모드로 오픈합니다.
"w" 파일을 쓰기 모드로 오픈합니다. 만약 폴더에 파일이 이미 존재하면 기존  내용은 지워지고 존재하지 않으면 파일이 생성됩니다.
"a" 파일을 추가 모드로 오픈합니다. 만약 폴더에 파일이 없으면 새로운 파일을 만들고 똑같은 이름의 파일이 있으면 데이터를 파일의 끝에 추가합니다.
"r+" 파일을 읽기와 쓰기 모드로 오픈합니다. 만약 폴더에 파일이 존재하지 않으면 오류를 리턴합니다.
"w+" 파일을 읽기와 쓰기 모드로 오픈합니다. 만약 폴더에 파일이 존재하지 않으면 파일이 생성되고 존재하지 않으면 새 데이터로 기존 파일을 덮어씁니다.
"a+" 파일을 읽기와 쓰기 모드로 오픈합니다. 만약 폴더에 파일이 있으면 데이터가 파일의 끝에 추가되고 파일이 없으면 새로운 파일을 만듭니다.
"b" 바이너리 파일 모드로 오픈합니다.

  2. fwrite(), fprintf(), fread(), fgets() 등으로 읽고 씁니다.
  3. fclose() 함수로 파일을 반드시 닫습니다.

 
FILE 포인터 함수를 이용하는 경우 오픈 하고자 파일이 읽기 목적인지 쓰기 목적인지에 따라 열기 모드를 지정하고 텍스트 파일을 읽고 쓸 때는 fprintf(), fputs(), fgets() 등의 함수를 사용하고 바이너리 파일을 읽고 쓸 때는 fread(), fwrite() 함수를 주로 사용합니다.  파일 입출력을 하고자 하는 용도에 맞게 관련 함수를 사용하면 되겠습니다.


파일 IO 함수군를 이용한 절차


1. open() 함수로 파일 오픈합니다. 파일을 열때 두번째 매개 변수의 mode는 아래와 같습니다.

모드 설명
O_RDONLY 파일을 읽기 전용으로 오픈합니다.
O_WRONLY 파일을 쓰기 전용으로 오픈합니다.
O_RDWR 파일을 읽기와 쓰기 모드로 오픈합니다.
O_CREAT 폴더에 파일이 없으면 새로운 파일을 생성하고 오픈합니다.
O_EXCL 폴더에 파일이 존재하면 오류를 발생하고 파일을 오픈하지 않습니다.
O_TRUNC 폴더에 파일이 존재하면 기존 파일의 모든 내용을 지웁니다.
O_APPEND 파일을 오픈한 후 파일 포인터가 파일의 끝에 위치합니다.

2. read(), write()로 파일 읽고 씁니다.
3. close() 함수로 파일을 닫습니다.
 
파일 IO 함수를 이용하는 경우에는 오픈 하고자 파일이 읽기 목적인지 쓰기 목적인지에 따라 열기 모드를 지정하고 read(), write() 함수를 이용하여 텍스트파일이나 바이너리 파일을 읽고 쓸 수 있습니다.

 

마치며...

습득한 지식을 글로 정리하는 것은 쉽지 않은 것 같습니다. 이상으로 C언어 기초코딩 연재를 마칩니다. 감사합니다.


이상 JollyTree였습니다 (•̀ᴗ•́)و