10 포인터 1
주소 Address( 주소 ) 메모리에는그메모리의저장장소의위치를나타내는주소값 주소 (address) 는 1 바이트마다 1 씩증가하도록메모리에는연속적인번호가구성 2
주소연산자 & & 변수 변수의주소값을알아내려면변수앞에주소연산자 & (ampersand) 를이용 주소값이용장단점 주소값을이용하면보다편리하고융통성있는프로그램이가능 그러나복잡하고어려운단점 3
예제 10-1 4
포인터변수 의미 포인터변수는일반변수와는다르게변수에저장되는값이메모리의주소 (address) 값만을저장할수있는특별한변수 포인터변수를이용하면프로그램이간결하고효율적이므로포인터변수를이용 포인터변수의선언 포인터변수는그포인터가가리키는변수의자료형에맞추어형을선언해야함 5
포인터변수선언 여러개의포인터변수를한번에선언 다음은 ptr1 만포인터변수 int* ptr1, ptr2, ptr3; 세변수를모두포인터변수로선언하려면다음과같이 int *ptr1, *ptr2, *ptr3; 아무값이없다는의미의 NULL 주소값 0 을의미 int *ptr = NULL; stdio.h 에정의 #define NULL ((void *) 0); 6
포인터변수의값 포인터변수는주소 (address) 값만을저장 포인터변수는주소 (address) 값만을저장할수있으므로초기값지정가능 위구문으로변수 ptr 에는변수 i 의주소값이저장 그러므로 *ptr 은변수 i 자체를의미 7
역참조연산자 참조 (reference) 포인터변수 ptr 은변수 i 를참조한다. 역참조연산자 * 역참조연산자는포인터변수앞에연산자 * 를붙이면그포인터가가리키는변수를지칭 *ptr == i 연산자 * 는포인터변수를뒤에기술하면역참조 (dereference) 연산자 8
예제 10-3 9
다양한포인터변수의자료유형 포인터변수도자료유형이존재 그러나포인터변수의크기는모두 4 바이트 int *pi; double *pd; 자료유형이호환이안되면오류발생 서로다른모든포인터변수는호환이안됨 char C = @ ; int *i = &C; // 경고발생 Char *pc = &C; 10
다양한포인터변수의필요성 동일한유형의포인터변수들간에만대입이가능한이유는무엇일까? 포인터변수는참조변수의시작주소값을저장하고, 이주소값을시작하여어디까지의변수크기로내용값을인지할지의여부를결정해야하기때문 포인터변수가참조하는변수의자료유형을알아야하기때문에모든일반변수가갖는다양한종류의포인터변수가필요 11
포인터변수연산 +, - 연산 정수 int 포인터변수인 ptr 에서포인터변수연산식인 (ptr+1) 의의미 ptr 이가리키는 int 형변수의다음에위치한 int 형변수의첫주소 int 형의저장공간의크기가 4 이므로 (ptr+1) 은 ptr 보다 4 가큰주소값 만일 ptr 이 double 형의포인터였다면 (ptr+1) 은 ptr 보다 8 이큰주소값 12
포인터변수변환 포인터변수는자동으로형변환 (cast) 이불가능하며필요하면명시적으로형변환을수행 char *pc = (char *)&i; 포인터변수 pc 는변수 i 의첫주소에서부터 1 바이트크기의 char 형자료를참조한다는것을의미 (*pc) 는변수 i 의첫주소에서부터 1 바이트에저장된값인 65 의아스키코드문자인 A 를참조 13
다양한포인터변수의변환 포인터변수와그변환 14
void * 자료유형 (void *) 는모든유형의포인터변수값을저장할수있는능력을가진자유로운포인터변수 int i = 100; double d = 5.2876; void *p; p = &i; p = &d; 변수 p 는 (int *) 이든 (double *) 이든상관없이모든포인터값을저장가능 (void *) 유형인변수가그주소값의변수를참조하려면바로참조할수는없으며유형변환에의하여참조가가능 int i = 100; void *p = &i; printf("p = %#p\n", p); // p 내부의저장값출력 printf("(int *)p = %#p\n", (int *)p); // p 내부의저장값을변환하여 printf("*(int *)p = %d\n", *(int *)p); // p 가참조하는변수를출력 //printf("*p = %d\n", *p); // 오류발생 15
예제 10-7 16
배열과포인터 포인터를이용하여배열의각원소를참조하는방법 다음과같은배열선언에서 int point[] = {95, 88, 76, 54, 85, 82}; 변수 point[] 는 int 형원소 6 개를저장할수있는배열을의미 그렇다면 point 는무엇으로이용할수있을까? 위에서 point 는배열의첫원소인 point[0] 의주소 point 는주소상수로배열의첫번째원소의주소값 point == &point[0] 주소상수 point 에역참조연산자 * 를이용하면바로변수 point[0] 를지칭하므로다음이성립 *point == point[0] 17
배열이름을이용한원소참조 배열이름을이용한 point + 1( 주소상수 + 1) 은무엇일까? 주소값 point 의다음원소의주소값을의미 즉 point + 1 은 &point[1] 을의미하며, point[1] 의주소값 주소값 point + 1 은실제의주소값으로살펴보면 point 의실제주소값에 int 형의크기만큼더한주소값을의미 배열이름인 point 와역참조연산자 * 를이용한식 *point == point[0] *(point + 1) == point[1] *(point + 2) == point[2] *(point + i) == point[i] 18
예제 10-8 19
포인터인자이용 함수 sumary 의함수헤더부분을기술하는다른방법 call by address 함수의인자로배열이나포인터를이용하는경우, 배열이름이나배열원소의첫번째원소의주소값을인자로넘기면됨 함수형식인자에포인터나배열을사용하는방법을주소에의한호출 (call by address) sum = sumaryf(point, 6); //sum = sumaryf(&point[0], 6); 20
예제 10-8 21
다중포인터 포인터의포인터 포인터변수의주소값을갖는변수를포인터의포인터 이러한포인터의포인터를다중포인터 변수선언에서 * 를여러번이용하여다중포인터변수를선언 int i = 10; int *pi = &i; int **dpi = π 다중포인터변수를이용하여일반변수를참조하려면역참조연산자를여러번이용 즉위의변수 dpi 를이용하여변수 i 의값을 20 으로수정하려면 **dpi = 20; 22
포인터배열 의미 포인터배열이란주소값을저장하는포인터를요소로하는배열 선언 포인터배열은다음과같이선언 int *pary[3]; int a = 50, b = 30, c = 40; pary[0] = &a; pary[1] = &c; pary[2] = &b; 23
이차원배열이름 이차원배열에서배열이름 tary 는무엇일까? 이차원배열에서배열이름 tary 는포인터상수 tary[0] 를가리키는포인터상수 int tary[][3] = {{1, 20, 12}, {3, 5, 16}}; 그러면 tary[0] 는무엇인가? 포인터상수 tary[0] 는배열의첫번째원소 tary[0][0] 의주소값 &tary[0][0] 을갖는포인터상수 결론적으로배열이름 tary 는포인터상수 tary[0] 의주소값을갖는포인터상수, 그러므로다음이성립 &tary[0][0] == tary[0] tary[0] == *tary 24
이차원배열의구조와의미 다음은포인터상수 tary[0] 와 tary 의모습을표현 점선인그림은상수를표현, 상수는저장장소는아님 25
이차원배열여러의미 일차원배열에서 연산자 sizeof ( 배열명 ) 를이용하면배열의크기 ( 바이트 ) 이차원배열에서 sizeof 연산을이용하면 sizeof (tary) == 24 ( 이차원배열의크기 = 6*4 ) sizeof (tary[0]) == 12 (1행의크기 = 4*3 ) sizeof (tary[0][0]) == 4 ( 원소의형인 int의크기 ) 연산 sizeof에서알수있듯이배열명 tary는이차원배열을대표하며, tary[i] 는 (i+1) 행을대표 포인터상수인 tary[0] 와 tary의역참조연산자를이용하면다음식만족 tary[0][0] == *tary[0] == 1 tary[0] == *tary == 12FF68 포인터상수인 tary[0] 가 &tary[0][0] 이라면 tary[1] 은 &tary[1][0], 역참조연산자를이용하면다음식만족 tary[0][0] == *tary[0] == 1 tary[1][0] == *tary[1] == 3 26
배열포인터상수 포인터상수인 tary[0] 의연산식 tary[0] + 1 은무엇을의미할까? 연산식 tary[0] + 1 은 &tary[0][1] 을의미 즉연산식 tary[0] + 1 은포인터상수 tary[0] 에다음정수에해당하는주소값을의미하므로 &tary[0][1] &tary[0][1] == (tary[0] + 1) == 12FF6C &tary[0][2] == (tary[1] + 2) == 12FF70 tary[0][1] == *(tary[0] + 1) == 20 tary[0][2] == *(tary[1] + 2) == 12 포인터상수인 tary 의연산식 tary + 1 은무엇을의미할까? 연산식 tary + 1 은포인터 tary 가가르키는배열상수의다음배열상수의주소값 즉연산식 tary + 1 은 &tary[1] 을의미 연산식 tary + i 은배열 tary 의 i+1 번째행의첫번째원소의주소값이있는상수를가리키는포인터 &tary[0][0] == tary[0] == *(tary) == 12FF68 &tary[1][0] == tary[1] == *(tary + 1) == 12FF74 tary[0][0] == *tary[0] == **(tary) == 1 tary[1][0] == *tary[1] == **(tary + 1) == 3 27
이차원배열포인터상수 배열이름을이용한원소참조 본포인터상수 tary 와 tary+1, tary[0], tary[1] 을배열과함께정리 이차원배열이름은이중포인터 다중포인터변수를이용하여일반변수를참조하려면 역참조연산자를여러번이용 즉배열이름인 tary 는이중포인터이며, 이 tary 를이용하여변수 tary[0][0] 의값을 20 으로수정하려면 **tary = 20; 28
이차원배열주소값표현 이차원배열에서의각원소의주소값을표현하는여러방법 int tary[][3] = {{1, 20, 12}, {3, 5, 16}}; 위배열 tary 에서첫번째원소인 tary[0][0] 의주소값을표현하는방법은 &tary[0][0], tary[0], *tary 방법 29
원소 tary[i][j] 표현 이차원배열에서원소 tary[i][j] 의저장값을표현하는여러방법 *( tary[i] +j ) *( *(tary + i) + j ) *( *tary + ( 열의수 )*i + j ) *( &tary[0][0] + ( 열의수 )*i + j ) ( *(tary + i) )[j] 마지막표현인 (*(tary + i))[j] 이복잡한구조 일차원배열에서다음식이성립함을확장한표현 30
2 차원배열포인터 다음과같은 2 차원배열을저장할수있는배열포인터 (pointer to array) 변수는무엇일까? int ary[][4] = {4, 5, 2, 6, 7, 8, 12, 51, 87, 99, 23, 43}; 변수 ptr 배열포인터는앞에서배운포인터배열과그선언방법이매우비슷하여혼동하기쉬우니주의하길바란다. int (*ptr) [4]; // 배열포인터 : 2 차원배열의주소값을저장 int *ptr[4]; // 포인터배열 : 배열원소가포인터인배열 31
전달의차이 call by value 함수의구현에서인자를사용하는방법중에서포인터가아닌일반변수를사용하면값에의한전달 (call by value) 방식 int increment(int number) { number++; return number; } 실인자로사용하는변수에는영향이없음 call by address 함수의인자로포인터변수를이용하는방식 함수구현부분의문장 (*num)++ 은 ++(*num) 으로하여도같은기능을수행 주의 : *num++ 나 *(num++) 로사용시같은결과를얻을수없음 void incrementbyaddress(int *num) { (*num)++; } 32
주소에의한전달의이해 call by address 33
함수포인터 함수포인터 (pointer to function) 변수 함수의주소값을저장하는변수 함수 add() 변수 pf 함수포인터 34
함수포인터배열 의미 포인터를원소로갖는함수포인터배열 배열의크기가 4 인함수포인터배열을선언하는문장 배열의각원소가가리키는함수 반환값이 void 이고인자목록이 (double*, double, double) 선언된배열에함수 4 개의주소값을저장하는문장 배열의선언과초기화문장으로간단히처리 35
C 로배우는프로그래밍기초 2 nd Edition 36