쉽게풀어쓴 C 언어 Express 제 14 장포인터활용
이중포인터 이중포인터 (double pointer) : 포인터를가리키는포인터 int i = 10; int *p = &i; int **q = &p; // i 는 int 형변수 // p 는 i 를가리키는포인터 // q 는포인터 p 를가리키는이중포인터
이중포인터 이중포인터의해석
이중포인터 // 이중포인터프로그램 #include <stdio.h> *q int main(void) int i = 100; int *p = &i; int **q = &p; **q 100 200 300 p 포인터 *p = 200; printf("i=%d *p=%d **q=%d \n", i, *p, **q); i 변수 i q 이중포인터 **q = 300; printf("i=%d *p=%d **q=%d \n", i, *p, **q); return 0; **q == *(*q) i=200 *p=200 **q=200 i=300 *p=300 result **q=300
예제 #2 #include <stdio.h> void set_pointer(char **q); char *proverb="all that glisters is not gold."; "All that glisters is not gold." int main(void) char *p="zzz"; set_pointer(&p); printf("%s \n", p); return 0; p *q q void set_pointer(char **q) *q = proverb; All that glisters is not gold. result
중간점검 double형포인터를가리키는이중포인터 dp를선언하여보자. char c; char *p; char **dp; p = &c; dp =&p; 와같이정의되었을때 **dp은무엇을가리키는가?
포인터배열 포인터배열 (array of pointers): 포인터를모아서배열로만든것
정수형포인터배열 int a = 10, b = 20, c = 30, d = 40, e = 50; int *pa[5] = &a, &b, &c, &d, &e ;
2 차원배열에문자열을저장 char fruits[4 ][10] = "apple", "blueberry", "orange", melon" ; a p p l e \0 b l u e b e r r y \0 2 차원배열을사용하면낭비되는공간이생성되죠. o r a n g e \0 낭비되는공간! m e l o n \0
문자형포인터배열 char *fruits[ ] = "apple", "blueberry", "orange", melon" ; a p p l e \0 fruits[0] fruits[1] fruits[2] fruits[3] b l u e b e r r y \0 o r a n g e \0 m e l o n \0
예제 // 문자열배열 #include <stdio.h> 각각의문자열의길이가달라도메모리의낭비가발생하지않는다. int main(void) int i, n; char *fruits[ ] = "apple", "blueberry", "orange", "melon" ; fruits[0] fruits[1] fruits[2] fruits[3] a p p l e \0 b l u e b e o r a n g m e l o n \0 r e \0 r y \0 // 배열원소개수계산 n = sizeof(fruits)/sizeof(fruits[0]); for(i = 0; i < n; i++) printf("%s \n", fruits[i]); apple blueberry result orange melon return 0;
중간점검 double형의포인터 10개를가지는배열을정의하여보자. 래그드배열이일반적인 2차원배열보다좋은점은무엇인가?
배열포인터 배열포인터 (a pointer to an array) 는배열을가리키는포인터
배열포인터 ( 의미 )
예제 #include <stdio.h> int main(void) int a[5] = 1, 2, 3, 4, 5 ; int (*pa)[5]; int i; pa = &a; for(i=0 ; i<5 ; i++) printf("%d \n", (*pa)[i]); return 0; result 1 2 3 4 5
함수포인터 함수포인터 (function pointer): 함수를가리키는포인터 int (*pf)(int, int);
함수포인터의해석
함수포인터의사용 int sub(int, int); int (*pf)(int, int);... pf = sub; result = pf( 10, 20); // 함수원형정의 // 함수포인터정의 // 함수의이름을함수포인터에대입 // 함수포인터를통하여함수호출 int int add(int add(int x, x, int int y) y)...... 함수 pf int int sub(int x, x, int int y) y)...... 함수
예제 // 함수포인터 #include <stdio.h> // 함수원형정의 int add(int, int); int sub(int, int); int main(void) int result; int (*pf)(int, int); // 함수포인터정의 pf = add; result = pf(10, 20); printf("10+20 은 %d\n", result); pf = sub; result = pf(10, 20); printf("10-20 은 %d\n", result); 함수의이름은배열의이름과마찬가지로함수의시작주소를나타내는포인터상수로간주된다. pf int add(int x, int y) 함수 int sub(int x, int y) 함수 return 0;
예제 int add(int x, int y) return x+y; int sub(int x, int y) return x-y; 10+20은 30 10-20은 -10result
함수포인터 ( 의미 )
함수포인터의배열 int (*pf[5]) (int, int);
함수포인터의배열 int (*pf[5]) (int, int);
예제 : 함수포인터배열 // 함수포인터배열 #include <stdio.h> // 함수원형정의 void menu(void); int add(int x, int y); int sub(int x, int y); int mul(int x, int y); int div(int x, int y); void menu(void) printf("=====================\n"); printf("0. 덧셈 \n"); printf("1. 뺄셈 \n"); printf("2. 곱셈 \n"); printf("3. 나눗셈 \n"); printf("4. 종료 \n"); printf("=====================\n");
fp2.c int main(void) int choice, result, x, y; // 함수포인터배열을선언하고초기화한다. int (*pf[4])(int, int) = add, sub, mul, div ; 함수포인터배열선언 while(1) menu(); printf(" 메뉴를선택하시오 :"); scanf("%d", &choice); int add(int x, int y) return x + y; int sub(int x, int y ) return x - y; int mul(int x, int y) return x * y; int div(int x, int y) return x / y; if( choice < 0 choice >=4 ) break; printf("2 개의정수를입력하시오 :"); scanf("%d %d", &x, &y); // 함수포인터를이용한함수호출 result = pf[choice](x, y); printf(" 연산결과 = %d\n",result); return 0; Pf[0] Pf[1] Pf[2] Pf[3]
fp2.c int add(int x, int y) return x + y; int sub(int x, int y) return x - y; int mul(int x, int y) return x * y; int div(int x, int y) return x / y; ===================== 0. 덧셈 1. 뺄셈 2. 곱셈 3. 나눗셈 4. 종료 result ===================== 메뉴를선택하시오 :2 2 개의정수를입력하시오 :10 20 연산결과 = 200 ===================== 0. 덧셈 1. 뺄셈 2. 곱셈 3. 나눗셈 4. 종료 ===================== 메뉴를선택하시오 :
함수인수로서의함수포인터 함수포인터도인수로전달이가능하다.
예제 다음과같은수식을계산하는프로그램을작성하여보자. 여기서 f(k) 는다음과같은함수들이될수있다.
예제 #include <stdio.h> #include <math.h> double f1(double k); double f2(double k); double formula(double (*pf)(double), int n); int main(void) printf("%f\n", formula(f1, 10)); printf("%f\n", formula(f2, 10)); double formula(double (*pf)(double), int n) int i; double sum = 0.0; for(i = 1; i < n; i++) sum += pf(i) * pf(i) + pf(i) + 1; return sum;
예제 double f1(double k) return 1.0 / k; double f2(double k) return cos(k); 13.368736 12.716152 result
중간점검 int 값을반환하고 double 값을인수로받는함수의포인터 pf를선언하여보자. 1번의함수포인터를통하여 3.0을인수로하여함수를호출하는문장을작성하라.
다차원배열과포인터 2차원배열 int m[3][3] 1행->2행->3행->... 순으로메모리에저장 ( 행우선방법 ) 하나의열 m m[0][0] m[0][0] m[0][1] m[0][1] 1 행 m[0] 하나의행 m[0][2] m[0][2] m[1][0] m[1][0] m[1] m[0][0] m[0][0] m[0][1] m[0][1] m[0][2] m[0][2] m[1][1] m[1][1] 2 행 열 (column) m[1][0] m[1][0] 행m[2][0] m[2][0] (row) m[1][1] m[1][1] m[2][1] m[2][1] m[1][2] m[1][2] m[2][2] m[2][2] m[1][2] m[1][2] m[2][0] m[2][0] m[2] m[2][1] m[2][1] 3 행 m[2][2] m[2][2]
2 차원배열과포인터 배열이름 m은 &m[0][0] m[0] 는 1행의시작주소 m[1] 은 2행의시작주소... m m[0] m[0] m[0][0] m[0][0] m[0][1] m[0][1] m[0][2] m[0][2] m[1] m[1] m[1][0] m[1][0] m[1][1] m[1][1] m[1][2] m[1][2] m[2] m[2] m[2][0] m[2][0] m[2][1] m[2][1] m[2][2] m[2][2]
2 차원배열의해석 m 은세개의원소를가지는배열이다. int m[3][3]; 해석의방향 그원소들은다시세개의원소로되어있다.
multi_array.c // 다차원배열과포인터 #include <stdio.h> int main(void) int m[3][3] = 10, 20, 30, 40, 50, 60, 70, 80, 9 0 ; printf("m = %p\n", m); printf("m[0] = %p\n", m[0]); printf("m[1] = %p\n", m[1]); printf("m[2] = %p\n", m[2]); printf("&m[0][0] = %p\n", &m[0][0]); printf("&m[1][0] = %p\n", &m[1][0]); printf("&m[2][0] = %p\n", &m[2][0]); return 0; m m[0] m[1] m[2] m = 1245020 m[0] = 1245020 m[1] = 1245032 m[2] = 1245044 &m[0][0] = 1245020 &m[1][0] = 1245032 &m[2][0] = 1245044 10 20 30 m[0][0] m[0][1] m[0][2] 40 50 60 m[1][0] m[1][1] m[1][2] 70 80 90 m[2][0] m[2][1] m[2][2] result
2 차원배열과포인터연산 2 차원배열 m[][] 에서 m 에 1 을더하거나빼면어떤의미일까?
포인터를이용한배열원소방문 행의평균을구하는경우 double get_row_avg(int m[][cols], int r) int *p, *endp; double sum = 0.0; m[0][0] m[0][0] m[0][1] m[0][1 ] m[0][2] m[0][2] p = &m[r][0]; endp = &m[r][cols]; while( p < endp ) sum += *p++; 열 (column) m[1][0] m[1 ][0] 행m[2][0] m[2][0] (row) m[3][0] m[3][0] m[1][1] m[1 ][1 ] m[2][1] m[2][1 ] m[3][1] m[3][1 ] m[1][2] m[1][2] m[2][2] m[2][2] m[3][2] m[3][2] sum /= COLS; return sum; p endp endp
포인터를이용한배열원소방문 전체원소의평균을구하는경우 double get_total_avg(int m[][cols]) int *p, *endp; double sum = 0.0; m[0][0] m[0][0] m[0][1 m[0][1 ] ] m[0][2] m[0][2] p = &m[0][0]; endp = &m[rows-1][cols]; m[1][0] m[1][0] m[1][1 m[1 ][1 ] ] m[1][2] m[1][2] while( p < endp ) sum += *p++; sum /= ROWS * COLS; return sum; p m[2][0] m[2][0] m[3][0] m[3][0] m[2][1 m[2][1 ] ] m[3][1] m[3][1] 열 (column) 행(row) m[2][2] m[2][2] m[3][2] m[3][2] endp endp
중간점검 m[10][10] 에서 m[0] 의의미는무엇인가? m[10][10] 에서 (m+1) 의의미는무엇인가?
const 포인터 const 를붙이는위치에따라서의미가달라진다. p 가가리키는내용이변경되지않음을나타낸다. 포인터 p 가변경되지않음을나타낸다. const char *p; char * const p;
예제 #include <stdio.h> int main(void) char s[] = "Barking dogs seldom bite."; char t[] = "A bad workman blames his tools"; const char * p=s; char * const q=s; //p[3] = 'a'; p 가가리키는곳의내용을변경할수없다. p = t; q[3] = 'a ; 하지만 p 는변경이가능하다. q 가가리키는곳의내용은변경할수있다. //q = t; 하지만 q 는변경이불가능하다. return 0;
volatile 포인터 volatile 은다른프로세스나스레드가값을항상변경할수있으니값을사용할때마다다시메모리에서읽으라는것을의미 p 가가리키는내용이수시로변경되니사용할때마다다시로드하라는의미이다. volatile char *p;
void 포인터 순수하게메모리의주소만가지고있는포인터 가리키는대상물은아직정해지지않음 ( 예 ) void *vp; 다음과같은연산은모두오류이다. *vp; // 오류 *(int *)vp; // void형포인터를 int형포인터로변환한다. vp++; // 오류 vp--; // 오류
vp.c #include <stdio.h> int main(void) int a[] = 10, 20, 30, 40, 50 ; void *vp; vp = a; vp = &a[2]; //*vp = 35; //vp++; // 가능 // 가능 // 오류 // 오류 *(int *)vp = 35; // 가능 return 0;
중간점검 void 형포인터 vp 를 int 형포인터 ip 로형변환하는문장을작성하라.
main() 함수의인수 지금까지의 main() 함수형태 int main(void).. 외부로부터입력을받는 main() 함수형태 int main(int argc, char *argv[])..
인수전달방법 C: \cprogram> mycopy src dst m y c c o o p y \0 \0 argv[0] s s r r c c \0 \0 argv[1] d s s t t \0 \0 3 3 argv[2] argc argv 배열
main_arg.c #include <stdio.h> int main(int argc, char *argv[]) int i= 0; for(i = 0;i < argc; i++) printf(" 명령어라인에서 %d 번째문자열 = %s\n", i, argv[i]); return 0; c:\cprogram\mainarg\debug>mainarg src dst 명령어라인에서 0 번째문자열 = mainarg 명령어라인에서 1 번째문자열 = src 명령어라인에서 2 번째문자열 = dst c:\cprogram\mainarg\debug>
비주얼 C++ 프로그램인수입력방법 [ 프로젝트 ]->[main_arg.exe 속성 ] 선택
mile2km.c #include <stdio.h> #include <stdlib.h> int main(int argc, char *argv[]) double mile, km; if( argc!= 2 ) printf(" 사용방법 : mile2km 거리 \n"); return 1; mile = atof(argv[1]); km = 1.609 * mile; printf(" 입력된거리는 %f km 입니다. \n", km); return 0; c:\cprogram\mainarg\debug>mainarg 10 입력된거리는 16.090000 km 입니다. c:\cprogram\mainarg\debug>
중간점검 C>main arg1 arg2 arg3와같이실행시킬때 argv[0] 가가리키는것은? C>main arg1 arg2 arg3와같이실행시킬때 argc의값은?
실습 : 이분법으로근구하기 2 차식의경우에는공식이있지만일반적인 n 차식의경우공식이존재하지않는다. 이때사용할수있는방법이이분법 (bisection) 이다
이분법 구간 [a, b] 에서근을가지는것이확실하면구간 [a, b] 의중점 m 을구하여구간 [a, m] 과구간 [m, b] 로나눈다. 각각의구간에서다시 f(a) 와 f(b) 의부호를조사하여근이어떤구간에위치하는지를결정한다. 다시그구간에대하여동일한과정을되풀이한다.
실행결과 다음과같은함수에대하여근을구한다. 함수포인터를받는 get_root() 를작성한다. double get_root(double (*fp)(double), double a, double b); a 의값을입력하시오 -200 b 의값을입력하시오 200 값은 -2.104950
예제소스 #include<stdio.h> #include <math.h> #define ESP 0.001 double get_root(double (*f)(double), double a, double b); double func(double x) return (x)*(x)*(x) + (x)*(x) + (x) + 7; void main() double x0,x1; double r; printf("a의값을입력하시오 "); scanf("%lf",&x0); printf("b의값을입력하시오 "); scanf("%lf",&x1); r = get_root(func, x0, x1); printf(" 값은 %f\n", r); return 0;
예제소스 double get_root(double (*f)(double), double x0, double x1) float x2; int i= 1; double f1,f2,f0; do x2=(x0+x1)/2; f0=f(x0); 부호가다르면 f1=f(x1); f2=f(x2); if(f0*f2<0) x1=x2; else x0=x2; i++; while(fabs(f2)>esp); f2 의절대값이무시할만큼작아지면, 즉 0 에가까워지면 return x2;
도전문제 사용자로부터함수를입력받도록프로그램을수정할수있는가? 다항식의최고차수는 3 으로제한하도록하자.
Q & A