누구나즐기는 C 언어콘서트 제 9 장포인터
이번장에서학습할내용 포인터이란? 변수의주소 포인터의선언 간접참조연산자 포인터연산 포인터와배열 포인터와함수 이번장에서는포인터의기초적인지식을학습한다.
포인터란? 포인터 (pointer): 주소를가지고있는변수
메모리의구조 변수는메모리에저장된다. 메모리는바이트단위로액세스된다. 첫번째바이트의주소는 0, 두번째바이트는 1,
변수와메모리 변수의크기에따라서차지하는메모리공간이달라진다. char 형변수 : 1 바이트, int 형변수 : 4 바이트, int main(void) { int i = 10; char c = 69; float f = 12.3; }
변수의주소 변수의주소를계산하는연산자 : & 변수 i 의주소 : &i
변수의주소 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
포인터의선언 포인터 : 변수의주소를가지고있는변수 int i = 10; int *p; p = &i; // 정수형변수 i 선언 // 포인터의선언 // 변수 i 의주소가포인터 p 로대입
다양한포인터의선언 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
간접참조연산자 간접참조연산자 *: 포인터가가리키는값을가져오는연산자 int i = 10; int *p; p = &i; printf("%d\n", *p); // 10 이출력된다.
간접참조연산자의해석 간접참조연산자 : 지정된위치에서포인터의타입에따라값을읽어들인다. int *p = 8; // 위치 8 에서정수를읽는다. char *pc = 8; // 위치 8 에서문자를읽는다. double *pd = 8; // 위치 8 에서실수를읽는다.
포인터예제 #1 #include <stdio.h> int main(void) { int i = 3000; int *p; p = &i; // 변수와포인터연결 printf("i = %d\n", i); printf( &i = %u\n", &i); // 변수의값출력 // 변수의주소출력 } printf( p = %u\n", p); printf("*p = %d\n", *p); return 0; // 변수의값출력 // 포인터를통한간접참조값출력 i = 3000 &i = 1245024 p = 1245024 *p = 3000
포인터예제 #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 = &y; printf("p = %d\n", p); printf("*p = %d\n", *p); return 0; p = 1245052 *p = 10 p = 1245048 *p = 20
포인터예제 #3 #include <stdio.h> int main(void) { int i=10; int *p; p = &i; printf("i = %d\n", i); } *p = 20; printf("i = %d\n", i); return 0; i = 10 i = 20
중간점검 1. 메모리는어떤단위를기준으로주소가매겨지는가? 2. 다음의각자료형이차지하는메모리공간의크기를쓰시오. (a) char (b) short (c) int (d) long (e) float (f) double 3. 포인터도변수인가? 4. 변수의주소를추출하는데사용되는연산자는무엇인가? 5. 변수 x의주소를추출하여변수 p에대입하는문장을쓰시오. 6. 정수형포인터 p가가리키는위치에 25를저장하는문장을쓰시오.
포인터사용시주의점 #1 초기화가안된포인터를사용하면안된다. int main(void) { int *p; // 포인터 p 는초기화가안되어있음 } *p = 100; // 위험한코드 return 0;
포인터사용시주의점 #2 포인터가아무것도가리키고있지않는경우에는 NULL 로초기화 NULL 포인터를가지고간접참조하면하드웨어로감지할수있다. 포인터의유효성여부판단이쉽다.
포인터사용시주의점 #3 포인터의타입과변수의타입은일치하여야한다. #include <stdio.h> int main(void) { int i; double *pd; pd = &i; *pd = 36.5; // 오류! --double 형포인터에 int 형변수의주소를대입 } return 0;
포인터연산 가능한연산 : 증가, 감소, 덧셈, 뺄셈연산 증가연산의경우증가되는값은포인터가가리키는객체의크기 포인터타입 ++ 연산후증가되는값 char 1 short 2 int 4 float 4 double 8 포인터를증가시키면가리키는대상의크기만큼증가합니다.
증가연산예제 #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", pc, pi, pd); pc++; pi++; pd++; printf(" 증가후 pc = %d, pi = %d, pd = %d\n", pc, pi, pd); printf("pc+2 = %d, pi+2 = %d, pd+2 = %d\n", pc+2, pi+2, pd+2); } return 0; 증가전 pc = 10000, pi = 10000, pd = 10000 증가후 pc = 10001, pi = 10004, pd = 10008 pc+2 = 10003, pi+2 = 10012, pd+2 = 10024
포인터의증감연산
간접참조연산자와증감연산자 수식 의미 v = *p++ p 가가리키는값을 v 에대입한후에 p 를증가한다. v = (*p)++ p 가가리키는값을 v 에대입한후에가리키는값을증가한다. v = *++p p 를증가시킨후에 p 가가리키는값을 v 에대입한다. v = ++*p p 가가리키는값을가져온후에그값을증가하여 v 에대입한다.
간접참조연산자와증감연산자 // 포인터의증감연산 #include <stdio.h> int main(void) { int i = 10; int *pi = &i; i = 10, pi = 0012FF60 i = 11, pi = 0012FF60 i = 11, pi = 0012FF60 i = 11, pi = 0012FF64 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); } return 0;
중간점검 1. 포인터에대하여적용할수있는연산에는어떤것들이있는가? 2. int 형포인터 p 가 80 번지를가리키고있었다면 (p+1) 은몇번지를가리키는가? 3. p 가포인터라고하면 *p++ 와 (*p)++ 의차이점은무엇인가? 4. p 가포인터라고하면 *(p+3) 의의미는무엇인가?
포인터와배열 #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
포인터와배열
포인터와배열 // 포인터와배열의관계 #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
포인터와배열
포인터를배열처럼사용 // 포인터를배열이름처럼사용 #include <stdio.h> int main(void) { int a[] = { 10, 20, 30, 40, 50 }; 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]); } return 0; 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
포인터를배열처럼사용
중간점검 1. 배열의첫번째원소의주소를계산하는 2가지방법을설명하라. 2. 배열 a[] 에서 *a의의미는무엇인가? 3. 배열의이름에다른변수의주소를대입할수있는가? 4. 포인터를이용하여배열의원소들을참조할수있는가? 5. 포인터를배열의이름처럼사용할수있는가?
함수호출시인수전달방법 값에의한호출 (call-by-value) C의기본적인방법 인수의값만이함수로복사된다. 복사본이전달된다고생각하면된다. 참조에의한호출 (call-by-reference) C에서는포인터를이용하여흉내낼수있다. 인수의주소가함수로복사된다. 원본이전달된다고생각하면된다.
값에의한호출
참조에의한호출
swap() 함수 #1 변수 2 개의값을바꾸는작업을함수로작성 #include <stdio.h> void swap(int x, int y); int main(void) { int a = 100, b = 200; printf( a=%d b=%d\n",a, b); swap(a, b); void swap(int x, int y) { int tmp; tmp = x; x = y; y = tmp; } } printf( a=%d b=%d\n",a, b); return 0; a=100 b=200 a=100 b=200 변경되지않음!! Why?
swap() 함수 #1 값들이복사되었고원본변수에는아무런영향이없다.
swap() 함수 #2 포인터를이용 #include <stdio.h> void swap(int x, int y); int main(void) { } 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; a=100 b=200 a=200 b=100 변경되었음!! void swap(int *px, int *py) { int tmp; tmp = *px; *px = *py; *py = tmp; }
swap() 함수 #2
2 개이상의결과를반환 #include <stdio.h> // 기울기와 y 절편을계산 int get_line_parameter(int x1, int y1, int x2, int y2, float *slope, float *yintercept) { } if( x1 == x2 ) else { } return -1; *slope = (float)(y2 - y1)/(float)(x2 - x1); *yintercept = y1 - (*slope)*x1; return 0; 기울기와 y- 절편을인수로전달
2 개이상의결과를반환 int main(void) { } float s, y; if( get_line_parameter(3, 3, 6, 6, &s, &y) == -1 ) else printf(" 에러 \n"); printf(" 기울기는 %f, y 절편은 %f\n", s, y); return 0; 기울기는 1.000000, y 절편은 0.000000
배열이함수인수인경우 일반변수 vs 배열 // 매개변수 x 에기억장소가할당된다. // 매개변수 b 에기억장소가할당되지않는다. void sub(int x) {... } void sub(int array[]) {... } 값에의한호출 참조에의한호출
배열이함수인수인경우 배열의원본을함수로전달하는이유 : 크기가큰배열을복사하려면많은 CPU 시간소모 따라서배열의경우, 배열의주소를전달하여서원본을직접전달한다.
예제
예제
함수가포인터를반환하는경우 함수는포인터도반환할수있다. 함수가종료되더라도남아있는변수의주소를반환하여야한다. 지역변수의주소를반환하면, 함수가종료되면사라지기때문에오류 int *add(int x, int y) { int result; 지역변수 result 는함수가종료되면소멸되므로그주소를반환하면안된다.!! } result = x + y; return &result;
포인터사용의장점 연결리스트나이진트리등의향상된자료구조를만들수있다. 14 장에서간단하게학습 N A C D E B 참조에의한호출 포인터를매개변수로이용하여함수외부의변수의값을변경할수있다. 동적메모리할당 14 장에서학습 메인메모리
중간점검 1. 함수에매개변수로변수의복사본이전달되는것을 라고한다. 2. 함수에매개변수로변수의원본이전달되는것을 라고한다. 3. 배열을함수의매개변수로지정하는경우, 배열의복사가일어나는가?
함수포인터 함수포인터 (function pointer): 함수를가리키는포인터 반환형 (* 함수포인터이름 ) ( 매개변수 1, 매개변수 2,...); ( 예 ) int (*pf)(int, int);
함수포인터
#include <stdio.h> // 함수원형정의 int add(int, int); int sub(int, int); int main(void) { int result; int (*pf)(int, int); fp1.c // 함수포인터정의 pf = add; result = pf(10, 20); printf("10+20은 %d\n", result); pf = sub; result = pf(10, 20); printf("10-20은 %d\n", result); // 함수포인터에함수 add() 의주소대입 // 함수포인터를통한함수 add() 호출 // 함수포인터에함수 sub() 의주소대입 // 함수포인터를통한함수 sub() 호출 } return 0;
int add(int x, int y) { return x+y; } fp1.c int sub(int x, int y) { return x-y; } 10+20 은 30 10-20 은 -10
중간점검 1. double 형매개변수를가지며 double 형의값을반환하는함수포인터 pf 를선언하여보자.
Q & A