240 likes | 962 Views
쉽게 풀어쓴 C 언어 Express. 제 17 장 동적 메모리와 연결 리스트. C Express. 이번 장에서 학습할 내용. 동적 메모리 할당에 대한 개념을 이해하고 응용으로 연결 리스트를 학습합니다. 동적 메모리 연결 리스트. 메모리 할당 방법. 정적 (static) 생존 시간 (lifetime): 프로그램 시작 프로그램 끝 실행 중에는 메모리가 일정하게 유지되므로 별도의 관리가 불필요함 자동 (automatic) 생존 시간 : 해당 블록 실행 시작 블록 끝
E N D
쉽게 풀어쓴 C언어 Express 제17장 동적 메모리와 연결 리스트 C Express
이번 장에서 학습할 내용 동적 메모리 할당에 대한 개념을 이해하고 응용으로 연결 리스트를 학습합니다. • 동적 메모리 • 연결 리스트
메모리 할당 방법 • 정적(static) • 생존 시간(lifetime): 프로그램 시작 프로그램 끝 • 실행 중에는 메모리가 일정하게 유지되므로 별도의 관리가 불필요함 • 자동(automatic) • 생존 시간: 해당 블록 실행 시작 블록 끝 • 메모리 할당및 해제(반납)가 블록과 연동되어 자동으로 이루어짐 • 실행 시간 스택(runtime stack) 사용 • 동적(dynamic) • 생존 시간: 동적 메모리 할당 동적 메모리 해제 • 프로그래머가 생존 시간 결정 • 힙(heap) 사용 • 정적/자동 메모리 할당의 단점 • 메모리의 크기가 프로그램 시작 전에 결정되어 고정됨 • 예: inta[100]; // 배열의 크기에 변수를 사용할 수 없음 • 결정된 크기보다 더 큰 데이터를 처리할 수 없음 • 결정된 크기보다 작은 데이터를 처리하면 메모리 낭비 유발
동적 메모리 할당 • 사용자의 요구에 따라 실행 중에메모리 할당 및 해제 • 할당 사용 해제 • 필요한 만큼만 메모리 할당 가능 메모리를 효율적으로 사용 • 라이브러리 함수 사용: <stdlib.h> • 할당: malloc(), calloc() • 재할당: realloc() • 해제: free()
동적 메모리 할당/해제 함수 void *malloc(size_t size); • size 바이트만큼 동적 메모리 블록 할당 • 반환 값: 성공 동적 메모리 블록 시작 포인터, 실패 NULL • 할당된 메모리 블록은 초기화되지 않음 void free(void *p); • p가 가리키는 동적 메모리 해제 • p가 NULL 포인터이면 무시 malloc • #include<stdlib.h> int *p; p = (int*)malloc(sizeof(int)); // 할당 if (p == NULL) … // 에러 처리 *p = 123; // 사용 free(p);// 해제 p ? free p 123
예제 #include<stdio.h> #include<stdlib.h> intmain(void) { int*score; inti; score = (int*)malloc(10 * sizeof(int)); if (score == NULL) { fprintf(stderr, "동적 메모리 할당 오류\n"); exit(1); } for (i = 0; i < 10; i++) score[i] = i; for (i = 0; i < 10; i++) printf("%d\n", score[i]); free(score); return0; } 0 1 2 3 4 5 6 7 8 9
예제 #include<stdio.h> #include<stdlib.h> intmain(void) { char *pc; inti; if (!(pc = (char*)malloc(26 + 1))) fprintf(stderr, "out of memory\n"), exit(1); for (i = 0; i < 26; i++) pc[i] = 'a' + i; pc[i] = '\0'; puts(pc); free(pc); return0; } abcdefghijklmnopqrstuvwxyz
예제 #include<stdio.h> #include<stdlib.h> #include<string.h> structBook { int number; chartitle[20]; }; int main(void) { struct Book *p; inti; if (!(p = (structBook *)malloc(2 * sizeof *p))) fprintf(stderr, "메모리부족\n"), exit(1); p->number = 1; strcpy(p->title, "C Programming"); (p+1)->number = 2; strcpy((p+1)->title, "Data Structure"); for (i = 0; i < 2; i++) printf("%d: %s\n", p[i].number, p[i].title); free(p); return 0; } 1: C Programming 2: Data Structure
calloc() void *calloc(size_t n, size_t size); • 크기가 size 바이트인 항목 n개를 저장할 수 있는 동적 메모리 할당 • ‘size * n’ 바이트 메모리 할당 • 반환 값: 성공 동적 메모리 블록 시작 포인터, 실패 NULL • 할당된 메모리 블록은 0으로 초기화됨 int *p = (int*)calloc(6, sizeof(int)); int *p = (int*)malloc(6 * sizeof(int)); // 다음은 모두 24B 할당 p = (int*)calloc(6, sizeof(int)); p = (int*)calloc(sizeof(int), 6); • p = (int*)calloc(6 * sizeof(int), 1); • p = (int*)calloc(1, 6 * sizeof(int)); ? ? ? ? ? ? 0 0 0 0 0 0 p p
예제 0: 96 1: 100 2: 103 3: 116 4: 96 5: 109 6: 89 7: 88 8: 109 9: 94 size = 10, total = 1000 #include <stdio.h>#include <stdlib.h>int main(void) {int *table, size, total, i; size = 5 + rand() % 6; // size: [5, 10]if (!(table = (int*)calloc(size, sizeof(int))))fprintf(stderr, "out of memory\n"), exit(1);for (i = 0; i < 1000; i++) table[rand() % size]++;for (total = 0, i = 0; i < size; i++) { total += table[i];printf("%2d: %3d\n", i, table[i]); }printf("\nsize = %d, total = %d\n", size, total); free(table); return0;}
realloc() void *realloc(void *p, size_t size); • p가 가리키는 동적 메모리의 크기를 size 바이트로 변경 • 메모리 크기가 size 바이트로 증가/감소됨 • 동적 메모리 블록이 다른 위치로 이동될 수 있음 • p가 가리키는 이전 메모리 내용은 변경되지 않음 • 크기가 감소되는 경우에는 감소된 부분만 이전 내용과 동일 • 증가된 부분은 초기화되지 않음 • 반환 값: 성공 동적 메모리 블록 시작 포인터, 실패 NULL • 성공 반환되는 포인터는 p와 다를 수 있음 • 실패 p가 가리키는 이전 메모리 내용은 변경되지 않음 • p가 NULL이면 malloc()과 동일 int *p = (int*)malloc(6 * sizeof(int)); 1 5 7 4 2 9 p 증가 int *q = (int*)realloc(p, 8 * sizeof(int)); int *q = (int*)realloc(p, 4 * sizeof(int)); 1 5 7 4 2 9 ? ? q 1 5 7 4 감소 q
realloc() int *p; • … if (!(p = (int*)malloc(6 * sizeof(int)))) // 에러 처리 … if (!(p = (int*)realloc(p, 8 * sizeof(int)))) //X // 에러 처리 // free(p) 불가능 (p가 가리키던 이전 메모리를 반환할 수 없음) // 메모리 누수(memory leak) 발생 int *p, *q; • … if (!(p = (int*)malloc(6 * sizeof(int)))) // 에러 처리 … if (!(q = (int*)realloc(p, 8 * sizeof(int)))) { //O // 에러 처리 // free(p) 가능 (p가 가리키던 이전 메모리를 반환할 수 있음) } else p = q;
예제 printf("\nmax size = %uKB\n", max_size); free(p); return0;}unsignedget_size(void) {return (rand() + 1) * 100;}void job(void *p, unsigned size) {printf("job %p%uKB\n", p, size);// do something} #include <stdio.h>#include <stdlib.h> unsignedget_size(void);void job(void *p, unsigned size); int main(void) {unsignedmax_size = 0, size, i;void *p = NULL, *q; for (i = 0; i < 5; i++) {if ((size = get_size()) > max_size) {printf("realloc%uKB => ", size);if (!(q = realloc(p, size * 1024))) { puts("failed"); size = max_size; } else { puts("succeeded");max_size = size; p = q; } } job(p, size); } realloc 4200KB => succeeded job 006D0020 4200KB realloc1846800KB => succeeded job 00AF0020 1846800KB job 00AF0020 633500KB realloc 2650100KB => failed job 00AF0020 1846800KB realloc 1917000KB => failed job 00AF0020 1846800KB max size = 1846800KB
예제 int *input(int *p_count) {enum { INCR_SIZE = 100 };int *list = NULL, *p, size = 0, count = 0, score;while (scanf("%d", &score) == 1) {if (size <= count) { size += INCR_SIZE;if (!(p = realloc(list, size * sizeof(int)))) {fprintf(stderr, "out of memory\n"); free(list); exit(1); } list = p; } list[count++] = score; } *p_count = count;return list;} #include <stdio.h>#include <stdlib.h>int *input(int *p_count);int main(void) {int *list, *p, *end, count, sum; list = input(&count); if (count < 1) {fprintf(stderr, "no data\n"); free(list); exit(1); } p = list; end = p + count; sum = 0;do { sum += *p; } while (++p < end);printf("average = %g\n", (double)sum / count); free(list); return0;} 10 20 30 40 50 60 70 80 ^Z average = 45
연결 리스트 • 배열(array) • 간단하고 다루기 쉬움 • 특정 위치의 원소에 대한 접근이 빠름 • 삽입/삭제가 비효율적 • 연결 리스트(linked list) • 각 원소가 포인터를 사용하여 다음 원소를 가리킴 • 다루기 어렵고 특정 위치의 원소에 대한 접근이 느림 • 삽입/삭제가 용이함 D D B N B C E C E A A 삽입 삭제
연결 리스트 • 노드(node) = 데이터 필드(data field) + 링크 필드(link field) • 일반적으로 동적 메모리를 사용하여 생성 • 헤드 포인터(head pointer): 첫 번째 노드를 가리키는 포인터 • 자기 참조 구조체(self-referential structure): 멤버 중에 자신과 같은 타입의 구조체를 가리키는 포인터가 있는 구조체 struct node { int data; structnode *link; };
기본적인 연결 리스트 조작 NODE *p1; p1 = (NODE*)malloc(sizeof(NODE)); p1->data = 10; p1->link = NULL; NODE *p2; p2 = (NODE*)malloc(sizeof(NODE)); p2->data = 20; p2->link = NULL; p1->link = p2; free(p1); free(p2); p2 20
예제: 책 리스트 if (list == NULL) list = p;elseprev->link = p; p->link = NULL;prev = p; } putchar('\n');for (p = list; p != NULL; p = p->link)printf("%s, %d\n", p->title, p->year); for (p = list; p != NULL; p = next) next = p->link, free(p); return0;} #include <stdio.h>#include <stdlib.h>#include <string.h> #define SIZE 50 typedefstruct NODE {chartitle[SIZE];int year;struct NODE *link;} NODE; int main(void) { NODE *list = NULL, *prev, *p, *next;charbuffer[SIZE]; while (1) {printf("책 제목: ");if (!gets(buffer)) break; p = (NODE*)malloc(sizeof(NODE));strcpy(p->title, buffer);printf("책 출판 연도: "); gets(buffer); p->year = atoi(buffer); 책 제목: 컴퓨터 개론 책 출판 연도: 2006 책 제목: C 언어 책 출판 연도: 2007 책 제목: ^Z 컴퓨터 개론, 2006 C 언어, 2007
예제: 학생 리스트 list.c list.h #include <stdio.h>#include <stdlib.h>#include "list.h" SNODE *make_list(void) { SNODE node, *p, *head = NULL;while (1) {printf("ID NAME GPA: ");if (scanf("%d%s%lf", &node.id, node.name, &node.gpa) != 3)break;if (!(p = (SNODE*)malloc(sizeof *p)))fprintf(stderr, "out of memory\n"), exit(1); *p = node; p->next = head; head = p; // reversed }return head;} voidfree_list(SNODE *p) { SNODE *q;for (; p; p = q) { q = p->next; free(p); }} voidout_list(const SNODE *p) {for (; p; p = p->next)printf("%d%s%g\n", p->id, p->name, p->gpa);} // double get_average(const SNODE *p) { ... } #ifndef _LIST_H_#define _LIST_H_#define NAME_SIZE 20typedefstructstudent_node {int id; char name[NAME_SIZE+1]; doublegpa;structstudent_node *next; } SNODE; SNODE *make_list(void);voidfree_list(SNODE *p);voidout_list(const SNODE *p);// double get_average(const SNODE *p);#endif
예제: 학생 리스트 (계속) #include "list.h"int main() { SNODE *list; list = make_list();putchar('\n');out_list(list);// printf("average = %g\n", get_average(list));free_list(list);return0;} ID NAME GPA: 2013001 강감찬 4.1 ID NAME GPA: 2013002 김유신 4.2 ID NAME GPA: 2013003 이순신 4.3 ID NAME GPA: ^Z 2013003 이순신 4.3 2013002 김유신 4.2 2013001 강감찬 4.1