1) 지식 창고는 본인이 작성한 콘텐츠(팁/노하우/리소스/강좌 등)을 무료 혹은 가상화폐인 납포인트를 통해 공유하는 공간입니다.
2) 본인이 작성한 콘텐츠에 대해서만 지식 창고에 등록할 수 있으며, 저작권에 위배되는 콘텐츠는 사전경고 없이 삭제될 수 있습니다.
3) 콘텐츠 구매 및 첨부파일 다운로드는 회원그룹 '연구원' 이상 가능하오니, 경험치를 쌓아 진급한 후에 이용 부탁드립니다.
4) 무료 콘텐츠의 본문은 구매절차 없이 즉시 이용할 수 있으며, 판매 납포인트가 있는 콘텐츠는 구매 후 이용할 수 있습니다.
5) 콘텐츠 판매에 따른 납포인트 수익은 지정한 비율(50%)에 따라 판매자에게 지급하며, 납포인트 수익을 통해 진급을 빨리할 수 있습니다.
6) 구매 후 평가를 하면 구매 납포인트의 20%를 돌려 드립니다.
판매자 | 프리미엄 | 판매 납포인트 | 무료 | 평점 | 0점 / 총 0명 참여 |
---|
***************************< 캠퍼스 C 강좌 >******************************
[제목] :
[코드] : campusc1-017 (초급)
[교재] : CAMPUS C (초급, Third edition) [출판사 : 책과스승]
[알림] :이 파일은 "캠퍼스 C"에서 모든 분께 공개한 "초급 강좌"입니다.
이 [알림]의 내용을 지우지 않는다면 누구에게나 임의로 복사해 줄 수
있습니다.그러나 이 강좌 내용에 대한 저작권은 "캠퍼스 C"에 있습니다.
[연락처] : 605-8662 (서울) ("캠퍼스 C", 도서출판 "책과 스승")
천리안 : go campusc
나우콤 : go lcampc
하이텔 ID : campusc
****************************<< 목 차 >>************************************
<1> 함수 포인터 --> char (*ma_dangbal)()
<2> 함수 포인터들 --> char (*ma_dangbal[3])()
<3> 포인터 표현과 함수머리(prototype) 선언을 구별하자.
<4> 함수의 사용형태 총정리 (3 군데가 같아야 한다)
<5> 포인터에 대한 설명을 마치며
**************************< 내용 시작 >********************************
<1> 함수 포인터 --> char (*ma_dangbal)()
***********************************************************************
1> 함수 포인터의 선언법
-----------------------------------------------------------------------
함수 포인터는 다음과 같이 선언을 합니다.
char (*dangbal)(); <-- 1) 함수 포인터
char *p; <-- 2) 우리가 잘 아는 1차원 포인터
main()
{
}
우리 당발 씨가 많이 출세를 했는데, 그 동안 별 볼일 없는 1차원
포인터부터 시작하더니 급기야 "함수 포인터" 까지 변신을 했군요.
함수 포인터는 위와 같은 형식으로 표현을 합니다.물론 표기법이니
까 외워야 하지만, 이것도 해석하는 법칙을 따져 보면 외울 필요도 없습니
다.
1) 안쪽부터 따져서 "당발은 좌우간 포인터다" ( *dangbal )
( main() 밖에서 변수로 선언 할 때 "*"의 의미 아시지요 ? )
2) 포인터면 가리키는 타입이 있을 것 아니야, 뭘 가리키는데 ?
여기서 2) 번의 대답을 정리 해보지요. 여러분들께서 그 동안 정신없이 포
인터에 대해서 공부해 왔지만, 사실 많은 연습을 통해서 익숙하지 않으면
여전히 혼동할 염려가 있을 것입니다.
아래는 "당발이들"이 가리키는 타입을 전부 모아 본 것입니다. 정리를 하는
차원에서 보고, 또 유독 "함수 포인터"의 모양이 "이상하게" 보이는걸 기억
하셔야 합니다.
----------------------< 1차원 >--------------------------
char *dangbal; <-- char 변수를 가리킨다
int *dangbal; <-- int 변수를 가리킨다
long *dangbal; <-- long 변수를 가리킨다
----------------------< 2차원 >--------------------------
char **dangbal; <-- char *변수를 가리킨다
int **dangbal; <-- int *변수를 가리킨다
long **dangbal; <-- long *변수를 가리킨다
char *dangbal[]; <-- char 변수를 가리키는 포인터들
int *dangbal[]; <-- int 변수를 가리키는 포인터들
long *dangbal[]; <-- long 변수를 가리키는 포인터들
----------------------< 3차원 >--------------------------
char ***dangbal; <-- char **변수를 가리킨다
int ***dangbal; <-- int **변수를 가리킨다
long ***dangbal; <-- long **변수를 가리킨다
----------------------< 함수형 포인터 >--------------------------
char (*dangbal)(); <-- char 형태로 보고(return)하는 함수를 가리킴
int (*dangbal)(); <-- int 형태로 보고하는 함수를 가리킨다
long (*dangbal)(); <-- long 형태로 보고하는 함수를 가리킨다
---------------
//
char (*dangbal)(char); <-- char형의 화살을 쏘고 char로 보고하는 //
int (*dangbal)(char); <-- char형의 화살을 쏘고 int 로 보고하는 //
long (*dangbal)(char); <-- char형의 화살을 쏘고 long로 보고하는 //
char (*dangbal)(int); <-- int형의 화살을 쏘고 char로 보고하는 //
int (*dangbal)(int); <-- int형의 화살을 쏘고 int로 보고하는 //
long (*dangbal)(int); <-- int형의 화살을 쏘고 long로 보고하는 //
char (*dangbal)(char, int); <-- char,int 형 화살 2개 쏘고 보고하는
int (*dangbal)(char, int);<-- char,int 형 화살 2개 쏘고 보고하는
long (*dangbal)(char, int); <-- char,int 형 화살 2개 쏘고 보고하는
위에서 1,2,3 차원의 포인터에 대해서는 더이상 설명을 안해도 잘
아시리라고 생각하고, 함수 포인터도 위와 같이 종합적으로 적어 놓으니까
내용이 짐작이 가지요 ? 즉 당발이가 함수를 가리키는 것은 좋은데, 함수의
형태가 위와 같이 여러 가지가 있지 않습니까 ? 그 중에서 정확히 어떤 함
수를 가리킬 것인가를 지정해 주어야 합니다.
예를 들면 [53쪽] "2.5 함수 부분"을 보세요. 이
것도 몇 가지만 예를 든 것이지만, main() 함수 (총사령관)이 다양하게
sub routine(병사들을) 부를 수 있지요. 여기서 병사 함수 xxx0() 과
xxx1()은 다른 형태를 가졌기 때문에 우리가 함수 포인터를 사용할 때도 다
른 "함수 포인터"를 사용해야 하지요. 구체적으로 예를 들면,보통 함수를 부
를 때 다음과 같이합니다
----------------------------
main()
{
rambo();
}
rambo()
{
printf("Good morning");
}
-------------------------------
이걸 함수 포인터로 사용하면 다음과 같이됩니다.
-------------------------------
int rambo(); <-- <요기 1> /* 함수머리(prototype) 선언 */
int (*dangbal)(); <-- (요기 2> /* 함수 포인터 "변수" 선언 */
main()
{
dangbal = rambo; <-- <요기 3>
dangbal(); <-- (요기 4>
}
rambo()
{
puts("good");
}
-----------------------------
어떤가요 ? 형태가 더 복잡해 졌지요 ? 공연히 쉽게 쓸 수 있는걸 어렵게 쓸
필요가 있을까요 ?
그러나 이 함수 포인터를 쓰면 엄청나게 프로그램이 간단해지고
편할 수 있다는 걸 아시고 일단은 잠시만 기다리세요.
위 내용을 자세히 보면,
<요기 1> :
우리가 잘아는대로 함수 머리를 main()위에다 선언하는 것입니다.즉 총사령
관에게 미리 보고를 하는 것이지요. 즉, 아래에 rambo()란 훌륭한 병사가
있어요...라고 말입니다. 함수 포인터를 사용할 때는 반드시 이렇게
prototype을 써 줘야 합니다 (중요)
<요기 2> :
보통, 변수 선언 하는 것과 마찬가지 입니다. 즉 함수 포인터 변수
"dangbal"을 선언하는 것이지요. 이 것은 그냥 다른 포인터와 똑같은 놈입니
다. 다만 가리키는 것이 "함수"이다 보니 지금처럼 특별 대우를 받는 것이지
요.
<요기 3>
dangbal = &xxx[] 처럼 남의 주소를 대입 하는 것인데, 여기서 당연히 "함
수의 주소"를 넣어야 겠지요. 여기서 주의 할 것은 함수 이름에 쌍따옴표를
안 쓰는 것입니다. 즉
dangbal = rambo; <-- 맞음(o)
dangbal = "rambo"; <-- 틀림(x)
이걸 보면 눈치가 빠른 사람은 벌써 다음과 같은 사실을 알 겁니다. "아하 !
함수 머리 xxx()도 결국 어디다가 베이스 캠프를 차려 놓고 배열에서 집 찾
을 때처럼 번지(address)로 찾아가는 것이구나"
<요기 4>
이거는 형태로 보아 보통 함수를 부르는 것과 똑같은 형태 아닙니까 ? 옛날
부터 포인터가 이런 짓을 잘했는데, 배열명 대신 포인터가 들어가도 똑같다
는 것 아시지요 ? 함수 포인터도 마찬가지입니다.여기서 주의할 점은 아래
의 표현 <1> <2> <3> 이 다 똑같다는 것입니다.
int (*dangbal)();
main()
{
dangbal = rambo; <-- 두개의 이름이 같은 곳에 손가락 질 한다
rambo(); <-- <1> 보통 함수 부르는 방식 !
dangbal(); <-- <2> rambo 대신 dangbal 이 들어간 형태.
왜냐하면 현재 dangbal = rambo 이니까
(*dangbal)(); <-- <3> 선언된 것과 똑같은 형태.
}
rambo()
{
}
위에서 <2> <3> 이 같이 쓰인다는 것을 알아야 합니다. 위 <2> 은
ANSI 규격입니다. ANSI 란 "America National Standards Institute"의 준말
로서 우리말로는 "미국 표준 협회"쯤 됩니다. 이 곳에서는 하는 일이 맨날
규격만 정하는 일이라, C 가 일정한 규격 없이 다양하게 번성을 하니까 1983
경에 규격을 정했습니다. 그래서 그 규격을 따르는 C 를 총칭해서 ANSI C
라고 부릅니다.
지금 우리가 사용하고 있는 터보 씨도 이 ANSI 규격을 따르고 있
습니다. 그래서 <2> 번의 표현이 허용되고, 규격이 세워지기 전에는 <3>번
표현이 너무나 널리 쓰이던 것이므로 역시 사용이 가능하도록 하고 있습니
다.
둘 중에 아무거나 써도 상관없으므로 자기가 연상하기 쉬운 것을 골
라서 사용하면 됩니다. 참고로 <2>는 보통 함수를 부르는 형식과 똑 같다
는 점 이고 <3>은 "선언"된 것과 똑같다는 겁니다.
***********************************************************************
<2> 함수 포인터들 --> char (*ma_dangbal[3])()
***********************************************************************
함수 포인터를 이해하면 "함수 포인터들"은 더 이상 설명할 것이 없을
겁니다. 옛날에 "마당발들"의 표현을 기억하실 겁니다. 잠시 비교해 볼까요
----------------<보통 포인터 >----------------
<마당발> <마당발들>
*ma_dangbal -----> *ma_dangbal[3]
----------------------------------------------
----------------<함수 포인터 >---------------------
<마당발> <마당발들>
(*ma_dangbal)() -----> (*ma_dangbal[3])()
--------------------------------------------------
유심히 보면 변하는 형태가 똑같다는 것을 알 겁니다. 내용도 역시 마찬가
지입니다. 즉, 보통 char 형 1차원 포인터는 아래처럼 스트링을 하나만 취
급하지요.
char *ma_dangbal;
main()
{
ma_dangbal = "good";
}
그러나 "마당발들"이 되면 아래처럼 여러 개의 스트링을 취급한다고 했습니
다.
char *ma_dangbal[3];
main()
{
ma_dangbal[0] = "good";
ma_dangbal[1] = "morning";
ma_dangbal[2] = "everybody";
}
함수 포인터도 마찬가지이지요
-------------------------------
int rambo();
int (*dangbal)();
main()
{
dangbal = rambo;
dangbal();
}
rambo()
{
puts("good");
}
이것이 "함수 포인터들"로 바뀌면
-------------------------------
int sub1();
int sub2();
int sub3();
int (*dangbal[3])() = { sub1, sub2, sub3 };
main()
{
dangbal[0]();
dangbal[1]();
dangbal[2]();
}
sub1()
{
puts("good ");
}
sub2()
{
puts("morning ");
}
sub3()
{
puts("everybody ");
}
"함수 포인터들"의 사용법을 이해하실 겁니다. 그러나 위와 같은 방법으로
쓰려면 굳이 "함수 포인터들"의 형태로 쓸 이유가 없지요. 배열로 쓰기 시작
하면 그 유명한 변수 i 를 사용하여 loop를 돌릴 수 있다는데 가장 큰 장점
이 있는 겁니다. 즉 다음과 같이 쓰지요
int sub1();
int sub2();
int sub3();
int (*dangbal[3])() = { sub1, sub2, sub3 };
main()
{
int i;
for(i=0; i < 3; i++) { <-- for 문으로 돌려버림
dangbal[i]();
}
}
sub1()
{
puts("good ");
}
sub2()
{
puts("morning ");
}
sub3()
{
puts("everybody");
}
표현이 한결 간결해 졌다는 것을 아시겠지요 ? 잘 기억 하셨다가 써먹어야
합니다. 중급 자로 되기 위한 표현입니다.
***********************************************************************
<3> 포인터 표현과 함수 머리(prototype) 선언을 구별하자.
***********************************************************************
함수의 포인터가 등장하기 시작하면 표현법이 조금 복잡해지기 시작합
니다. 따라서 소스를 읽을 때 특히 "함수의 머리(prototype)선언" 부분과
"변수"의 선언 부분을 혼동할 경우가 많은데 여기서 정리를 잠깐 해 보기로
하지요.
char (*p1)();
int (*p2)(int);
long (*p3)(char, int);
char *pp1();
int *pp2(int);
long *pp3(char, int);
main()
{
}
여러분들 혼동되시라고 일부러 그 유명한 당발씨를 빼 버렸습니다. 위에서
형태만 보고 "함수 포인터"와 "prototype의 선언" 한 것을 구별하시겠습니까
? 여기서 구별할 수 있는 유일한 근거는 괄호 (*xx) 입니다.
--------------------------------
(*xxx) 형태가 "함수 포인터 변수"
--------------------------------
입니다. 실제로 프로그램이 커지면 다음과 같은 다양한 표현을 쓸 경우가
생길 겁니다.
char sub1();
int sub2();
long sub3();
char *sub11();
int *sub12();
long *sub13(); /* 까지 prototype 선언 */
char p1;
int p2;
long p3;
char *p11;
int *p12;
long *p13;
char (*p21)();
int (*p22)();
long (*p23)();
char *(*p31)();
int *(*p32)();
long *(*p33)(); /* 까지 변수 선언 */
main()
{
}
<예제> 위 프로그램같이 선언 되었을 때 main() 에서 다음과 같이 썼다.
아래 <1> <2> <3> 중 선언된 형태대로 제대로 쓰고 있는 것은 어느 것인가 ?
main()
{
p21 = sub1; <--- <1> (o)
p21 = sub2; <--- <2> (x)
p21 = sub3; <--- <3> (x)
(*p21)();
}
char sub1()
{
}
int sub2()
{
}
long sub3()
{
}
<예제> 위에서 <2> <3> 은 왜 틀리는가 ?
<답> 선언된 형태를 보면 아래와 같다. 즉 "함수 포인터" p21 은 "char
xxx()"를 가리키기로 했으므로 <1> 이 맞다.
char sub1(); <1>
int sub2(); <2>
long sub3(); <3>
char (*p21)();
<문제> 이건 국민학생들 짝짓기 문제와 똑같습니다. 아래 함수 포인터가 받
을 수 있는 함수 명을 적어 주면 ?.
char sub1();
int sub2();
long sub3();
char *sub11();
int *sub12();
long *sub13();
char (*p21)();
int (*p22)();
long (*p23)();
char *(*p31)();
int *(*p32)();
long *(*p33)();
main()
{
p21 = sub1 /* 맞았습니다 */
p22 = ???
p23 = ???
p31 = ???
p32 = ???
p33 = ???
}
<문제> ctrlbrk(int (*handler)(void)) 라고 선언되 있는 함수가 있다고 한
다면 사용하는 법이 맞는 것은 어느 것인가 ? (힌트) int 형을 찾는다.
main()
{
ctrlbrk(sub1) <1>
ctrlbrk(sub2) <2>
ctrlbrk(sub3) <3>
}
<문제> fff( char (*xxx)(void)) 라는 함수가 있다면 사용법이 맞는 것은 ?
(힌트) char 형을 반환하는 것을 찾아야 됨.
main()
{
fff(sub1) <1>
fff(sub2) <2>
fff(sub3) <3>
}
<문제> 사용법이 다음과 같은 함수가 있다.
int fff( void (*func)(void))
이 함수를 사용하려면 prototype을 어떻게 선언해야 하는가 ?
void sub(); <1>
char sub(); <2>
int sub(); <3>
long sub(); <4>
main()
{
fff(sub);
}
void sub(void)
{
}
혹시 정답을 모르시겠거든 직접 컴파일을 해보시고 컴파일러의 반응을 보세
요. 직접 확인할 수 있을 겁니다.
***********************************************************************
<4> 함수의 사용 형태 총정리( 3 군데가 같아야 한다)
***********************************************************************
함수를 정확히 사용하기 위해서는 아래 <1> <2> <3> 처럼 3군데서 정확
해야 한다고 했습니다.
(int) rambo(char) <--- <1> prototype 선언
main()
{
int listen;
char arrow;
listen = rambo(arrow); <--- <2> 호출 (람보야 ! )
}
int rambo(char xxx) <--- <3> 함수 짜기 (만들기)
{
printf("%c 쏘겠습니다", xxx);
printf(" 보고는 int 형입니다");
return(100);
}
좀더 정확히 말하면 두 가지 측면에서 형태가 같아야 합니다. 즉
------------------------------------------------
1. rambo() 괄호 안에는 화살 1개만, 그 화살은 char 타입이다
2. 리턴 값은 int다
------------------------------------------------
위 1 번의 경우는 아래에서 밑줄을 그어 놨습니다. 그리고
위 2 번의 경우는 아래에서 확대를 해 놨습니다.
(int) rambo(char) <--- <1> prototype 선언(int는 생략 가능)
listen = rambo(arrow); <--- <2> 호출 (람보야 ! )
int rambo(char xxx) <--- <3> 함수 짜기 (만들기)
여러분께서 함수를 직접 짤 때 도 이와 같이 정확히 해 주어야 하므로 잘 기
억해야 합니다.
실제 프로그램을 짜면 함수들을 많이 사용하기 때문에 조금 복잡
해 질 수 있는데, 그 간단한 경우를 [52-53쪽]에 실었습니다.
지금 보세요.
여기서 각각의 함수들이 위의 3가지 <1> <2> <3>을 정확히 맞추고
있는걸 확인해야 합니다
<예제> [52쪽]에서 <3> 함수 머리 선언 부분을 보면 xxx3()은 다음과
같은 형태로 되있다. char xxx3(char, char *) 사용법이 맞는 것은 ?
char aa;
char *aa2;
int bb;
int *bb2;
main()
{
xxx3(aa, aa2); <1> ( 0 ) (char , char *)
xxx3(aa, bb); <2> ( x ) (char , int)
xxx3(bb, aa); <3> ( x ) (int , char)
xxx3(bb, bb2); <4> ( x ) (int , int *)
}
<예제> [52쪽]의 함수머리 선언에 xxx7() 가 다음과 같이 되있다.
사용법이 맞는 것은 ?
char *xxx7(char, char *)
char aa;
char *aa2;
int bb;
int *bb2;
main()
{
aa = xxx7(aa, aa2); <1> ( x ) char =
aa2 = xxx7(aa, aa2); <2> ( 0 ) char * =
bb = xxx7(aa, aa2); <3> ( x ) int =
bb2 = xxx7(aa, aa2); <4> ( x ) int * =
}
<문제> [52쪽]의 함수 머리 선언에 xxx8() 이 있다. 맞는 표현은 ?
int *xxx8(char, char *)
char aa;
char *aa2;
int bb;
int *bb2;
main()
{
aa = xxx8(bb, bb2); <1>
aa2 = xxx8(bb, bb2); <2>
bb = xxx8(bb, bb2); <3>
bb2 = xxx8(bb, bb2); <4>
}
***********************************************************************
<5> 포인터에 대한 설명을 마치며
***********************************************************************
포인터에 대한 설명을 마치겠습니다. 포인터는 사실 이것보다 더 어려운
표현이 있을 수 있지만, 컴파일러는 자기에게 주어진 지극히 간단한 법칙
에 의해 단순히 반복 해석하는 것이기 때문에 우리는 기본 원리만 알면 충
분합니다. 이를테면, 3의 제곱의 제곱의 제곱의... 하는 계산을
인간이 끝까지 하려면 정신이 없고 어렵지요. 그러나 우리가 끝까지 할 필
요는 없지요. 3의 제곱 하나만 할 줄 알면 나머지는 굳이 안 해봐도 상관이
없는것 아니겠습니까 ?
따라서 포인터의 표현도 "독약 한 모금 먹고" 어렵게 만들려면
한도 끝도 없지만, 그렇게 만들어서 뭐하겠다는 말입니까 ? C 에 능숙한
사람 정도의 수준에서 "척봐서 알 수 없는 지경"의 복잡한 표현은 쓸 일이
없는 겁니다. 지금 우리는 C를 편하게 써 보자고 노력하고 있는 중인데, 그
런 표현을 굳이 만들 필요가 없지요. 배열도 4차원,5차원..n 차원 까지 허
용하지만 우리가 기껏해야 3 차원까지 쓰는 것과 똑같습니다.
그러나 오늘 다룬 함수의 포인터까지는 가볍게 다룰 정도가 되야
합니다. (최소한 이 강좌를 들은 사람들에게는)
앞으로 스트락춰와 유니온에서 복합적으로 포인터가 나올 수는 있
으나 포인터에 대한 설명은 이것이 마지막 입니다. 무언가 미진한 것 같은
부분이 있는 것 같아 저도 개운치는 않으나, 진도는 나가야 겠지요. 그리고
이 강좌에서 다룬 부분도 과연 여러분께서 다 이해하셨는지 잘 모르겠습니
다. 혹시 이해가 안되는 부분이 있으시면 진도와 관계없이 질문하세요.
포인터는 그만큼 C 에서는 중요한 비중을 차지하는 내용입니다.
<숙제>
그 동안 우리가 사용해 왔던 "menu.c" 파일에 이 함수 포인터를 쓰는 법이
있습니다 ([359쪽] }. 함수 포인터는 이 menu.c에서 처럼 쓰는 것이 가장
일반적인 방법입니다. 물론 응용하기에 따라 여러 가지로 쓸 수 있을 겁니다.
이 menu.c를 읽어보세요. 이제는 내용을 이해할 수 있어야 합니다.
**************************< 끝마치며 >********************************
수고하셨습니다.
***********************************************************************