PPT

C언어- 포인터(POINTER)

MSNU 2016. 9. 3. 17:21

















이번 장에서 학습핝 내용 .포인터란? .변수의주소.포인터의선언.간접참조연산자.포인터연산.포인터와배열.포인터와함수이번장에서는포인터의기초적인지식을학습한다. 포인터란? . 포인터(pointer): 주소를 가지고 있는 변수 1001 1002 1003 1004 1005 1006 1007 영화관포인터(pointer) 메모리의 구조 . 변수는 메모리에 저장됨 . 메모리는 바이트 단위로 액세스됨 . 0번지, 100 번즞 변수와 메모리 . 변수의 크기에 따라서 차지하는 메모리 공간이 다륽 . char 형: 1 바이트; int 형: 4 바이트; … int i = 10; char c = 69; float f = 12.3F; 변수값 변수명 주소 10 i 12.3 f 69 c 주소 연산자 & . & : 변수의 주소를 계산하는 연산자 . 변수 i의 주소 : &i 변수값 변수명 주소 10 i 12.3 f 69 c &i &c &f 변수의 주소 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의주소: 1245024 c의주소: 1245015 f의주소: 1245000 포인터 변수 . inti = 10; . int *p; // 정수 포인터 변수 선언 p = &i; //p 는 i를 가리킴 . int*p = &i; // 정수 포인터 변수 선언 및 초기화 포인터 변수 char c = 'A'; // char 형 변수 c float f = 36.5F; // float 형 변수 f double d = 3.141592; // double 형 변수 d char *pc = &c; // char 형을 가리키는 포인터 변수 pc float *pf = &f; // float 형을 가리키는 포인터 변수 pf double *pd = &d; // double 형을 가리키는 포인터 변수 pd pc pf pd A 36.5 3.141592 d f c 간. 참조 연산자 * . * : 포인터가가리키는대상을참조하는연산자 inti =10; int *p = &i; printf("%d", *p); & 연산자와 * 연산자 포인터 예제 #include int main(void) { int i = 3000; p 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; } // 포인터를통한간접참조값출력 i = 3000 &i = 1245024 *p = 3000 p = 1245024 3000 i 터 예제 #include 20 y p 10 x 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 = %u\n", (unsigned)p); printf("*p = %d\n", *p); return 0; } p = 1245052 *p = 10 p = 1245048 *p = 20 포인터예제 i = 10 i = 20 포인터예제 i = 10 i = 20 #include int main(void) { int i = 10, *p; p = &i; printf("i = %d\n", i); *p = 20; printf("i = %d\n", i); return 0; } 포인터 사용시 주의점 . 초기화되지 않은 포인터를 사용하면 안 됨 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) … 포인터 사용시 주의. . 포인터의 타입과 변수의 타입은 일치해야 함 #include int main(void) { int i; double *pd; i int pd double * pd = &i; *pd = 36.5; // double 형 포인터에 int 형 변수의 주소 대입 // 형변환: int * . double * --C: warning, C++: error } return 0; 포인터 연산 . 포인터++, 포인터--, 포인터 ± 정수 : 포인터가 가리키는 대상의 크기(포인터 타입의 크기 )만큼 증감 /덧셈/뺄셈 수행 포인터타입++연산후증가되는값char 1 short 2 int 4 float 4 double 8 100 p++ 104 p: int* 100 p++ 108 p: double * 100 p + 2 108 p: int* 100 p + 2 116 p: double * 100 p--96 p: int* 100 p--92 p: double * 포인터 연산 . pi-1 pc-2 . pc-1 . pc . . pi pc + 1 . pc + 2 . . pi + 1 . pi + 2 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 char *pc int *pi 포인터 연산 #include 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++; return 0; } printf("증가후pc = %d, pi = %d, pd = %d\n", (int)pc, (int)pi, (int)pd); 증가전pc = 10000, pi = 10000, pd = 10000 증가후pc = 10001, pi = 10004, pd = 10008 간. 참조 연산자와 증감 연산자 . *p++ . *(p++) : p 를 나중에 증가 . (*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 를 먼저 증가 간. 참조 연산자와 증감 연산자 #include int main() { int i = 10; int *pi = &i; printf("i = %d, pi = %p\n", i, pi); (*pi)++; printf("i = %d, pi = %p\n", i, pi); printf("i = %d, pi = %p\n", i, pi); *pi++; printf("i = %d, pi = %p\n", i, pi); i = 10, pi = 0012FF60 i = 11, pi = 0012FF60 i = 11, pi = 0012FF60 i = 11, pi = 0012FF64 return 0; } 포인터의 형변환 #include 명시적으로 포인터 타. 변경 가능 int main() { double d, *pd = &d; char buffer[8]; int *pi = (int*)pd; 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)); 3.140000 123 456 return 0; } 포인터와 배엱 . 배열과 포인터는 아주 밀접한 관계를 가지고 있음 . 배열읁 포인터처럼 사용 가능 . 배열의 이름은 배열읁 가리키는 포인터로 사용 가능 . 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] 배열 포인터 포인터와 배엱 #include 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] 1245008 &a[1] 1245012 &a[2] 1245016 a 1245008 배열읁 포인터처럼 사용 #include 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 = 1245008 a + 1 = 1245012 *a = 10 *(a + 1) = 20 #include 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("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", p[0], p[1], p[2]); 포인터를배열처럼사용 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 return 0; } 포인터와 배열의 유사. . int a[5]; . int a[5], *p = a; . *(a + i) . a[i] . a[i] ≡ p[i] ≡ *(a + i) ≡ *(p + i) . a +i . &a[i] . &a[i] ≡ &p[i] ≡ a + i ≡ p + i . a . a +0 . &a[0] . int *p; . p[i] . *(p + i) . &p[i] . p +i 포인터와 배열의 차이. . 배엱 이름은 상수 . 함수의 인자에서는 배열처럼 . int a[5], *p; 선언됙 것도 실제로는 포인터 . void f(int a[10]) { int *p; … } . p =a; //O, p =&a[0] . void f(int *a) { int *p; … } . p++; // O . a = p; //O . Cf.) intn; n =3; n++; //O . a++; // O . a =p; //X . a++; // X . sizeofa :4 ( 또는 8) . Cf.) int n; 3 =n; 3++; // X . sizeof a : 20 . sizeofp :4 ( 또는 8) 포인터의 관계 연산과 산술 연산 a[0] a[1] a[2] a[3] a[4] a[5] . p, q 가 같은 타입의 포인터이고 . inta[5], *p = a + 1, *q = a + 5; n이 정수읹 경욪 . !p, p == NULL, p != NULL . p ==q, p !=q p p+2 q . p >q, p >=q, p 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; 50 40 30 20 10 } void print_reverse(const int a[ ], int n) { const int *p = a +n-1; // 마지막 원소를 가리키도록 초기화 while (p >= a) printf("%d\n", *p--); // *(p--) } 인자 전달 : call-by-value . 형식인자는 실인자와 별도로 존재 . 함수 호출 시 실인자의 값 . 메모리 사용과 실행 속도 측면에서 비효율적. . 형식인자를 변경해도 실인자는 바뀌즞 않음 . 부작용(side effect) 이 없음 . C는 call-by-value 방식만 지원 복사 x = 1; f(x); // x = 1 void f(int y) { y++; } 1 x 1 .2 y 인자 전달 : call-by-reference . 형식인자는 실인자는 동읹 . 함수 호출 시 실인자의 값읁 . 메모리 사용과 실행 속도 측면에서 효율적. . 형식인자를 변경하면 실인자도 바뀜 . 부작용(side effect) 이 있음 . C++는 call-by-value 와 call-by-reference 방식 모두 지원 x = 1; void f(int &y) { f(x); y++; 복사하즞 않음 //x=2 } x ≡ y 1 .2 포인터 인자 . 포인터 인자를 사용하면 . call-by-value 방식에서도 call-by-reference 효과 유발 가능 . side effect 유발 가능 . 실인자가 배열인 경욪 : 시작 포인터를 형식인자로 전달 . int a[5]; . f(a) . f(&a[0]) x = 1; void f(int *p) { f(&x); (*p)++; //x=2 } p 100 1 .2 x 100 #include void swap(int *px, int *py); int main() { int a = 100, b = 200; printf("a=%d b=%d\n", a, b); swap(&a, &b); printf("a=%d b=%d\n", a, b); return 0; } swap 함수 void swap(int *px, int *py) { int tmp; printf("*px=%d *py=%d\n", *px, *py); tmp = *px; *px = *py; *py = tmp; printf("*px=%d *py=%d\n", *px, *py); }
&a &b px py 100 200 a b a=100 b=200 *px 100 *py 200 *px 200 *py 100 a=200 b=100 scanf() 함수 . 변수에 값읁 저장하기 위하여 변수의 주소 전달 인자를 통해 2개 이상의 결과 반환 #include // 기울기와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); return 0; } 기울기는1.000000, y 절편은0.000000 기울기와y절편을인자를통해반환 배엱 인자 #include 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); printf("%d %d %d\n", a[0], a[1], a[2]); return 0; } void sub(int b[ ], int n) { b[0] = 4; b[1] = 5; b[2] = 6; } 1 2 3 4 5 6 포인터 반환 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