2015-1 프로그래밍언어 7. 포인터 (Pointer), 동적메모리할당 2015 년 4 월 4 일 교수김영탁 영남대학교공과대학정보통신공학과 (Tel : +82-53-810-2497; Fax : +82-53-810-4742 http://antl.yu.ac.kr/; E-mail : ytkim@yu.ac.kr)
Outline 포인터 (pointer) 란? 간접참조연산자 포인터사용시주의할점 포인터연산 포인터와배열 포인터와함수 이중포인터 (double pointer) 포인터배열 (array of pointers) 배열포인터 (pointer of array) 함수포인터 다차원배열과포인터 const pointer, volatile pointer, void pointer main 함수의 argument 동적메모리할당과포인터 7-2
포인터란? 포인터 (pointer): 주소를가지고있는변수 1003 1004 1005 영화관 1002 1006 1001 포인터 (pointer) 1007 7-3
변수와메모리 변수의크기에따라서차지하는메모리공간이달라진다. char형변수 : 1바이트, int형변수 : 4바이트, int main(void) { int i = 10; char c = 69; float f = 12.3; } 변수값변수이름 10 i c 69 12.3 f 주소 7-4
변수의주소 변수의주소를계산하는연산자 : & 변수 i의주소 : &i 변수값변수이름주소 10 i c 69 12.3 f &i &c &f 7-5
변수의주소 int main(void) { int i = 10; char c = 69; float f = 12.3; } printf("i의주소 : %u n", &i); // 변수 i의주소출력 printf("c의주소 : %u n", &c);// 변수 c의주소출력 printf("f의주소 : %u n", &f); // 변수 f의주소출력 return 0; i 의주소 : 1245024 c 의주소 : 1245015 f 의주소 : 1245000 7-6
포인터의선언 포인터 : 변수의주소를가지고있는변수 7-7
포인터의선언 7-8
포인터와변수의연결 int i = 10; int *p = &i; // 정수형변수 i 선언 // 변수 i 의주소가포인터 p 로대입 7-9
다양한포인터의선언 char c = 'A'; float f = 36.5; double d = 3.141592; // 문자형변수 c // 실수형변수 f // 실수형변수 d char *pc = &c; float *pf = &f; double *pd = &d; // 문자를가리키는포인터 pc // 실수를가리키는포인터 pf // 실수를가리키는포인터 pd pc A c pf f 36.5 pd d 3.141592 포인터 7-10 변수
간접참조연산자 간접참조연산자 *: 포인터가가리키는곳의값을가져오는연산자 int i; int *p=&i; printf( %d, *p): 7-11
간접참조연산자의해석 간접참조연산자 : 지정된위치에서포인터의타입에따라값을읽어들인다. int *p = 8; // 위치 8에서정수 (4 bytes) 를읽는다. char *pc = 8; // 위치 8에서문자 (1 byte) 를읽는다. double *pd = 8; // 위치 8에서실수 (8 bytes) 를읽는다. 7-12
& 연산자와 * 연산자 7-13
포인터예제 #1 #include <stdio.h> int main(void) { int i = 3000; int *p = &i; printf("&i = %u n", &i); printf("i = %d n", i); p // 변수와포인터연결 // 변수의주소출력 // 변수의값출력 3000 i printf("*p = %d n", *p); printf("p = %u n", p); // 포인터를통한간접참조값출력 // 포인터의값출력 } return 0; i = 3000 &i = 1245024 *p = 3000 p = 1245024 7-14
포인터예제 #2 #include <stdio.h> int main(void) { int x=10, y=20; int *p; p = &x; printf("p = %d n", p); printf("*p = %d n n", *p); p 10 x 20 y } p = &y; printf("p = %d n", p); printf("*p = %d n", *p); return 0; p = 1245052 *p = 10 p = 1245048 *p = 20 7-15
#include <stdio.h> int main(void) { int i=10; int *p; 포인터예제 #3 p 10 i 20 p = &i; printf("i = %d n", i); } *p = 20; printf("i = %d n", i); return 0; i= 10 i= 20 7-16
Pointing to Recall: int *p1; double *p2; char *p3; int v1, v2; double d; char ch; p1 = &v1; p2 = &d; p3 = &ch; Two ways to refer to v1 now: Variable v1 itself: v1 = 100; Via pointer p1: *p1 = 200; Dereference operator, * Pointer variable "de-rereferenced" Means: "Get data that p1 points to" p1 = &v1; p2 = &d; p3 = &ch; p1 p2 p3 v1 v2 d ch 7-17
포인터사용시주의점 초기화가안된포인터를사용하면안된다. int main(void) { int *p; // 포인터 p는초기화가안되어있음 } *p = 100; // 위험한코드 return 0; 주소가잘못된것같은데 p? 7-18
포인터사용시주의점 포인터가아무것도가리키고있지않는경우에는 NULL 로초기화 NULL 포인터를가지고간접참조하면하드웨어로감지할수있다. 포인터의유효성여부판단이쉽다. 포인터가아무것도가리키지않을때는반드시 NULL 로설정하세요. NULL x 7-19
포인터사용시주의점 포인터의타입과변수의타입은일치하여야한다. #include <stdio.h> int main(void) { int i; double *pd; pd = &i; *pd = 36.5; // 오류! double 형포인터에 int 형변수의주소를대입 } return 0; pd double * i int 7-20
포인터연산 가능한연산 : 증가, 감소, 덧셈, 뺄셈연산 증가연산의경우증가되는값은포인터가가리키는객체의크기 포인터타입 ++ 연산후증가되는값 Char * 1 Short * 2 int * 4 float * 4 double * 8 포인터의증가는일반변수와는약간다릅니다. 가리키는객체의크기만큼증가합니다. 100 100 double short float int * * char * ++ 연산 104 108 102 101 7-21
증가연산예제 // 포인터의증감연산 #include <stdio.h> int main(void) { char *pc; int *pi; double *pd; 10000 10001 10004 10000 10000 10008 pc pi pd pc = (char *)10000; pi = (int *)10000; pd = (double *)10000; printf(" 증가전 pc = %d, pi = %d, pd = %d\n", pc, pi, pd); pc++; pi++; pd++; printf(" 증가후 pc = %d, pi = %d, pd = %d\n", pc, pi, pd); 증가전 pc = 10000, pi = 10000, pd = 10000 증가후 pc = 10001, pi = 10004, pd = 10008 } return 0; 7-22
포인터의증감연산 pc-1 pc-2 pi-1 Pd-1 pi-2 <pc <pi 는 char int 형포인터 > > 3.141592 f a 10 i 변수값변수이름주소 <pd 는 double 형포인터 > pc+1 pc+2 pi+1 pd+1 pi+2 pd pc pi
*p++; 간접참조연산자와증감연산자 p 가가리키는위치에서값을가져온후에 p 를증가한다. (*p)++; p 가가리키는위치의값을증가한다. 수식 의미 v=*p++ p 가가리키는값을 v 에대입한후에 p 를증가한다. v = (*p)++ p 가가리키는값을 v 에대입한후에가리키는값을증가한다. v = *++p p 를증가시킨후에 p 가가리키는값을 v 에대입한다. v = ++*p p 가가리키는값을가져온후에그값을증가하여 v 에대입한다. 7-24
간접참조연산자와증감연산자 // 포인터의증감연산 #include <stdio.h> int main(void) { int i = 10; int *pi = &i; printf("i = %d, pi = %p\n", i, pi); (*pi)++; printf("i = %d, pi = %p\n", i, pi); 10 11 i 0012FF60 0012FF64 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; 7-25
포인터의형변환 (pointer type conversion) C 언어에서는꼭필요한경우에, 명시적으로포인터의타입을변경할수있다. double *pd = &f; int *pi; pi = (int *)pd; 7-26
간접참조연산자와증감연산자 #include <stdio.h> int main(void) { char buffer[8]; double *pd; int *pi; char 형포인터를 double 형포인터로변환배열의이름은 char 형포인터이다. pd = (double *)buffer; *pd = 3.14; printf("%f\n", *pd); pi = (int *)buffer; *pi = 123; *(pi+1) = 456; char 형포인터를 int 형포인터로변환 3.140000 123 456 } printf("%d %d\n", *pi, *(pi+1)); return 0; 7-27
포인터와배열 배열과포인터는아주밀접한관계를가지고있다. 배열이름은주소값을변경할수없는포인터이다. 포인터는배열이름처럼사용이가능하다. 배열 포인터 7-28
포인터와배열 // 포인터와배열의관계 #include <stdio.h> int main(void) { int a[] = { 10, 20, 30, 40, 50 }; printf("&a[0] = %u n", &a[0]); printf("&a[1] = %u n", &a[1]); printf("&a[2] = %u n", &a[2]); printf("a = %u n", a); } return 0; &a[0] = 1245008 &a[1] = 1245012 &a[2] = 1245016 a = 1245008 7-29
포인터와배열 // 포인터와배열의관계 #include <stdio.h> int main(void) { int a[] = { 10, 20, 30, 40, 50 }; printf("a = %u\n", a); printf("a + 1 = %u\n", 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 7-30
포인터와배열 포인터는배열이름처럼사용할수있다. 즉인덱스표기법을포인터에사용할수있다. 7-31
포인터를배열처럼사용 // 포인터를배열이름처럼사용 #include <stdio.h> int main(void) { int a[] = { 10, 20, 30, 40 }; int *p; 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 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; p 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]); } return 0; 변수값변수이름주소 a 60 10 20 70 a[0] a[1] 30 80 a[2] 40 a[3] p p[0] p[1] p[2] p[3] 7-32
포인터를사용한방법의장점 포인터가인덱스표기법보다빠르다. Why?: 원소의주소를계산할필요가없다. int get_sum1(int a[], int n) { int i; int sum = 0; for(i = 0; i < n; i++ ) sum += a[i]; return sum; } 인덱스표기법사용 int get_sum2(int a[], int n) { int i; int *p; int sum = 0; p = a; for(i = 0; i < n; i++ ) sum += *p++; return sum; } 포인터사용 7-33
#include <stdio.h> 배열의원소를역순으로출력 void print_reverse(int a[], int n); int main(void) { int a[] = { 10, 20, 30, 40, 50 }; print_reverse(a, 5); return 0; } 50 40 30 20 10 void print_reverse(int a[], int n) { int *p = a + n - 1; // 마지막노드를가리킨다. while(p >= a) // 첫번째노드까지반복 printf("%d\n", *p--); // p가가리키는위치를출력하고감소 } p p p p p 변수값변수이름 주소 10 20 30 40 a[0] a[1] a[3] 7-34 a[2] 50 a[4]