JSCC: JavaScript로 개발하는 C Compiler

선언 분석의 규칙

토픽 JSCC: JavaScript로 개발하는 C Compiler > C의 선언

3.2) 선언 분석의 규칙

C의 선언은 다음을 규칙으로 한다.

이름을 기준으로 한다.

이름의 오른쪽부터 해석한다오른쪽의 해석이 끝나면 왼쪽을 해석한다.

이름에 가까운 괄호부터 먼저 해석한다.

이 세 가지 규칙만으로 C의 모든 선언을 분석할 수 있다다음 예제들을 보자.

int var; // var: int

이름은 var이다이름의 오른쪽에 요소가 없으므로 왼쪽 분석을 진행한다다른 요소가 없으므로 분석이 끝난다. var는 int.

int arr[5]; // arr: array[5] of int

이름은 arr이다이름의 오른쪽에 있는 배열 기호를 획득한다이름의 오른쪽에 더 이상 요소가 없으므로 왼쪽 분석을 진행한다다른 요소가 없으므로 분석이 끝난다. arr는 array[5] of int, 즉 크기가 5인 int형 배열이다.

int *ptr; // ptr: pointer to int

이름은 ptr이다이름의 오른쪽에 요소가 없으므로 왼쪽 분석을 진행한다왼쪽에 있는 포인터 기호를 획득한다다른 요소가 없으므로 분석이 끝난다. ptr는 pointer to int, 즉 int형 변수에 대한 포인터 변수다.

int arr2d[3][5]; // arr2d: array[3] of array[5] of int

이름은 arr2d이다이름의 오른쪽에 있는 배열 기호를 차례로 획득한다오른쪽 분석이 끝나고 왼쪽을 분석하는데,왼쪽에 남은 요소가 없으므로 분석이 끝난다. arr2d는 array[3] of array[5] of int, 즉 ((int형 변수 5개의 배열) 3개의 배열)이다이 부분이 혼란스러울 수 있는데 typedef를 이용하여 다음과 같이 정의된 것이라고 이해하면 될 것이다.

typedef int intArr5[5];

intArr5 arr2d[3];

int *ptrarr[5]; // ptrarr: array[5] of pointer to int

이름은 ptrarr이다이름의 오른쪽에 있는 배열 기호를 획득한다오른쪽 분석이 끝나고 왼쪽을 분석한다왼쪽에 있는 포인터 기호를 획득한다다른 요소가 없으므로 분석이 끝난다. ptrarr는 array[5] of pointer to int, 즉 ((int형 포인터 변수) 5개의 배열)이다.

int (*arrptr)[5]; // arrptr: pointer to array[5] of int

이름은 arrptr이다이름의 오른쪽에 있는 배열 기호를 획득하는데괄호를 먼저 해석해야 하므로 괄호 바깥은 해석하지 않는다괄호 안에 있는 건 *arrptr뿐이고 현재 arrptr의 오른쪽에 아무 것도 없으므로 왼쪽 분석을 진행하여 포인터를 먼저 획득한다괄호 내에서 분석할 것이 없으므로 괄호를 탈출한다그리고 다시 오른쪽부터 분석을 진행한다오른쪽에 있는 배열 기호를 획득한다남은 요소가 없으므로 분석을 종료한다. arrptr은 pointer to array[5] of int, 즉 ((int형 변수 5개의 배열)에 대한 포인터 변수).

int fnc(); // fnc: function() returning int

이름은 fnc이름의 오른쪽에 있는 함수 기호를 획득한다(이때 괄호의 의미는 fnc가 함수라는 것을 나타내는 것이지선언 분석에서 먼저 분석해야 함을 의미하는 것은 아니다). 오른쪽의 분석이 끝났으므로 왼쪽을 분석하는데 왼쪽에 요소가 없으므로 분석이 끝는다. fnc는 function() returning int, 즉 int형 값을 반환하는 함수다.

아래에 제시되는 것은 올바른 분석 방법을 설명하기 위한 적법한 선언이지만실제로는 C의 특수성 등과 같은 이유로 컴파일러가 정상적으로 해석할 수 없는 선언문이 섞여있다.

int arr_fnc()[5]; // arr_fnc: function() returning array[5] of int

이름은 arr_fnc이다이름의 오른쪽에 있는 기호를 차례로 획득하므로함수 기호를 획득하고 배열 기호를 나중에 획득하게 된다오른쪽의 분석이 끝났는데 왼쪽에 아무 것도 없으므로 분석이 끝난다. arr_fnc은 function() returning array[5] of int, 즉 (int형 변수 5개의 배열)을 반환하는 함수다. (실제로 적용할 수 없음)

int *ptrarr_fnc()[5]; // ptrarr_fnc: function() returning array[5] of pointer to int

이름은 ptrarr_fnc이다이름의 오른쪽에 있는 기호를 차례로 획득하므로함수 기호를 획득하고 배열 기호를 나중에 획득하게 된다오른쪽의 분석이 끝났으므로 왼쪽을 분석하여 포인터 기호를 획득한다. ptrarr_fnc는 function() returning array[5] of pointer to int, 즉 ((int형 변수에 대한 포인터 변수) 5개의 배열)을 반환하는 함수다. (실제로 적용할 수 없음)

int (*arr_fncptr)()[5]; // arr_fncptr: pointer to function() returning array[5] of int

이름은 arr_fncptr이다이름의 오른쪽에 있는 기호를 차례로 획득하는데 괄호를 먼저 해석해야 한다괄호 내에는 오른쪽에 요소가 없으므로 왼쪽을 진행하여 포인터 기호를 획득한 후 괄호를 탈출한다이후 다시 오른쪽에 있는 함수 기호와 배열 기호를 차례로 획득하고 분석이 끝난다. arr_fncptr는 pointer to function() returning array[5] of int, 즉 ((int형 변수 5개의 배열)을 반환하는 함수)에 대한 포인터다. (실제로 적용할 수 없음)

int (*arrptr_fnc())[5]; // arrptr_fnc: function() returning pointer to array[5] of int

이름은 arrptr_fnc이다이름의 오른쪽에 있는 기호를 차례로 획득하는데 괄호를 먼저 해석해야 한다괄호 내에 함수 기호가 있으므로 먼저 획득한 후왼쪽에서 포인터 기호를 획득하고 괄호를 탈출한다이후 다시 오른쪽에 있는 배열 기호를 획득하고 분석이 끝난다. arrptr_fnc는 function() returning pointer to array[5] of int, 즉 ((int형 변수5개의 배열)에 대한 포인터)를 반환하는 함수다위의 예제와 달리 이 선언은 실제로 적법한 선언인데 그 이유는 나중에 밝히겠다.

char (*(*x[3])())[5]; // x: array[3] of pointer to function() returning pointer to array[5] of char

이름은 x이름의 오른쪽에 있는 배열 기호를 획득하고 포인터 기호를 획득한 후 괄호를 탈출한다이후 남은 부분에 대해 다시 괄호 내의 기호를 해석하여 함수 기호를 얻고 포인터 기호를 획득한 후 괄호를 탈출한다이후 오른쪽의 배열 기호를 획득하면 남는 요소가 없으므로 분석이 끝난다. x는 array[3] of pointer to function() returning pointer to array[5] of char아주 길지만 정리해서 말하면, x는 ((((char형 변수 5개의 배열)에 대한 포인터)를 반환하는 함수)에 대한 포인터) 3개의 배열이다놀랍게도 이 선언 또한 적법한데 그 이유는 위와 같다.

 

이 내용을 코드로 정리하면 다음과 같다.

int var; // var: int

int arr[5]; // arr: array[5] of int

int fnc(); // fnc: function() returning int

int *ptr; // ptr: pointer to int

int arr2d[3][5]; // arr2d: array[3] of array[5] of int

int *ptrarr[5]; // ptrarr: array[5] of pointer to int

int (*arrptr)[5]; // arrptr: pointer to array[5] of int

int fnc(); // fnc: function() returning int

int arr_fnc()[5]; // arr_fnc: function() returning array[5] of int

int *ptrarr_fnc()[5]; // ptrarr_fnc: function() returning array[5] of

// pointer to int

int (*arr_fncptr)()[5]; // arr_fncptr: pointer to function() returning

// array[5] of int

int (*arrptr_fnc())[5]; // arrptr_fnc: function() returning pointer to

// array[5] of int

char (*(*x[3])())[5]; // x: array[3] of pointer to function() returning

// pointer to array[5] of char

이와 같이 C의 선언에 대해 이해할 수 있었다.

댓글

댓글 본문