Download presentation
Presentation is loading. Please wait.
1
쉽게 풀어쓴 C언어 Express 제11장 포인터 C Express
2
이번 장에서 학습할 내용 이번 장에서는 포인터의 기초적인 지식을 학습한다. 포인터란? 변수의 주소 포인터의 선언
간접 참조 연산자 포인터 연산 포인터와 배열 포인터와 함수
3
포인터란? 포인터(pointer): 주소를 가지고 있는 변수 1004 1005 1003 1006 1002 1001 1007
영화관 1003 1006 1002 1001 포인터(pointer) 1007
4
메모리의 구조 변수는 메모리에 저장됨 메모리는 바이트 단위로 액세스 가능 0번지, 100번지
5
변수와 메모리 변수의 크기에 따라서 차지하는 메모리 공간이 다름 char 형: 1바이트; int 형: 4바이트; … 10 69
int i = 10; char c = 69; float f = 12.3F; 10 69 12.3 변수값 변수명 i c f 주소
6
주소 연산자 & &i &c &f & : 변수의 주소를 계산하는 연산자 변수 i의 주소: &i 69 10 12.3 i c f
변수값 변수명 i c f 주소 &i &c &f
7
변수의 주소 int main(void) { int i = 10; char c = 69; float f = 12.3F;
printf("i의 주소: %u\n", (unsigned)&i); // 변수 i의 주소 출력 printf("c의 주소: %u\n", (unsigned)&c); // 변수 c의 주소 출력 printf("f의 주소: %u\n", (unsigned)&f); // 변수 f의 주소 출력 return 0; } i의 주소: c의 주소: f의 주소:
8
포인터 변수 int i = 10; int *p; // 정수 포인터 변수 선언 p = &i; // p는 i를 가리킴
int *p = &i; // 정수 포인터 변수 선언 및 초기화
9
포인터 변수 pc pf pd A 36.5 3.141592 d f c char c = 'A'; // char 형 변수 c
float f = 36.5F; // float 형 변수 f double d = ; // double 형 변수 d char *pc = &c; // char 형을 가리키는 포인터 변수 pc float *pf = &f; // float 형을 가리키는 포인터 변수 pf double *pd = &d; // double 형을 가리키는 포인터 변수 pd pc pf pd A 36.5 d f c
10
간접 참조 연산자 * * : 포인터가 가리키는 대상을 참조하는 연산자 int i = 10; int *p = &i;
printf("%d", *p);
11
& 연산자와 * 연산자
12
포인터 예제 i p 3000 #include <stdio.h> int main(void) {
int i = 3000; int *p = &i; // 변수와 포인터 연결 printf("i = %d\n", i); // 변수의 값 출력 printf("&i = %u\n", (unsigned)&i); // 변수의 주소 출력 printf("*p = %d\n", *p); // 포인터를 통한 간접 참조 값 출력 printf("p = %u\n", (unsigned)p); // 포인터의 값 출력 return 0; } 3000 i p i = 3000 &i = *p = 3000 p =
13
포인터 예제 x y p 10 20 #include <stdio.h> int main(void) {
int x = 10, y = 20; int *p; p = &x; printf("p = %u\n", (unsigned)p); printf("*p = %d\n\n", *p); p = &y; printf("*p = %d\n", *p); return 0; } 20 y p 10 x p = *p = 10 p = *p = 20
14
포인터 예제 i = 10 i = 20 #include <stdio.h> int main(void) {
int i = 10, *p; p = &i; printf("i = %d\n", i); *p = 20; return 0; } i = 10 i = 20
15
포인터 사용시 주의점 초기화되지 않은 포인터를 사용하면 안 됨 NULL 포인터: 아무것도 가리키고 있지 않는 포인터
int *p; // p는 초기화되어 있지 않음 *p = 100; // 위험! NULL 포인터: 아무것도 가리키고 있지 않는 포인터 #define NULL 0 아무것도 가리키고 있지 않을 경우, 포인터를 NULL로 설정 p = NULL; p = 0; if (p == NULL) … // p가 아무것도 가리키지 않으면 if (p == 0) … if (!p) … if (p != NULL) … // p가 무엇이든 가리키고 있으면 if (p != 0) … if (p) …
16
포인터 사용시 주의점 i pd 포인터의 타입과 변수의 타입은 일치해야 함 double * int
#include <stdio.h> int main(void) { int i; double *pd; pd = &i; // double 형 포인터에 int 형 변수의 주소 대입 // 형변환: int * double * -- C: warning, C++: error *pd = 36.5; return 0; } pd double * i int
17
포인터 연산 포인터++, 포인터--, 포인터 ± 정수: 포인터가 가리키는 대상의 크기(포인터 타입의 크기)만큼 증감/덧셈/뺄셈 수행 포인터 타입 ++연산 후 증가되는 값 char 1 short 2 int 4 float double 8 101 100 p++ 102 104 p: int* 101 100 p++ 102 104 108 p: double * 101 100 p-- 102 104 96 p: int* 101 100 p + 2 102 104 108 p: int* 101 100 p-- 102 104 92 p: double * 101 100 p + 2 102 104 116 p: double *
18
포인터 연산 char *pc int *pi pi - 1 pc - 2 pc - 1 pc pi pc + 1
96 pi - 1 97 pc - 2 98 pc - 1 99 pc 100 pi pc + 1 101 pc + 2 102 103 104 pi + 1 105 106 107 108 pi + 2 109 110 111 char *pc int *pi
19
포인터 연산 #include <stdio.h> int main(void) {
char *pc; int *pi; double *pd; pc = (char*)10000; pi = (int*)10000; pd = (double*)10000; printf("증가 전 pc = %d, pi = %d, pd = %d\n", (int)pc, (int)pi, (int)pd); pc++; pi++; pd++; printf("증가 후 pc = %d, pi = %d, pd = %d\n", (int)pc, (int)pi, (int)pd); return 0; } 증가 전 pc = 10000, pi = 10000, pd = 10000 증가 후 pc = 10001, pi = 10004, pd = 10008
20
간접 참조 연산자와 증감 연산자 *p++ *(p++) : p를 나중에 증가
v = *p++; v = *p; p++; // p를 나중에 증가 v = (*p)++; v = *p; (*p)++; // *p를 나중에 증가 v = *++p; ++p; v = *p; // p를 먼저 증가 v = ++*p; ++(*p); v = *p; // *p를 먼저 증가
21
간접 참조 연산자와 증감 연산자 i = 10, pi = 0012FF60 i = 11, pi = 0012FF60
#include <stdio.h> int main() { int i = 10; int *pi = &i; printf("i = %d, pi = %p\n", i, pi); (*pi)++; *pi++; return 0; } i = 10, pi = 0012FF60 i = 11, pi = 0012FF60 i = 11, pi = 0012FF64
22
포인터의 형변환 #include <stdio.h>
int main() { char buffer[8]; double *pd; int *pi; pd = (double*)buffer; // char * double * *pd = 3.14; printf("%f\n", *pd); pi = (int*)buffer; // char * int * *pi = 123; *(pi + 1) = 456; printf("%d %d\n", *pi, *(pi + 1)); return 0; } 명시적으로 포인터 타입 변경 가능 double d, *pd = &d; int *pi = (int*)pd; (주의: 위험)
23
포인터와 배열 배열과 포인터는 아주 밀접한 관계를 가지고 있음 배열을 포인터처럼 사용 가능
배열의 이름은 배열을 가리키는 포인터로 사용 가능 int a[5]; a &a[0] a + 1 &a[1] *(a + 1) a[1] 포인터를 배열처럼 사용 가능 포인터에 인덱스 표기법 사용 가능 int a[5], *p = a; p[1] *(p + 1) a[1] 배열 포인터
24
포인터와 배열 #include <stdio.h> int main() {
int a[] = { 10, 20, 30, 40, 50 }; printf("&a[0] = %u\n", (unsigned)&a[0]); printf("&a[1] = %u\n", (unsigned)&a[1]); printf("&a[2] = %u\n", (unsigned)&a[2]); printf("a = %u\n", (unsigned)a); return 0; } &a[0] = &a[1] = &a[2] = a =
25
배열을 포인터처럼 사용 #include <stdio.h> int main(void) {
int a[] = { 10, 20, 30, 40, 50 }; printf("a = %u\n", (unsigned)a); printf("a + 1 = %u\n", (unsigned)(a + 1)); printf("*a = %d\n", *a); printf("*(a + 1) = %d\n", *(a + 1)); return 0; } a = a + 1 = *a = 10 *(a + 1) = 20
26
포인터를 배열처럼 사용 #include <stdio.h> int main() {
a[0]=10 a[1]=20 a[2]=30 p[0]=10 p[1]=20 p[2]=30 a[0]=60 a[1]=70 a[2]=80 p[0]=60 p[1]=70 p[2]=80 #include <stdio.h> int main() { int a[] = { 10, 20, 30, 40 }; int *p; p = a; printf("a[0]=%d a[1]=%d a[2]=%d \n", a[0], a[1], a[2]); printf("p[0]=%d p[1]=%d p[2]=%d \n\n", p[0], p[1], p[2]); p[0] = 60; p[1] = 70; p[2] = 80; printf("p[0]=%d p[1]=%d p[2]=%d \n", p[0], p[1], p[2]); return 0; }
27
포인터와 배열의 유사점 int a[5]; int a[5], *p = a; *(a + i) a[i]
a a + 0 &a[0] int *p; p[i] *(p + i) &p[i] p + i int a[5], *p = a; a[i] ≡ p[i] ≡ *(a + i) ≡ *(p + i) &a[i] ≡ &p[i] ≡ a + i ≡ p + i
28
포인터와 배열의 차이점 배열 이름은 상수 함수의 인자에서는 배열처럼 선언된 것도 실제로는 포인터 int a[5], *p;
p = a; // O, p = &a[0] p++; // O Cf.) int n; n = 3; n++; // O a = p; // X a++; // X Cf.) int n; 3 = n; 3++; // X sizeof a : 20 sizeof p : 4 (또는 8) 함수의 인자에서는 배열처럼 선언된 것도 실제로는 포인터 void f(int a[10]) { int *p; … } void f(int *a) { int *p; … } a = p; // O a++; // O sizeof a : 4 (또는 8)
29
포인터의 관계 연산과 산술 연산 p, q가 같은 타입의 포인터이고 n이 정수일 경우
!p, p == NULL, p != NULL p == q, p != q p > q, p >= q, p < q, p <= q p ± n, p++, p-- p - q : 연산 결과는 정수 p + q : 허용되지 않음 int a[5], *p = a + 1, *q = a + 5; p == q : 0 (False) p < q : 1 (True) p + 2 : &a[3] q – p : 4 (5 – 1) p – q : -4 (1 – 5) int *r = (p + q) / 2; // X int *r = p + (q – p) / 2; // O p + 2 &a[3] a[0] a[1] a[2] a[3] a[4] a[5] p p+2 q
30
포인터 사용의 장점 인덱스 표기법보다 빠름 인덱스 표기법 사용 포인터 사용
int get_sum1(const int a[ ], int n) { int i, sum = 0; for (i = 0; i < n; i++) sum += a[i]; return sum; } int get_sum2(const int *p, int n) { const int *end = p + n; int sum = 0; while (p < end) sum += *p++; return sum; } 인덱스 표기법 사용 포인터 사용
31
배열 역순 출력 #include <stdio.h>
void print_reverse(const int a[ ], int n); int main( ) { int a[ ] = { 10, 20, 30, 40, 50 }; print_reverse(a, sizeof a / sizeof a[0]); return 0; } void print_reverse(const int a[ ], int n) { const int *p = a + n - 1; // 마지막 원소를 가리키도록 초기화 while (p >= a) printf("%d\n", *p--); // *(p--) 50 40 30 20 10
32
인자 전달: call-by-value x y 1 1 2 형식인자는 실인자와 별도로 존재
함수 호출 시 실인자의 값 복사 메모리 사용과 실행 속도 측면에서 비효율적임 형식인자를 변경해도 실인자는 바뀌지 않음 부작용(side effect)이 없음 C는 call-by-value 방식만 지원 x = 1; f(x); // x = 1 void f(int y) { y++; } 1 x 1 2 y
33
인자 전달: call-by-reference
형식인자는 실인자는 동일 함수 호출 시 실인자의 값을 복사하지 않음 메모리 사용과 실행 속도 측면에서 효율적임 형식인자를 변경하면 실인자도 바뀜 부작용(side effect)이 있음 C++는 call-by-value와 call-by-reference 방식 모두 지원 x = 1; f(x); // x = 2 void f(int &y) { y++; } x ≡ y 1 2
34
포인터 인자 포인터 인자를 사용하면 call-by-value 방식에서도 call-by-reference 효과 유발 가능 side effect 유발 가능 실인자가 배열인 경우: 시작 포인터를 형식인자로 전달 int a[5]; f(a) f(&a[0]) x = 1; f(&x); // x = 2 void f(int *p) { (*p)++; } 1 2 x 100 p
35
swap 함수 &a 100 &b 200 <swap> <main> a b
#include <stdio.h> void swap(int *px, int *py); int main() { int a = 100, b = 200; printf("a=%d b=%d\n", a, b); swap(&a, &b); return 0; } void swap(int *px, int *py) { int tmp; printf("*px=%d *py=%d\n", *px, *py); tmp = *px; *px = *py; *py = tmp; } <main> 100 &a 200 &b <swap> px py a b a=100 b=200 *px=100 *py=200 *px=200 *py=100 a=200 b=100
36
scanf() 함수 변수에 값을 저장하기 위하여 변수의 주소 전달
37
인자를 통해 2개 이상의 결과 반환 #include <stdio.h> // 기울기와 y절편 계산
int get_line_parameter(int x1, int y1, int x2, int y2, float *slope, float *yintercept) { if (x1 == x2) return -1; *slope = (float)(y2 - y1) / (float)(x2 - x1); *yintercept = y1 - (*slope) * x1; return 0; } int main(void) { float s, y; if (get_line_parameter(3, 3, 6, 6, &s, &y) == -1) printf("에러\n"); else printf("기울기는 %f, y절편은 %f\n", s, y); 기울기와 y절편을 인자를 통해 반환 기울기는 , y절편은
38
배열 인자 #include <stdio.h> void sub(int b[ ], int n);
int main( ) { int a[3] = { 1, 2, 3 }; printf("%d %d %d\n", a[0], a[1], a[2]); sub(a, 3); return 0; } void sub(int b[ ], int n) { b[0] = 4; b[1] = 5; b[2] = 6; 1 2 3 4 5 6
39
포인터 반환 int *f(int x, int y) { int result; result = x + y;
return &result; } // (X) local auto 변수 주소 반환 … int *p = f(3, 4); int n = *p; // 존재하지 않는 메모리 참조 int g = 1; int *f( ) { return &g; } // global static 변수 주소 반환 … int *p = f( ); int n = *p; // n = 1 *f( ) = 2; // g = 2 int *f( ) { static int s = 1; return &s; } // local static 변수 주소 반환 … int *p = f( ); int n = *p; // n = 1 int *f(int *q) { *q = 1; return q; } // 포인터 인자 반환 … int n; int m = *f(&n); // n = 1, m = 1
Similar presentations