11차시: 함수 동적 메모리 할당 다차원 배열
• 프로그래밍 및 실험
• 제 11주
• 동국대학교 조영석
이 문서는 나눔글꼴로 작성되었습니다. 설치하기
2
6.6 함수 인자로써의 배열
- 함수정의에서 배열로 선언된 형식 매개 변수는 pointer임.
- 함수의 인자로 배열이 전달되면 배열의 기본 주소가 (배열의
내용이 아님) call-by-value로 전달됨.
- 배열 원소는 복사되지 않음.
3
(예)
#include <stdio.h> double sum(double *, int); void main( ) { double D[5] = {3.5, 4.5, 5.5, 6.5}; /* D[4] == 0.0 */ int n = 5, i; for (i = 0, i < n; ++i) printf("%lf", *(D + i)); printf("\n%lf", sum(D, n));
} double sum(double a[], int n) { /* n is the size of a[] */ int i; /* double a[] == double *a */ double sum = 0.0; /* 변수이름 = 함수이름, but O.K. */ for (i = 0; i < n; ++i) sum = sum + a[i]; return sum; }
실행결과 : 3.5 4.5 5.5 6.5 0.0 20.0
4
sum( )이 호출되는 여러 방법
호출 계산 및 리턴되는 값
sum(v, 100) v[0] + v[1] + ... + v[99]
sum(v, 88) v[0] + v[1] + ... + v[87]
sum(&v[7], k - 7) v[7] + v[8] + ... + v[k - 1]
sum(v + 7, 2 * k) v[7] + v[8] + ... + v[2 * k + 6]
5
6.7 예제 : 버블정렬
void swap(int *, int *);
void bubble(int a[], int n)
{
int i, j;
for (i = 0; i < n - 1; ++i)
for (j = n - 1; j > i; --j)
if (a[j - 1] > a[j])
swap(&a[j - 1], &a[j]);
}
6
int a[] = {7, 3, 66, 3, -5, 22, -77, 2};
bubble(a, 8); a[j-1] > a[j]?
i = 0, j = 7 a[6] v.s. a[7]
i = 0, j = 6 a[5] v.s. a[6]
i = 0, j = 5 a[4] v.s. a[5]
i = 0, j = 4 a[3] v.s. a[4]
i = 0, j = 3 a[2] v.s. a[3]
i = 0, j = 2 a[1] v.s. a[2]
i = 0, j = 1 a[0] v.s. a[1]
7
i = 1, j = 7 a[6] v.s. a[7] : : : : i = 7, j = 2 a[1] v.s. a[2]
i = 2, j = 7 a[6] v.s. a[7] : : : : i = 7, j = 3 a[2] v.s. a[3]
i = 3, j = 7 a[6] v.s. a[7] : : : : i = 7, j = 4 a[3] v.s. a[4] : : i = 6, j = 7 a[6] v.s. a[7]
i = 7, j = 7 실행종료
8
0 1 2 3 4 5 6 7
i j
i j
i j
i j
i j
: : 종료.
9
algorithm의 효율성 분석: 비교 횟수
for (i = 0; i < n - 1; ++i)
for (j = n - 1; j > i; --j)
if (a[j - 1] > a[j])
swap(&a[j - 1], &a[j]);
i의 변화: 0 부터 (n-2)까지 (n-1)회
j의 변화: (n-1)부터 (i+1)까지 (n-2)+(n-3)+(n-4)+…+1회
= (n-2)(n-1)/2 = (n2-3n+2)/2 ≈ n2 = O(n2)
n(자료의 개수)가 늘어나면 시간복잡도(time complexity)가 크게 증가
10
6.8 calloc( )과 malloc( )을 이용한 동적 메모리 할당(dynamic memory allocation)
- 표준 library에 제공.
- prototype은 stdlib.h에 포함됨.
- calloc( ) : Contiguous Allocation.
• calloc(n, el_size)
• 각 원소가 el_size인 n개의 원소로 이루어진 배열.
• memory 영역은 모든 bit가 0으로 초기화 됨.
• void * 형의 pointer가 return됨.
(또는 NULL이 return됨. - 실패했을 경우)
11
- malloc( ) : Memory Allocation.
• malloc(memory_size)
• memory의 초기화 않음.
• calloc( )보다 시간이 적게 걸림.
• void * 형의 pointer(또는 NULL)가 return됨.
- free(ptr)
• 동적으로 할당받은 공간은 함수의 실행이 끝나도 system에 반환되지 않으므로 반드시 명시적으로 반환할 것.
12
(예) #include <stdio.h> #include <stdlib.h>
int main( ) {
int *a; int n;
....
a = calloc(n, sizeof(int)); /* machine independent */
....
free(a); }
또는 , a = malloc(n * sizeof(int));
13
6.9 예제 : 합병(merge)과 합병정렬(merge sort)
- 두개의 정렬된 array a[]와 b[]를 이용하여 하나의 정렬된 array c[]를 만드는 방법.
• a[i]와 b[j]를 비교하여 작은 수를 c[k]에 저장하고 작은쪽의 index와 c[]의 index를 하나씩 증가시킴.
• a[], b[] 어느 한쪽의 index가 범위를 넘으면 (즉, 모두 c[]로 옮겨지면) 다른 array의 나머지 element를 모두 순서대로 c[]에 옮김.
a
a[i]
0 1 2 .... M-1 b
b[j]
0 1 2 .... N-1
c
c[k]
0 1 2 ... ... N+M-2
14
File "mergesort.h"의 내용.
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
void merge(int a[], int b[], int c[], int m, int n);
void mergesort(int key[], int n);
void wrt(int key[], int sz);
15
File "merge.c"의 내용.
#include "mergesort.h"
void merge(int a[], int b[], int c[], int m, int n) {
int i = 0, j = 0, k = 0; while (i < m && j < n) if (a[i] < b[j]) c[k ++] = a[i++]; else c[k++] = b[j++];
while (i < m) c[k++] = a[i++];
while (j < n) c[k++] = b[j++];
}
16
File "mergesort.c"의 내용.
#include "mergesort.h" void mergesort(int key[], int n) { int j , k, m, *w; for (m = 1; m < n; m = m * 2) ; if (n < m) { printf("\nERROR : Array size must be a power of 2.\n"); exit(1); }
w = calloc(n, sizeof(int)); assert(w != NULL); for (k = 1; k < n; k = k * 2) { for (j = 0; j < n - k; j = j + 2 * k) merge(key + j, key + j + k, w + j, k, k); for (j = 0; j < n; ++j) key[j] = w[j]; } free(w); }
17
n = 8의 경우
merge(key+j, key+j+k, w+j, k, k);
k j(=j+2k) n-k function call .
1 0 7 merge(key+0, key+0+1, w+0, 1, 1); A
1 2 7 merge(key+2, key+2+1, w+2, 1, 1); B
1 4 7 merge(key+4, key+4+1, w+4, 1, 1); C
1 6 7 merge(key+6, key+6+1, w+6, 1, 1); D
key
0 1 2 3 4 5 6 7
A B C D
18
k j(=j+2k) n-k function call .
2 0 6 merge(key+0, key+0+2, w+0, 2, 2); A
1 4 7 merge(key+4, key+4+2, w+4, 2, 2); B
k j(=j+2k) n-k function call .
4 0 4 merge(key+0, key+0+4, w+0, 4, 4); A
key
0 1 2 3 4 5 6 7
A B
key
0 1 2 3 4 5 6 7
A
19
합병정렬의 효율성 분석:
- array(data)의 크기가 2의 거듭제곱인 경우에 효율적
- data 비교횟수 분석: N = 32 = 25의 경우
k=1 (1:1) 16set(1회(≈2)/set 비교) 비교횟수≤32회
k=2 (2:2) 8set(max 3회(≈4)/set) 비교횟수≤32회
k=4 (4:4) 4set(max 7회(≈8)/set) 비교횟수≤32회
k=8 (8:8) 2set(max 15회(≈16)/set) 비교횟수≤32회
k=16 (16:0) 정렬완료
외부 loop: 4회(≈5회; log2 32 = log2 25 = 5 log2 2 = 5)
내부 loop: ≈32회 비교(= N)
총 loop횟수: ≤ 5 * 32 (= log2N * N = N log2 N)
time complexity: O(n log n): Bubble Sort에 비해 매우 양호
20
6.10 문자열
- char형의 1차원 배열.
- 문자열은 '\0'(NULL)로 끝남.
- 문자열의 크기는 '\0'를 포함하며 가변임.
(예) "abc" : 크기 = 4, 마지막 element = '\0'
- char *p = "abc"; /* char *p; p = "abc"; */
printf("%s %s\n", p, p + 1);
실행결과 : abc bc
• p는 문자열 "abc"의 'a'의 주소 값을 가짐.
• printf()는 p에서 시작하여 '\0'의 직전까지 print.
- 문자열 상수는 pointer로 취급됨.
"abc"[1] == 'b'
*("abc" + 2) == 'c'
21
- char *p = "abcde";
char s[] = "abcde";
임시 memory 영역
(예) 문자열 내의 단어의 수를 세는 program.
#include <stdio.h> #include <ctype.h>
int word_count (const char *a); void main() { char *s = "Mary had a little lamb.";
printf("\n%s\n", s);
printf("\nNumber of words : %d", word_count(s));
}
a b c d e \0 s
a b c d e \0 p
22
int word_count (const char *s)
{
int count = 0;
while (*s != '\0') {
while( isspace(*s) ) /* skip space */ ++s;
if (*s != '\0') { /* found a word */
++ count;
while (!isspace(*s) && *s != '\0')
++s; /* skip the word */
} }
return count; }
23
6.11 표준 라이브러리에 있는 문자열 조작 함수(string.h)
- char *strcat(char *s1, const char *s2);
• 두개의 문자열 s1과 s2를 결합하여 s1에 결과 저장.
• s1은 결과를 저장할 수 있을 만큼의 충분한 공간을 point할 수 있도록 해야 함.
• 문자열 s1이 return 됨.
char *strcat(char *s1, register const char *s2) { register char *p = s1; while (*p) /* go to the end */ ++p; while (*p++ = *s2++) /* copy */ ; return s1; }
24
- int strcmp(const char *s1, const char *s2);
• 두 문자열이 인자로 전달됨.
• s1과 s2를 사전적 순서로 비교하여
s1이 작으면 음수, 크면 양수, 같으면 0을 return.
25
- char *strcpy(char *s1, const char *s2);
• 문자열 s2를 '\0'이 나올때까지 s1에 복사.
• s1의 내용은 s2의 내용으로 overwrite 됨
• s1에 충분한 공간이 마련되어야 함.
• pointer s1이 return됨.
char *strcpy(char *s1, register const char*s2) {
register char *p = s1;
while(*p++ = *s2++) ;
return s1;
}
26
- size_t strlen(const char *);
• '\0'을 제외한 문자의 개수를 returen.
• size_t는 부호없는 정수적 형.
• 4 bytes/word system에서는 unsigned int.
• 2 bytes/word system에서는 unsigned long.
size_t strlen(const char *s) {
size_t n;
for (n = 0; *s != '\0'; ++s)
++n;
return n;
}
27
선언 및 초기화
char s1[] = "beautiful big sky country",
s2[] = "how now brown cow";
수식 값
strlen(s1) 25
strlen(s2 + 8) 9
strcmp(s1, s2) 음의 정수
문장 출력되는 값
printf("%s", s1+10); big sky country
strcpy(s1 + 10, s2 + 8);
strcat(s1, "s!");
printf("%s", s1); beautiful brown cows!
28
6.12 다차원 배열
int a[100]; :일차원 배열
int b[2][7]; :이차원 배열
int c[5][3][2]; :삼차원 배열
29
- 2차원 배열
(예) int a[3][5];
- a[i][j]의 다른 표현
• *(a[i] + j)
• (*(a + i))[j]
• *((*(a + i)) + j)
• *(&a[0][0] + 5 * i + j)
1열 2열 3열 4열 5열
1행 a[0][0] a[0][1] a[0][2] a[0][3] a[0][4]
2행 a[1][0] a[1][1] a[1][2] a[1][3] a[1][4]
3행 a[2][0] a[2][1] a[2][2] a[2][3] a[2][4]
row(행) column(열)
30
형식 매개변수 선언
- 함수를 정의할 때 형식매개변수가 다차원 배열일 경우, 첫번째를 제외한 모든 크기를 명시해야 함.
(예) int a[3][5];
int sum(int a[][5]) {
int i, j, sum = 0;
for (i = 0; i < 3; ++i) for (j = 0; j < 5; ++j) sum = sum + a[i][j];
return sum; }
31
• 매개 변수의 다른 정의 : 함수 정의의 header에서만 동일함.
int a[][5]
int a[3][5] : 상수 3을 compiler는 무시
int (*a)[5]
(비교 : 1차원 배열)
int b[]
int b[3]
int *b
32
- 초기화
int a[2][3] = {1, 2, 3, 4, 5, 6};
int a[2][3] = {{1, 2, 3}, {4, 5, 6}};
int a[][3] = {{1, 2, 3}, {4, 5, 6}};
• 내부 중괄호가 생략되면 a[0][0], a[0][1], a[0][2],
a[1][0], a[1][1], a[1][2]의 순으로 초기화 함.
- typedef의 사용
#define N 3
typedef double scalar ;
typedef scalar vector[N];
typedef scalar matrix[N][N];
(또는 typedef vector matrix[N];)
33
(예) void add(vector x, vector y, vector z) {
int i;
for (i = 0; i < N; ++i) x[i] = y[i] + z[i]; }
scalar dot_product(vector x, vector y) {
int i;
scalar sum = 0.0;
for (i = 0; i < N; ++i) sum = sum + x[i] * y[i];
return sum; }
34
void multiply (matrix a, matrix b, matrix c) {
int i, j, k;
for (i = 0; i < N; ++i) { for (j = 0; j < N; ++j) { a[i][j] = 0.0; for (k = 0; k < N; ++k) a[i][j] = a[i][j] + b[i][k] * c[k][j]; } } }
= *
35
6.13 포인터 배열
(예) char * w[5];
w[0] = "Mary"; w[1] = "had"; w[2] = "a"; w[3] = "little"; w[4] = "lamb.";
↑
↑
↑
↑
↑
M a r y \0
h a d \0
a \0
l i t t l e \0
l a m b . \0
w[0] "Mary"
"had"
"a"
"little"
"lamb."
36
#include <stdio.h>
#define N 5
void main()
{
char *w[N]; int i;
w[0] = "Mary"; w[1] = "had"; w[2] = "a";
w[3] = "little"; w[4] = "lamb.";
printf ("\n%s %s %s %s %s\n",
w[0], w[1], w[2], w[3], w[4]);
printf ("\n%s %s %s %s %s\n",
*w, *(w+1), *(w+2), *(w+3), *(w+4));
}
37
#include <stdio.h>
#define N 5
void main()
{
char *w[N]; int i;
*w = "MARY"; *(w+1) = "HAD"; *(w+2) = "A";
*(w+3) = "LITTLE"; *(w+4) = "LAMB.";
for (i=0; i<N; ++i)
printf ("%s ", w[i]);
for (i=0; i<N; ++i)
printf ("%s ", *(w+i));
}
38
문자열 정렬
void sort_words(char *w[], int n) {
int i, j;
for (i = 0; i < n; ++i) for (j = i + 1; j < n; ++j) if (strcmp(w[i], w[j]) > 0) swap(&w[i], &w[j]); }
void swap(char **p, char **q) {
char *tmp; tmp = *p; *p = *q; *q = tmp; }
39
6.14 main( ) 함수의 인자
#include <stdio.h>
void main(int argc, char *argv[]) {
int i; printf("argc = %d\n", argc);
for (i = 0; i < argc; ++i) printf("argv[%d] = %s\n", i, argv[i]); }
my_echo 라는 이름의 실행 화일을 생성.
my_echo a is for apple.
(실행결과) argc = 5 argv[0] = my_echo argv[1] = a argv[2] = is argv[3] = for argv[4] = apple.
40
6.15 래기드 배열(ragged array)
6.16 인자로서의 함수
6.17 예제
6.18 함수 포인터의 배열
6.19 형 한정자 const와 volatile
생 략.