U E D R , A S I H C RSS

C/C++어려운선언문해�하기

{{|
CodeProject�서 최근� 아주 �미로운 글� �었습니다. 글� 내용� 별로 길지� 않고 워낙 유용한 정보� ���서 날림으로 번역해봤습니다. �어와 한글� 어순� 반대�서 매�럽지 못한 부분� 많�� �런 경우 �문� 같� 볼 수 있�� 같� 올렸습니다.



�문 : How to interpret complex C/C++ declarations (http://www.codeproject.com/cpp/complex_declarations.asp)





간혹 int * (* (*fp1) (int) ) 10; 과 같� 선언문�나 혹� �와 유사하게 난해한 선언문� 볼 기회가 있습니까? � 글� �런 C/C++

선언문� �면했� 때 �를 어떻게 해�하는가를 알려주기 위한 글입니다. 매우 기본��고 �범한 예제�서 시작해서 복작한 경우까지

다루겠습니다. 우리가 매� �히 볼 수 있는 선언문� 비롯해서 간간히 문제� 소지를 �으킬 수 있는 const 수정�와 typedef � 함수

��터를 다룬 후 마지막으로 "오른쪽�서 왼쪽으로" 규칙� 알아 봅니다.



� 글� 단순히 우리가 �러한 선언문� 맞닥뜨리게 �었� 때 어떻게 � 선언문� �해할 수 있는가 방법� 알려주는 것입니다. 여기

예제�서 제시� 것과 같� 복잡한 선언문� 사용하여 �해하기 어려운 코드를 작성하는것� 결코 좋� 프로그래� 습관� 아니겠죠.



기초



int a;



위� 문장� '변수 n � int 형으로 선언한다'�고 해�할 수 있습니다.



��터로 넘어가서 다�과 같� 선언문� 보게�면,



int *p;



'변수 p를 int *형으로 선언한다' �고 해�할 수 있고 다시 �하면 '변수 p를 int형� 가리키는 ��터로 선언한다'�고 할 수 있습니

다. 여기서 잠� 샛길로 빠져서 ��터 연산�(*) �는 참조 연산�(&)는 변수 형 (int)� 붙�는 것 보다 변수(p)� 붙�는것� 항�

좋습니다. 왜�하면 다�과 같� 선언문� 사용하면서 실수할만한 소지를 없애주기 때문입니다. (역�주: 참고로 C++ 창시�� Bjarne

Stroustrup는 변수 형� 붙�는걸 � 선호한다고 했답니다.) (추가: 단순히 B. S.는 �걸 선호했다고 하��는 무책임한 언급만 남기고

�세한 설명� 첨가하지 못해서 죄송합니다. 다�� ��를 참조하시면 � �세한 �유를 보실 수 있으실 �니다. http://www.research.att.com/~bs/bs_faq2.html#whitespace )



int* p,q;



위� 선언문� 처� 보게 �면 변수 p와 변수 q가 마치 int를 가리키는 ��터형 (int *) 변수로 선언� 것 처럼 보입니다. 하지만 사실

변수 p만 int를 가리키는 ��터형으로 선언�었고 변수q는 int형으로 선언� 것입니다. (역�주: int *p; int *q; 처럼 �� 따로 선언

하는게 �미가 분명히 전달�고 �편하겠습니다.)



우리는 �한 ��터를 가리키는 ��터를 선언할 수 있습니다.



char **argv;



�리만 따지�면 � 관계는 무한히 반복� 수 있습니다. 따�서 우리는 float형� 가리키는 ��터를 가리키는 ��터를 가리키는 ��

터를 선언할 수� 있습니다. 몇 단계 � 반복해� �니다.



다�과 같� 선언문들� 봅시다. (역�주: 여기서 부터는 선언문 해� �문� 괄호안� 넣습니다. 선언문� 한글로 표현하기가 �렇게

어렵군요.)



int RollNum304;

int (*p)4=RollNum;

int *q5;



변수 p는 int형� 요소로 하는 �기가 4� 배열� 가리키는 ��터(a pointer to an array of 4 ints)�며, 변수 q는 int형 ��터를 요

소로 하는 �기가 5� 배열(an array of 5 pointer to integers) 입니다.





const modifier

const 수정�는 변수가 변경�는 것� 금지 (변수 <-> 변경할 수 없다? 모순�군요 ) 하기 위해서 사용하는 키워드입니다. const 변수

를 선언하는 경우 선언문�서 바로 초기화를 해�니다. 변경� 불가능하기 때문� 선언문 외�서는 값� 초기화할 수 없겠죠.



const int n = 5;

int const m = 10;



위� 예제� � 변수 n과 m� 똑같� const 정수형으로 선언�었습니다. C++ 표준�서 �가지 선언� 모� 가능하다고 나와있습니만 개

��으로는 const가 강조�어서 �미가 � 분명한 첫번째 선언문� 선호합니다.



const가 ��터와 결합�면 조금 복잡해집니다. 예를 들어서 다�과 같� 변수 p, q가 선언�었습니다.



const int *p;

int const *q;



그럼 여기서 퀴즈, const int형� 가리키는 ��터와 int형� 가리키는 const ��터를 구별해보세요.



사실 � 변수 모� const int를 가리키는 ��터입니다. int 형� 가리키는 const ��터는 다�과 같� 선언�니다.



int * const r = &n; // n � int형 변수로 선언 �었슴



위�서 p와 q는 const int형 변수�기 때문� *p 나 *q� 값� 변경할 수 없습니다. r� const ��터�기 때문� �단 위와 같� 선언

� 후� 다시 r = &m 과 같� 다른 주소값� 할당하는 것� 불가능합니다. (물론 m� � 다른 int형 변수�겠죠) 물론 � 경우 *r�

값� 변경� 가능합니다.



위�서 나온 � 가지 선언문� 결합하여 const int 형� 가리키는 const ��터를 선언하려고 하면 다�과 같습니다.



const int * const p = &n; // n� const int형 변수로 선언�었슴



다�� 나열한 선언문들� 보시면 const를 어떻게 해�할 수 있는지 � 분명하게 아실 수 있��니다. � 중� 몇몇 선언문� 선언하면

서 �시� 초기화해줘야만 컴파� 할 수 있는� 여기서는 쉽게 �해할 수 있�� �략하였습니다. (컴파� 안�다고 딴지 사절)





char ** p1; // pointer to pointer to char

const char **p2; // pointer to pointer to const char

char * const * p3; // pointer to const pointer to char

const char * const * p4; // pointer to const pointer to const char

char ** const p5; // const pointer to pointer to char

const char ** const p6; // const pointer to pointer to const char

char * const * const p7; // const pointer to const pointer to char

const char * const * const p8; // const pointer to const pointer to const char



typedef� 세밀한 구분

typedef는 typedef는 "* �는 &가 형� 아닌 변수� �용�다"�는 규칙� 극복하게 해�니다. 다�과 같� typedef 를 사용하게 �면



typedef char * PCHAR;

PCHAR p,q;



변수 p, q 모� char를 가리키는 ��터 변수가 �니다. typedef가 사용�지 않았다면 q는 char를 가리키는 ��터 변수가 아니� char 형 변수였� �� 솔�히 �런 사실� 모르는 경우 실수하기 쉽��죠.



아래� typedef를 사용한 선언문들� 설명과 함께 나열해봅니다.



typedef char * a; // a is a pointer to a char

//

// a는

// char형� 가리키는 ��터



typedef a b(); // b is a function that returns

// a pointer to a char

//

// b는

// char형� 가리키는 ��터를 리턴하는

// 함수



typedef b *c; // c is a pointer to a function

// that returns a pointer to a char

//

// c는

// char형� 가리키는 ��터� 리턴하는

// 함수를 가리키는

// ��터



typedef c d(); // d is a function returning

// a pointer to a function

// that returns a pointer to a char

//

// d는

// char형� 가리키는 ��터를 리턴하는

// 함수를 가리키는

// ��터를 리턴하는

// 함수



typedef d *e; // e is a pointer to a function

// returning a pointer to a

// function that returns a

// pointer to a char

//

// e는

// char형� 가리키는 ��터를 리턴하는

// 함수를 가리키는

// ��터를 리턴하는

// 함수를 가리키는

// ��터



e var10; // var is an array of 10 pointers to

// functions returning pointers to

// functions returning pointers to chars.

//

// var는

// char형� 가리키는 ��터를 리턴하는

// 함수를 가리키는

// ��터를 리턴하는

// 함수를 가리키는

// ��터를 요소로하는 �기 10� 배열



typedef는 보통 아래� 나열한것과 같� struct는 선언과 같� 사용�니다. 다�과 같� struct 선언문� 사용하면 C++ �만 아니� C�

서� struct를 �략할 수 있습니다.



typedef struct tagPOINT

{

int x;

int y;

}POINT;



POINT p; /* C �서� 유효 */



함수 ��터

아마� 선언문� 해�하는� 있어서 가장 혼�� 초래하는 것� 함수 ��터� 것입니다. 함수 ��터는 DOS 시절� 백그�운드 �주 프

로그램(TSR)� 작성하는� 사용�기� 했고 Win32나 X-Windows서는 callback 함수를 작성하는� 주로 사용�니다. � 외�� 많� 곳�

서 함수 ��터가 사용�니다. 예를 들�면 가� 함수 테�블, STL� �부 템플릿 그리고 Win NT/2K/XP� 시스템 서비스�서 사용�는

것 � 볼 수 있습니다. 그럼 간단한 예제부터 시작해보겠습니다.



int (*p)(char);



위� 선언문� 변수 p를 char를 입력 ��로 하고 int를 리턴하는 함수를 가리키는 ��터(a pointer to a function that takes a char

argument and return an int)로 선언합니다.



�개� float를 입력��로 하고 char를 가리키는 ��터를 가리키는 ��터를 리턴하는 함수를 가리키는 ��터(a pointer to a

function that take two floats and returns a pointer to a pointer to a char)는 다�과 같� 선언합니다.



char ** (*p)(float, float);



그렇다면 char를 가리키는 const ��터 �개를 입력 ��로 받고 void ��터를 리턴하는 함수를 가리키는 ��터를 요소로하는 �기 5� 배열

(an array of 5 pointers to functions that receive two const pointers to chars and return void pointer)� 어떻게 선언하면 �까요

?



void * (*a5)(char * const, char * const);



오른쪽�서 왼쪽으로 규칙 (중요함)

� 규칙� 매우 간단하지만 어떠한 복잡한 선언문��� 해�할 수 있게해�니다.



선언문� 가장 안쪽� 괄호부터 �기 시작한다. 괄호안�서 가장 오른쪽부터 시작해서 왼쪽으로 �기 시작합니다. � 때 괄호가 발견�

ë©´ 순서가 바뀌어야 합니다. (ì—­ìž� 주: "순서가 ë°”ë€�다"ê°€ 정확히 ì�˜ë¯¸í•˜ëŠ” 바를 파악하기가 어렵습니다. ì œ ì§�ìž‘ì—�는 함수 ìž…ë ¥ì�¸ìž�를 í�¬í•¨í•˜ëŠ” 괄호안ì—�서는 왼쪽ì—�서 오른쪽으로 ìž…ë ¥ì�¸ìž�들ì�„ ì�½ëŠ”ë‹¤ë�¼ëŠ” ì�˜ë¯¸ê°€ ì•„ë‹�까 ì‹ìŠµë‹ˆë‹¤). 괄호안ì�˜ 모든 ë‚´ìš©ì�´ í•´ì„�ë�˜ì—ˆìœ¼ë©´ 괄호 밖으로 í•´ì„�ì�„ 확

대합니다. 모든 선언문� 해�� 때 까지 � 과정� 반복합니다.



"Start reading the declaration from the innermost parentheses, go right, and then go left. When you encounter parentheses, the

direction should be reversed. Once everything in the parentheses has been parsed, jump out of it. Continue till the whole

declaration has been parsed."



오른쪽�서 왼쪽으로 �기 규칙�서 작� 변경 사항: 선언문� 처�으로 �기 시작할 때�는 가장 안쪽� 괄호부터 �기 시작하는게 아

니� 변수 �름 부터 �기 시작합니다.



다� 선언문� 첫 번째 예제로 설명합니다.



int * (* (*fp1) (int) ) 10;



1. 변수 �름부터 시작합니다. --- fp1�

2. 오른쪽� ) �고는 아무것� 없으니까 왼쪽� 보면 *가 있네요 --- ��터입니다. � ��터는 3.� 가리킵니다.

3. 괄호를 벗어나서 오른쪽� 보면 (int)가 있습니다. --- 함수�� int를 입력 ��로 받는군요.

4. 왼쪽으로 가면 *가 있습니다. - � 함수는 ��터를 리턴하는� � ��터는 4.를 가리킵니다.

5. 괄호를 벗어나서 오른쪽� 보면 10 � 있습니다. --- �기가 10� 배열입니다.

6. 왼쪽� 보면 *가 있습니다. --- 배열� 요소는 ��터�� � ��터는 7.를 가리킵니다.

7. � 왼쪽� 보면 int가 있습니다. --- ��터가 int형� 가리킵니다.



어순� 달�서 �어로 표현하는 것보다 한글로 표현하기 무지 힘듭니다. 위� 내용� 종합하면 다�과 같습니다.



"fp1� int형� 가리키는 ��터를 요소로 하는 �기가 10� 배열� 가리키는 ��터를 리턴하면서 int를 입력 ��로 받는 함수를 가리

키는 ��터"입니다.





1. Start from the variable name -------------------------- fp1

2. Nothing to right but ) so go left to find * -------------- is a pointer

3. Jump out of parentheses and encounter (int) --------- to a function that takes an int as argument

4. Go left, find * ---------------------------------------- and returns a pointer

5. Jump put of parentheses, go right and hit 10 -------- to an array of 10

6. Go left find * ----------------------------------------- pointers to

7. Go left again, find int -------------------------------- ints.





다�� � 번째 예제입니다.



int *( *( *arr5)())();



1. 변수 �름부터 시작합니다. --- arr�

2. 오른쪽� 보면 5가 있습니다. --- �기가 5� 배열입니다.

3. 왼쪽� 보면 *가 있습니다. --- 배열� 요소는 ��터�� 4.를 가리킵니다.

4. 괄호를 벗어나서 오른쪽� 보면 ()가 있습니다. --- 함수�� 입력��를 아무것� 받지 않습니다.

5. 왼쪽� 보면 *가 있습니다. --- 함수는 ��터를 리턴하는� ��터는 6.� 가리킵니다.

6. 괄호를 벗어나서 오른쪽� 보면 ()가 있습니다. --- 함수�� 입력��를 아무것� 받지 않습니다.

7. 왼쪽� 보면 *가 있습니다. --- 함수는 ��터를 리턴하는� ��터는 8.� 가리킵니다.

8. � 왼쪽� 보면 int가 있습니다. --- ��터가 int형� 가리킵니다.



"arr� int형� 가리키는 ��터를 리턴하면서 입력��로 아무것� 받지 않는 함수를 가리키는 ��터를 리턴하면서 입력��로 아무것

� 받지 않는 함수를 가리키는 ��터를 요소로 하는 �기가 5� 배열"입니다.



1. Start from the variable name --------------------- arr

2. Go right, find array subscript --------------------- is an array of 5

3. Go left, find * ----------------------------------- pointers

4. Jump out of parentheses, go right to find () ------ to functions

5. Go left, encounter * ----------------------------- that return pointers

6. Jump out, go right, find () ----------------------- to functions

7. Go left, find * ----------------------------------- that return pointers

8. Continue left, find int ----------------------------- to ints.



추가 예제



(역� 주: 여기서 �역� 제가 오른쪽-왼쪽 규칙� 따�가보면서 �접 쓴것�고 �역� �역� 근거해서 나름대로 해�� 한글 어순�

맞게 다시 쓴것입니다.)



float ( * ( *b()) [] )(); // b is a function that returns a

// pointer to an array of pointers

// to functions returning floats.

//

// (�역) b는 함수�� ��터를 리턴합니다.

// � ��터는 배열� 가리키는� 배열� 요소는 ��터입니다.

// � ��터는 함수를 가리키는� � 함수는 float를 리턴합니다.

//

// (�역) b는 float를 리턴하는 함수를

// 가리키는 ��터를 요소로하는 배열� 가리키는 ��터를

// 리턴하는 함수입니다.



void * ( *c) ( char, int (*)()); // c is a pointer to a function that takes

// two parameters:

// a char and a pointer to a

// function that takes no

// parameters and returns

// an int

// and returns a pointer to void.

//

// (�역) c는 ��터�� � ��터는 함수를 가리킵니다.

// � 함수는 char형과

// int형� 리턴하는 함수를 가리키는 ��터를 입력��로 합니다.

// � 함수는 ��터를 리턴는� � ��터는 void를 가리킵니다.

//

// (�역) c는 void를 가리키는 ��터를 리턴하면서

// char형과 int를 리턴하는 함수를 가리키는 ��터를 입력��로 가지는

// 함수를 가리키는 ��터입니다.



void ** (*d) (int &,

char **(*)(char *, char **)); // d is a pointer to a function that takes

// two parameters:

// a reference to an int and a pointer

// to a function that takes two parameters:

// a pointer to a char and a pointer

// to a pointer to a char

// and returns a pointer to a pointer

// to a char

// and returns a pointer to a pointer to void

//

// (�역) d는 ��터�� � ��터는 함수를 가리킵니다.

// � 함수는 int형� 참조와 함수를 가리키는 ��터를 ��로 가집니다.

// � ��터가 가리키는 함수는

// char를 가리키는 ��터와

// char를 가리키는 ��터를 가리키는 ��터를 ��로 가지면서

// char를 가리키는 ��터를 가리키는 ��터를 리턴합니다.

// � 함수는 void를 가리키는 ��터를 가리키는 ��터를 리턴합니다.

//

// (�역) d는 void를 가리키는 ��터를 가리키는 ��터를 리턴하면서

// int형� 참조와

// char를 가리키는 ��터와

// char를 가리키는 ��터를 가리키는 ��터를 ��로 가지면서

// char를 가리키는 ��터를 가리키는 ��터를 리턴하는

// 함수를 ��로 하는 함수를

// 가리키는 ��터입니다.

// ì•…~~~



float ( * ( * e10)

(int &) ) 5; // e is an array of 10 pointers to

// functions that take a single

// reference to an int as an argument

// and return pointers to

// an array of 5 floats.

//

// (�역) e는 �기가 10� 배열�� 그 요소는 ��터입니다.

// � ��터는 함수를 가리키는� 함수는 int형 참조를 입력으로 받으면서

// ��터를 리턴합니다.

// � ��터는 �기가 5� 배열� 가리키는� 배열� 요소는 float입니다.

//

// (�역) e는 float� 요소로 하는 �기가 5� 배열� 가리키는

// ��터를 리턴하면서

// int형 참조를 입력��로 하는 함수를 가리키는 ��터를 요소로하는

// �기가 10� 배열입니다.



�래스 멤버 함수 ��터

�문�서는 설명� 없었지만 �래스 멤버 함수 ��터� 경우�� 똑같� 규칙� �용� 수 있다는걸 알 수 있습니다. �문� 달린 답글

중�서 예제를 복사해왔습니다.



int (CFoo::*p)(); // p is a pointer to a method in class CFoo

// that takes no argument and return an int

//

// (�역) p는 ��터�� � ��터는 CFoo �래스� 멤버함수를 가리킵니다.

// � 멤버함수는 아무런 입력��� 받지 않고 int를 리턴해�니다.

//

// (�역) p는 아무런 입력��� 받지 않고 int를 리턴해주는

// CFoo �래스� 멤버 함수를 가리키는 ��터입니다.



í˜¸ìœ ê·œì•½ (calling convetion)

ê°™ì�€ ë§¥ë�½ìœ¼ë¡œ 호ìœê·œì•½ì�´ 들어간 경우ì—�ë�„ 똑같ì�€ 규칙ì�„ ì �용합니다.



int (__stdcall* q)(); // q is a pointer to a __stdcall function

// that takes no argument and return int

//

// (�역) q는 ��터�� � ��터는 __stdcall 함수를 가리킵니다.

// � 함수는 아무런 입력��� 받지않고 int를 리턴해�니다.

//

// (�역) q는 아무런 입력��� 받지않고 int를 리턴해주는

// __stdcall 함수를 가리키는 ��터입니다.
|}}

Valid XHTML 1.0! Valid CSS! powered by MoniWiki
last modified 2021-02-07 05:22:47
Processing time 0.0190 sec