제17장 동적 메모리와 연결 리스트

Download Report

Transcript 제17장 동적 메모리와 연결 리스트

쉽게 풀어쓴 C언어 Express
제17장 동적 메모리와 연결 리스트
© 2012 생능출판사 All rights reserved
이번 장에서 학습할 내용
• 동적 메모리
• 연결 리스트
© 2012 생능출판사 All rights reserved
동적 메모리 할당에
대한 개념을
이해하고 응용으로
연결 리스트를
학습합니다.
메모리 할당 방법
 정적(static)
 생존 시간(lifetime): 프로그램 시작  프로그램 끝
 실행 중에는 메모리가 일정하게 유지되므로 별도의 관리가 불필요함
 자동(automatic)
 생존 시간: 해당 블록 실행 시작  블록 끝
 메모리 할당 및 해제(반납)가 블록과 연동되어 자동으로 이루어짐
 실행 시간 스택(runtime stack) 사용
 동적(dynamic)
 생존 시간: 동적 메모리 할당  동적 메모리 해제
 프로그래머가 생존 시간 결정
 힙(heap) 사용
 정적/자동 메모리 할당의 단점
 메모리의 크기가 프로그램 시작 전에 결정되어 고정됨
 예: int a[100];
// 배열의 크기에 변수를 사용할 수 없음
 결정된 크기보다 더 큰 데이터를 처리할 수 없음
 결정된 크기보다 작은 데이터를 처리하면 메모리 낭비 유발
© 2012 생능출판사 All rights reserved
동적 메모리 할당
 사용자의 요구에 따라 실행 중에
메모리 할당 및 해제
 할당  사용  해제
 필요한 만큼만 메모리 할당 가능
 메모리를 효율적으로 사용
 라이브러리 함수 사용: <stdlib.h>
 할당: malloc(), calloc()
 재할당: realloc()
 해제: free()
운영 체제
할당
요구
##iinncclluuddee
##iinncclluuddee
<<ssttddiioo..hh>>
<<ssttddlliibb..hh>>
iinntt mmaaiinn((vvooiidd))
{{
iinntt **pp;;
pp == ((iinntt **))mmaalllloocc(( ssiizzeeooff((iinntt)) ));;
......
}}
프로그램
© 2012 생능출판사 All rights reserved
동적 메모리 할당/해제 함수
void *malloc(size_t size);
 size 바이트만큼 동적 메모리 블록 할당
 반환 값: 성공  동적 메모리 블록 시작 포인터, 실패  NULL
 할당된 메모리 블록은 초기화되지 않음
void free(void *p);
 p가 가리키는 동적 메모리 해제

p가 NULL 포인터이면 무시
malloc
#include <stdlib.h>
p
int *p;
p = (int*)malloc(sizeof(int)); // 할당
if (p == NULL) …
// 에러 처리
*p = 123;
// 사용
free(p);
// 해제
© 2012 생능출판사 All rights reserved
?
free
p
123
예제
#include <stdio.h>
#include <stdlib.h>
int main(void) {
int *score;
int i;
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);
return 0;
© 2012 생능출판사 All rights reserved
0
1
2
3
4
5
6
7
8
9
예제
#include <stdio.h>
#include <stdlib.h>
int main(void) {
char *pc;
int i;
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);
return 0;
abcdefghijklmnopqrstuvwxyz
© 2012 생능출판사 All rights reserved
예제
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct Book {
int number;
char title[20];
};
int main(void) {
struct Book *p;
int i;
if (!(p = (struct Book *)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");
© 2012 생능출판사 All rights reserved
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*)malloc(6 * sizeof(int));
?
?
?
?
?
?
p
int *p = (int*)calloc(6, sizeof(int));
0
0
0
p
© 2012 생능출판사 All rights reserved
0
0
0
// 다음은 모두 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));
예제
#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); return 0;
© 2012 생능출판사 All rights reserved
0:
1:
2:
3:
4:
5:
6:
7:
8:
9:
96
100
103
116
96
109
89
88
109
94
size = 10, total = 1000
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
p
4
2
9
int *q = (int*)realloc(p, 8 * sizeof(int));
1
7
4
2
9
?
?
q
int *q = (int*)realloc(p, 4 * sizeof(int));
1
q
© 2012 생능출판사 All rights reserved
5
5
7
4
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;
© 2012 생능출판사 All rights reserved
예제
#include <stdio.h>
#include <stdlib.h>
unsigned get_size(void);
void job(void *p, unsigned size);
int main(void) {
unsigned max_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);
}
© 2012 생능출판사 All rights reserved
}
printf("\nmax size = %uKB\n", max_size);
free(p); return 0;
unsigned get_size(void) {
return (rand() + 1) * 100;
}
void job(void *p, unsigned size) {
printf("job %p %uKB\n", p, size);
// do something
}
realloc 4200KB => succeeded
job 006D0020 4200KB
realloc 1846800KB => succeeded
job 00AF0020 1846800KB
job 00AF0020 633500KB
realloc 2650100KB => failed
job 00AF0020 1846800KB
realloc 1917000KB => failed
job 00AF0020 1846800KB
max size = 1846800KB
예제
#include <stdio.h>
#include <stdlib.h>
int *input(int *p_count);
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;
}
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); return 0;
© 2012 생능출판사 All rights reserved
}
*p_count = count;
return list;
10 20 30 40
50 60 70 80
^Z
average = 45
연결 리스트
 배열(array)
 간단하고 다루기 쉬움
 특정 위치의 원소에 대한 접근이 빠름
 삽입/삭제가 비효율적
0
1
2
3
4
5
 연결 리스트(linked list)
 각 원소가 포인터를 사용하여 다음 원소를 가리킴
 다루기 어렵고 특정 위치의 원소에 대한 접근이 느림
 삽입/삭제가 용이함
A
6
7
C
B
메인 메모리
N
C
A
D
E
B
C
A
B
삽입
© 2012 생능출판사 All rights reserved
삭제
D
E
8
D
9
E
연결 리스트
연 결 리 스 트필드(link
의 노드는
 노드(node) = 데이터 필드(data field) + 링크
field)
첫번째 노드를 가리키
는 포인터를 헤드포인
터라고 하고 맨 마지
막 노드의 링크 필드
는 NULL입 니 다 .
 일반적으로 동적 메모리를 사용하여 생성
data
데이터 필드와 링크
필드로 이루어집니다.
link
 헤드 포인터(head pointer): 첫 번째 노드를 가리키는 포인터
plist
10
20
3N0U L L
4N0U L L
5N0U L LN U L L
 자기 참조 구조체(self-referential structure): 멤버 중에 자신과 같은
타입의 구조체를 가리키는 포인터가 있는 구조체
struct node {
int data;
struct node *link;
};
© 2012 생능출판사 All rights reserved
기본적인 연결 리스트 조작
NODE *p1;
p1 = (NODE*)malloc(sizeof(NODE));
p1->data = 10;
p1->link = NULL;
p1
NODE *p2;
p2 = (NODE*)malloc(sizeof(NODE));
p2->data = 20;
p2->link = NULL;
p1->link = p2;
free(p1);
free(p2);
© 2012 생능출판사 All rights reserved
p1
p1
p1
p2
10
10
10
NULL
10
20
NULL
20
NULL
20
NULL
예제: 책 리스트
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
if (list == NULL) list = p;
else prev->link = p;
p->link = NULL;
prev = p;
#define SIZE 50
}
typedef struct NODE {
char title[SIZE];
int year;
struct NODE *link;
} NODE;
putchar('\n');
for (p = list; p != NULL; p = p->link)
printf("%s, %d\n", p->title, p->year);
int main(void) {
NODE *list = NULL, *prev, *p, *next;
char buffer[SIZE];
while (1) {
printf("책 제목: ");
if (!gets(buffer)) break;
p = (NODE*)malloc(sizeof(NODE));
strcpy(p->title, buffer);
printf("책 출판 연도: ");
gets(buffer);
p->year = atoi(buffer);
© 2012 생능출판사 All rights reserved
for (p = list; p != NULL; p = next)
next = p->link, free(p);
}
return 0;
책
책
책
책
책
제목: 컴퓨터 개론
출판 연도: 2006
제목: C 언어
출판 연도: 2007
제목: ^Z
컴퓨터 개론, 2006
C 언어, 2007
예제: 학생 리스트
list.c
list.h
#ifndef _LIST_H_
#define _LIST_H_
#include <stdio.h>
#include <stdlib.h>
#include "list.h"
#define NAME_SIZE 20
typedef struct student_node {
int id;
char name[NAME_SIZE+1];
double gpa;
struct student_node *next;
} SNODE;
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;
}
SNODE *make_list(void);
void free_list(SNODE *p);
void out_list(const SNODE *p);
// double get_average(const SNODE *p);
void free_list(SNODE *p) {
SNODE *q;
for (; p; p = q) { q = p->next; free(p); }
}
#endif
void out_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) { ... }
© 2012 생능출판사 All rights reserved
예제: 학생 리스트 (계속)
#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);
return 0;
© 2012 생능출판사 All rights reserved
ID
ID
ID
ID
NAME
NAME
NAME
NAME
GPA:
GPA:
GPA:
GPA:
2013001 강감찬 4.1
2013002 김유신 4.2
2013003 이순신 4.3
^Z
2013003 이순신 4.3
2013002 김유신 4.2
2013001 강감찬 4.1