270 likes | 506 Views
C By Dissection. 제 9 장 Arrays and Pointers. 강 의 내 용. 9.1 1 차원 배열 9.2 예제 : 각 문자 수 세기 9.3 배열과 포인터와의 관계 9.4 포인터 연산과 요소의 크기 9.5 함수로 배열 전달하기 9.6 정렬 알고리즘 : 버블 정렬 9.7 2 차원 배열 9.8 다차원 배열 9.9 동적 메모리 할당. 9.1 1 차원 배열. 배열 이란 ? 데이터 형을 갖는 데이터 아이템들의 순서열
E N D
C By Dissection 제 9 장 Arrays and Pointers
강 의 내 용 • 9.1 1 차원 배열 • 9.2 예제 : 각 문자 수 세기 • 9.3 배열과 포인터와의 관계 • 9.4 포인터 연산과 요소의 크기 • 9.5 함수로 배열 전달하기 • 9.6 정렬 알고리즘 : 버블 정렬 • 9.7 2 차원 배열 • 9.8 다차원 배열 • 9.9 동적 메모리 할당
9.1 1차원 배열 • 배열 이란? • 데이터 형을 갖는 데이터 아이템들의 순서열 • 메모리에 순차적으로 저장되어 첨자(index)로 각 아이템들을 지정 • 메모리 할당은 기저 주소(base address)에서부터 이루어지며, 배열의 이름은 이 기저 주소를 가리키는 포인터 상수 • 배열의 각 원소들은 인덱스(index)라고도 부르는 첨자를 사용 • index는 반드시정수형 상수, 정수형 변수, 정수형 수식 • int grade[3]; • grade[0], grade[1], grade[2] : 세 개의 저장 공간 확보됨 • grade[3] = 3; /* ERROR 배열의 범위 벗어남 */
9.1 1차원 배열 • 변수이름 자체a에는 a[0]의 주소1000이 저장됨 • a는 포인터(주소를 담을 수 있는 저장 공간) 상수(변하지 않음)임. • &a[0] : 1000 • &a[1] : 1004 • &a[2] : 1008 • &a[3] : 1012 • &a[4] : 1016 • a : 1000
9.1 1차원 배열 #include <stdio.h> /*Fill and Print an array. */ #define N 5 int main(void) { int a[N]; /* allocate space for a[0] to a[4] */ int i, sum = 0; for (i = 0; i < N; ++i) a[i] = 7 + i * i; for (i = 0; i < N; ++i) printf("a[%d] = %d ", i, a[i]); for (i = 0; i < N; ++i)sum += a[i]; printf("\nsum = %d\n", sum); /* print the sum */ return 0; } a[0]=7, a[1]=8, a[2]=11 a[3]=16, a[4]=23 sum = 0 + 7 + 8 + 11 + 16 + 23 = 65
9.1 1차원 배열 • 초기화 • 배열은 선언문 내에서초기화될 수 있음 • float x[7] = {-1.1, 0.2, 33.0, 4.4, 5.05, 0.0, 7.7}; • x[0] = -1.1, x[2] = 0.2, x[3] = 33.0, … , x[6] = 7.7 이 입력됨 • int n[4] = {2, 7}; • n[0] = 2, n[1] = 7, n[2] = 0, n[3] = 0이 입력됨 • int a[] = {3, 4, 5, 6};는 int a[4] = {3, 4, 5, 6};과 같음 • a[0] = 3, a[1] = 4, a[2] = 5, a[3] = 6 • 배열 a의 원소 접근 (index) • int i, a[size]; • a[expr] = 4; /* 0 <= expr <= size-1 & 범위 매우 중요 */ • 범위 초과하면 실행 시간 오류 발생, 컴파일 체크(Ⅹ)
9.2 예제 : 각 문자 수 세기 #include <stdio.h> /* Count each uppercase letter separately. */ #include <ctype.h> int main(void) { int c, i, letter[26]; for (i = 0; i < 26; ++i) letter[i] = 0; /*init array to zero 매우 중요*/ while ((c = getchar()) != EOF) /* count the letters */ if (isupper(c)) ++letter[c - 'A']; /* letter[0] = ‘a’ or ‘A’… */ for (i = 0; i < 26; ++i) { /* print the results */ if (i % 6 == 0) printf("\n"); printf("%4c:%3d\n\n", 'A' + i, letter[i]); } return 0; }
9.3 배열과 포인터와의 관계 • 배열이름 자체는 주소 또는 포인터 값 • 포인터와 배열의 메모리 접근 방식은 거의 동일 • 포인터는 주소를 값으로 갖는변수 • 배열 이름은 상수 포인터로 취급되는 고정 주소 • * 표 • 변수 선언시 사용 : 포인터 변수 선언 • 산술 연산자 사용(c = a * b;) : 곱하기 연산자 • 포인터 변수 앞에 사용(*p = 100;) • 포인터 p가 가리키는 주소(메모리 공간)에 저장된 값(data)을 읽어오기, 저장하기, 수식 등에 사용됨 • *p = 100; /* 포인터 p가 가리키는 메모리 공간에100을 저장하라는 의미 */ int a[100], *p;
9.3 배열과 포인터와의 관계 int a[100], *p; • a[0]=2, a[1]=4, a[2]=8, … , a[99]=10 : 300, 304, 308, … , 696 번지의 메모리에 할당되었을 경우 • p = a; • p = &a[0]; 와 같으며… p에는 300 번지가 할당 됨 • *p의 값은? (2임) • 포인터는 주소 값을 담을 수 있는 메모리 공간 임 • 포인터 연산은 변수 형의 메모리 크기(sizeof(int) = 4)와 같은 크기로 이루어짐 • p = p + 2; ??? p에는 308 번지(300 + 2*4)할당 됨 • *p 의 값은? (8임) • *p = p[0], *(p+2) = p[2], *(p+i) = p[i]와 같음 /* 매우 중요 */
9.4 포인터 연산과 요소의 크기 • 포인터 연산에서의 요소의 크기는 • 포인터가 선언될 때의 변수형의 크기가 된다 • double *pt; • sizeof(double) = 8 : 포인터 연산의 크기 • 따라서… pt = 1000 번지 였다면… • pt = pt + 1; pt = 1008번지가 됨 • int *p; • sizeof(int) = 4 : 포인터 연산의 크기 • 따라서… p = 1000 번지 였다면… • p = p + 1; p = 1004번지가 됨
9.5 함수로 배열 전달하기 • 함수의 정의에서 배열로 선언된 형식 인자가 실제 포인터 • 배열이 함수의 인자로 전달 될 때 • 배열의 기저 주소는 값-기반 호출(call-by-value)로 전달 • 포인터는 주소를 담고 있는 메모리 공간 이므로 • 주소를 함수의 인자로 전달하는 방식을 call-by-reference라고 한다 int sum(int a[], int n) { int i, s = 0; for (i=0; i<n; ++i) s+= a[i]; return s; } int a[]와 int *a는 같은 표현임 단, 함수의 인자로 전달될 때만 배열을 전달 받아서 합계를 계산하여 반환하는 함수
9.6 정렬 알고리즘 : 버블 정렬 • 배열의 앞뒤원소를 순차적으로 비교하면서 바꿔 치기 하는 방식 • 작은 값 혹은 큰 값을 계속 앞으로 보내는 방식 • 반복을한 번하고 나면 가장 작은 값 혹은 큰 값이맨 앞으로 이동 됨 • 반복을n 번 하면 크기 순으로 정렬 됨 • 단점 : 반복 및 비교 횟수 많음(n2에 비례, 효율적인 방식 n*log2n에 비례) void swap(int *, int *); void bubble(int a[], int n) /* n is the size of a[] */ { int i, j; for (i = 0; i < n - 1; ++i) / * 반복을 n 번 */ for (j = n - 1; i < j; --j) / * 반복을 한 번 */ if (a[j-1] > a[j]) swap(&a[j-1], &a[j]); }
9.6 정렬 알고리즘 : 버블 정렬 for (j = n - 1; i < j; --j) if (a[j-1] > a[j]) swap(&a[j-1], &a[j]); a[0]=7, a[1]=3, a[2]=66, a[3]=3, a[4]=-5, a[5]=22, a[6]=-77, a[7]=2 a[0]=-77, a[1]=7, a[2]=3, a[3]=66, a[4]=3, a[5]=-5, a[6]=22, a[7]=2 a[0]=-77, a[1]=-5, a[2]=7, a[3]=3, a[4]=66, a[5]=3, a[6]=2, a[7]=22 a[0]=-77, a[1]=-5, a[2]=2, a[3]=7, a[4]=3, a[5]=66, a[6]=3, a[7]=22 a[0]=-77, a[1]=-5, a[2]=2, a[3]=3, a[4]=7, a[5]=3, a[6]=66, a[7]=22 a[0]=-77, a[1]=-5, a[2]=2, a[3]=3, a[4]=3, a[5]=7, a[6]=22, a[7]=66 a[0]=-77, a[1]=-5, a[2]=2, a[3]=3, a[4]=3, a[5]=7, a[6]=22, a[7]=66 a[0]=-77, a[1]=-5, a[2]=2, a[3]=3, a[4]=3, a[5]=7, a[6]=22, a[7]=66
9.7 2-차원 배열 • 2차원 배열 Syntax • int b[2][7]의 기억공간 (2*7 = 14개의 메모리 공간) • b[0][0], b[0][1], b[0][2], … , b[0][6] • b[1][0], b[1][1], b[1][2], … , b[1][6]
9.7 2-차원 배열 • 이차원배열의 elements • 이차원 배열의 기억장소를 앞 테이블 같이 그려보았지만 실제로 컴퓨터 메모리에 저장되는 기억장소는 다음과 같다. 일련의 메모리 공간에 차례대로 배열이 지정되는 것이다. row0 row1 row2 &a[0][0] &a[0][1] &a[1][0] &a[1][4] &a[0] &a[1] &a[1]
9.7 2-차원 배열 • 2차원 배열 element를 access하는 여러 가지 방법 • a[ i ]는 a의 i번째 행 • a[ i ][ j ]는 배열의 i번째 행과j번째 열의 원소 • 배열 이름a는 &a[0]와 같다. • a[i][j]형식으로 사용하는 것이 편리함
9.7 2-차원 배열 p a[0][0] a[0][1] a[0][2] a[1][0] a[1][1] a[1][2] a[0] a[1] p = &a[0][0] ; p = a[0]; p = a; p = &a[0]; 같은 표현 이지만컴파일 시 : Warning 발생 'int *' differs in levels of indirection from 'int (*)[3]'
9.7 2-차원 배열 int sum(int a[][5]) { int i,j, sum=0; for(i = 0; i < 3; ++i) for(j = 0; j < 5; ++j) sum += a[i][j]; return sum; } 이차원 배열을 함수의 인자로 전달하기 ** 매우 중요 ** ** 함수의 헤더부분 선언 시 다음은 모두 같은 표현 ** int a[][5], int(*a)[5], int a[3][5] : 모두 같은 표현 함수의 인자전달 매우 중요 반드시 크기 필요 int main (void) { int i, j, grade[3][5], sum_1=0, sum_2=0; for (i=0; i<3; ++i) for(j=0; j<5; ++j) grade[i][j] = i*10 + j; sum_1 = sum(grade); for (i=0; i<3; ++i) sum_2 = sum_2 + sum_one_dem(grade[i]); return 0; } /* sum_1 == sum_2 (? 같음?) */ int sum_one_dem (int *a); int sum(int a[][5]); 같은 일을 하는 두 개의 함수가 선언되어 있을 때
9.8 다차원 배열 int sum(int a[][9][2]) { int i,j,k sum=0; for(i = 0; i < 7; ++i) for(j = 0; j < 9; ++j) for(k = 0; k < 2; ++k) sum += a[i][j]; return sum; } 3차원 배열을 함수의 인자로 전달하기 ** 매우 중요 ** ** 함수의 헤더부분 선언 시 다음은 모두 같은 표현 ** int a[][9][2], int(*a)[9][2], int a[7][9][2] : 모두 같은 표현 함수의 인자전달 매우 중요 반드시 크기 필요 int main (void) { int grade[7][9][2], total=0; for (i=0; i<7; ++i) for(j=0; j<9; ++j) for(k=0; k<2; ++k) grade[I][j][k] = i+j+k; total = sum(grade); /* sum(int a[][9][2]) Call */ }
9.8 다차원 배열 • 다차원 배열의 초기화 모든 원소들은 0으로 초기화 된다.
9.9 동적 메모리 할당 • C-언어에서는 동적 메모리 할당을 위해 • calloc(), malloc(), free()함수 제공 함 • <stdlib.h>포함 시켜야 함 • 어디에서 메모리를 할당 받나? • 누구한테 : System OS으로부터 • 어디에서 : 메인 메모리의 Heap 부분에서 • heap? 일반적인 변수, 함수를 위한 메모리와 다른 영역임 • 할당 받은 것이기 때문에…반드시 반환해야 함(free) • 왜 동적 할당을 쓰나 ? • 시스템의 메인 메모리는 한정된 자원임 • 한정된 메모리 공간을 효율적으로 사용하기 위함
9.9 동적 메모리 할당 • 동적 할당 함수 • void *calloc(size_t n, size_t size); • size_t Type ? : 일반적으로 unsigned int • void *:할당 받은 메모리의 시작 주소 • size_t n: (size 크기를 같는)n 개의 메모리 블록 할당 • size_t size:할당 되는 메모리의 한 개의 블록 크기 • 따라서 전체의 할당 메모리 크기는 n*size바이트 임 • 동적 할당 함수는 포인터를 반환하므로 할당 받은 메모리는 포인터 변수를 이용해야 만이 사용 가능(저장, 읽기, 수식 사용 등등) int *a; a = (int *)calloc(10, sizeof(int)); /* a[0], a[1], … , a[9] */] a[0] = 10; … if(a[2] == 1) … ; … ; 사용이 끝나면 free(a); /* 반드시 해야 함 */
9.9 동적 메모리 할당 • 동적 할당 함수 • void *malloc(size_t n); • calloc() 함수와 같으나 n바이트의 메모리 블록을 할당 int *a, *p; a = (int *)malloc(10*sizeof(int)); • calloc() 함수 : 할당 메모리 블록 초기화 됨 • malloc() 함수 : 할당 메모리 블록 초기화 안됨 • 두 함수 모두 실패하면 NULL 포인터 값 반환 됨 • 반환 성공 여부 확인 방법 if (a != NULL) …성공 else …실패 • 반드시 free() 해 줘야 함. free (a); /* 반환 된 뒤에는 사용 불가ex: a[2] = 10; (ERROR) */
9.9 동적 메모리 할당 #include <stdio.h> #include <stdlib.h> int main(void) { int *a, i, n, sum = 0; printf("\n%s", "An array will be created dynamically.\n\n" "Input an array size n followed by n integers: "); scanf("%d", &n); a = (int *)calloc(n, sizeof(int)); /* get space for n ints */ for (i = 0; i < n; ++i) scanf("%d", &a[i]); for (i = 0; i < n; ++i) sum += a[i]; free(a); /* free the space */ /* output routine 생략됨 */ return 0; /* 참고 사항 : output routine은 */ } /* free(a); 하기 전에 작성하는 것이 좋음 */
기 타 • 배열에서 매우 주의해야 할 것 • 배열변수의 첨자(index)값이 그 범위를 초과하는 것 • 동적 메모리 할당을 사용할 경우 • calloc(), malloc() 함수 사용 시 : 성공 여부 확인필요 • 반드시 할당 받은 메모리를 반환(free) 해야 함 • 포인터와 배열 그리고 함수 인자 전달에 관해서 • 포인터, 배열, 함수 인자 전달방식을 이해하면 C-언어가 보임 • 함수 인자 전달 방식 • 함수의 헤더 부분 선언 하는 요령ex: int sum(int *a, int n); • 함수의 선언 부분을 보고 함수를 Call 하는 요령 • 함수의 Call은 다른 함수 안에서 임ex: sum(&arry[0], 5);
포인터를 알면 C-언어가 보입니다. 수고하셨습니다. Array and Pointers