180 likes | 464 Views
처음으로 배우는 C 프로그래밍. 제4부 복합 데이터 형 제 8 장 배열, 주소, 포인터. grades[0] grades[1] grades[2] grades[3] grades[4] *grades *(grades+1) *(grades+2) *(grades+3) *(grades+4). grades. & grades[0]. 8.1 포인터로서의 배열명. 배열과 포인터는 밀접한 관련이 있음 배열명은 배열의 첫번째 원소의 주소와 동일함 예) int grades[5];
E N D
처음으로 배우는 C 프로그래밍 제4부 복합 데이터 형 제 8 장 배열, 주소, 포인터
grades[0] grades[1] grades[2] grades[3] grades[4] *grades *(grades+1) *(grades+2) *(grades+3) *(grades+4) grades &grades[0] 8.1 포인터로서의 배열명 • 배열과 포인터는 밀접한 관련이 있음 • 배열명은 배열의 첫번째 원소의 주소와 동일함 예) int grades[5]; • 주의할 사항 • grades 배열의 각 원소는 integer로써 2 바이트를 차지함 (기계종속) • 배열명 grades는 &grades[0] 와 동일하며, 포인터 상수임 • grades에 1을 더하면 다음 원소의 주소가 됨 (2 바이트 증가함) • &grades[3] == &grades[0] + (3*2)
8.1 포인터로서의 배열명 • 배열의 액세스 방법 : 2 가지 • 첨자를 이용한 access : Program 8-1 • 포인터를 이용한 access : Program 8-2 Program 8-1: #include <stdio.h> void main(void) { int i; int grades[] = {98, 87, 92, 79, 85}; for (i=0; i<5; i++) printf(“\n Element %d is %d”, i, grades[i]); } 실행 결과 : Element 0 is 98 Element 0 is 87 Element 0 is 92 Element 0 is 79 Element 0 is 85
grades[0] grades[1] grades[2] grades[3] grades[4] *g_ptr *(g_ptr+1) *(g_ptr+2) *(g_ptr+3) *(g_ptr+4) g_ptr &grades[0] 8.1 포인터로서의 배열명 Program 8-1: #include <stdio.h> void main(void) { int *g_ptr;; int grades[] = {98, 87, 92, 79, 85}; g_ptr = &grades[0]; for (i=0; i<5; i++) printf(“\n Element %d is %d”, i, *(g_ptr+i) ); }
18934 18936 18938 18940 18942 18943 nums[0] nums[1] n_pt 18934 8.2 포인터 연산 • 포인터 변수 • 포인터 (주소)를 값으로 가지는 변수 • 포인터 변수나 포인터 상수에 대하여 산술 연산이나 관계 연산을 실행할 수 있음 : 포인터 연산 • 포인터 연산의 결과는 의미 있는 주소가 되어야 함 (기억 장소의 의미 있는 영역을 가리켜야 함) • 포인터 연산의 결과는 숫자 연산과 다름을 주의 예) int nums[100]; /* 배열 nums의 시작 주소를 18934로 가정함 */ int *npt; n_pt = &nums[0]; /* 아래 문장과 동일한 의미 */ n_pt = nums;
8.2 포인터 연산 • 포인터 연산과 숫자 연산의 다른점 • n_pt++는 1 증가하는 것이 아니라 다음 원소의 주소가 됨 즉, 정수가 2바이트를 차지하는 기계에서는 n_pt++는 2 (바이트) 만큼 증가함 • 포인터 연산의 예 : pt_num은 포인터 변수임 *pt_num++; /* (*pt_num)++ */ *++pt_num; /* *(++pt_num) */ *pt_num--; /* (*pt_num)-- */ *--pt_num; /* *(--pt_num) */
8.2 포인터 연산 • 포인터 연산을 사용한 프로그램의 예 : Program 8-4 #include <stdio.h> void main(void) { int nums[5] = {16,54,7,43,-5}; int i, total = 0, *n_pt; n_pt = nums; for (i=0; i<= 4; ++i) total = total + *n_pt++; printf(“The total of the array elements is %d”, total); } 결과 : The total of the array elements is 115
8.2 포인터 연산 • 포인터의 초기화 • 포인터 변수를 주소값으로 초기화할 수 있음 int miles; int *pt_num = &miles; float prices[5]; float *zing = &prices[0]; float *zing = prices; prices = &prices[3]; /* 오류 : prices는 포인터 상수임 */
nums vals 120 120 2 18 1 27 16 8.3 배열의 전달과 사용 • 배열이 함수의 인자로 전달될 때 배열 자체가 전달되는 것이 아니라 배열의 주소 (첫 원소의 주소)가 전달됨 Program 8-6 : #include <stdio.h> void main(void) { int nums[5] = {2, 18, 1, 27, 16}; int find_max(int{], int); printf(“The maximum value is %d”, find_max(nums, 5)); } int find_max(int vals[], int num_els) { int i, max = vals[0]; for (i=1; i < num_els; ++i) if (max < vals[i]) max = vals[i]; return (max); }
8.3 배열의 전달과 사용 • Program 8-6의 변형들 • 포인터 변수로 배열을 받는 경우 • 함수 몸체에서도 포인터를 사용하는 방법 int find_max(int *vals, int num_els) { /* 포인터 변수 vals로 배열을 전달받음 */ int i, max = vals[0]; for (i=1; i < num_els; ++i) if (max < vals[i]) max = vals[i]; return (max); } int find_max(int *vals, int num_els) { /* 포인터 변수 vals로 배열을 전달받음 */ int i, max = *vals; for (i=1; i < num_els; ++i) if (max < *(vals+i)) max = *(vals+i); return (max); }
nums[0] nums[1] nums &nums[0][0] nums[0][0] nums[0][1] nums[0][2] nums[1][0] nums[1][1] nums[1][2] &nums[0] &nums[0][0] 8.3 배열의 전달과 사용 • 확장된 포인터 표기법 • 다차원 배열에 대한 포인터 접근법 • 차원이 높을 수록 복잡해짐 예) int nums[2][3] = {{16,18,20},{25,26,27}}; <그림 > 배열 및 포인터 상수와 관련된 메모리 구조
포인터 표시법 첨자 표시법 값 *nums[0] nums[0][0] 16 *(nums[0] + 1) nums[0][1] 18 *(nums[0] + 2 ) nums[0][2] 20 *nums[1] nums[1][0] 25 *(nums[1] + 1) nums[1][1] 26 *(nums[1] + 2) nums[1][2] 27 8.3 배열의 전달과 사용 이차원 배열에 대한 포인터와 첨자 사용의 비교
포인터 표시법 첨자 표시법 값 *( *nums) nums[0][0] 16 *(*nums + 1) nums[0][1] 18 *(*nums+ 2 ) nums[0][2] 20 *(*(nums+1)) nums[1][0] 25 *(*(nums + 1)+1) nums[1][1] 26 *(*(nums + 1)+2) nums[1][2] 27 8.3 배열의 전달과 사용 • 이차원 배열에 대한 포인터와 첨자 사용의 비교
포인터 표시법 첨자 표시법 값 *(*pt) nums[0][0] 16 *(*pt + 1) nums[0][1] 18 *(*pt + 2 ) nums[0][2] 20 *(*(pt+1)) nums[1][0] 25 *(*(pt+1)+1) nums[1][1] 26 *(*(pt+1)+2) nums[1][2] 27 8.3 배열의 전달과 사용 • 이차원 배열에 대한 포인터와 첨자 사용의 비교 이차원 배열 nums가 calc()로 전달되는 경우 함수 헤드는 다음과 같음 clac(pt) int pt[2][3]; 이 경우 아래와 같은 관계가 성립함
8.3 배열의 전달과 사용 • 기타 유의할 점들 • 포인터를 리턴하는 함수 • 함수의 선언부에 리턴될 데이터 형을 선언해야 함 예) int *calc(); /* 함수 calc()는 정수값을 가리키는 포인터를 리턴함 • 함수에 대한 포인터 선언 예) int (*calc)(); /* calc는 정수를 리턴하는 함수를 지시함 sum()이 정수를 리턴하는 함수라면 calc = sum은 유효함 이 경우, calc는 포인터 변수이고, sum는 함수 이름으로 포인터 상수임
8.4 자주 발생하는 에러들 • 존재하지 않는 배열 요소를 포인터로 참조하는 경우 예) nums가 10 개의 정수를 가지는 배열이라면 *(nums + 15)는 배열의 영역을 넘어감; 그러나 C 컴파일러는 배열 첨자의 한계를 검사하지 않으므로 컴파일시 에러가 발생하지 않음 • 포인터로 배열을 참조할 때 잘못된 주소의 사용과 간접 연산자를 남용하는 경우 예) 만일 pt가 포인터 변수이면 pt=&45; pt=&(miles + 10); • 위 두 표현식은 값에 대한 주소를 취하려 하기 때문에 오류이다. 그러나 pt = &miles + 10; 은 유효하다. 여기서 10은 miles의 주소에 더해지게 된다. 따라서 마지막 주소가 유효한 값을 가리키게 하는 것은 프로그래머의 책임이다.
8.4 자주 발생하는 에러들 • 레지스터 변수의 주소를 취하는 경우 예) register int total; int *pt_total; pt_total=&total; • 포인터 상수의 주소를 취하는 경우 예) int nums[25]; int *pt; pt=&nums; • 포인터 상수의 특성 이해 : 포인터 변수와의 차이 1. 포인터 상수의 주소는 취할 수 없다. 2. 포인터 상수에 저장된 주소는 변화시킬 수 없다.
8.5 요약 1. 배열명은 포인터 상수이고, 이 포인터 상수의 값은 배열의 첫번째 요소의 주소이다. 2. 배열을 참조할 때 첨자에 의한 참조는 항상 포인터에 의한 참조로 바뀔 수 있다. a[i]는 *( a + i)로 바꾸어 사용될 수 있음 3. 참조에 의한 호출(call by reference)로 전달된 배열은 함수에서 직접적으로 그 배열의 요소들을 접근할 수 있다. 4. 일차원 배열이 함수의 인자로 전달될 때는, 함수의 인자 선언에서 배열이나 포인터로 선언하여 사용될 수 있다. 따라서 아래의 선언은 동일하게 된다. float a[]; float *a; 5. 포인터는 증가, 감소, 그리고 비교 될 수 있다. 포인터에 가산, 감산되어지는 값의 단위은 포인터가 가리키고 있는 변수의 자료형에 의해 자동적으로 결정된다.