핫게 실시간 커뮤니티 인기글
종합 (4063518)  썸네일on   다크모드 on
에로망가.. | 18/03/23 10:00 | 추천 15 | 조회 2496

C언어) 그리면서 배우는 포인터와 배열 +154 [18]

디시인사이드 원문링크 m.dcinside.com/view.php?id=superidea&no=139777

int (**(*(*a)[2])[3])[4]; 은 어떻게 읽어야 할까요?


포인터나 배열 등 표기연산자의 우선순위에 대한 규칙을


Clockwise Rule 또는 Spiral Rule 이라고 부릅니다


이 규칙에 대해서는 


http://gall.dcinside.com/board/view/?id=pridepc_new3&no=7459263


여기에 적어 놓았습니다만 간단히 요약하면


포인터나 배열 등의 표기연산자에서 선언된 문장을 읽는 우선순위는


변수명을 시작으로 


1순위 ( ) 괄호

2순위 [ ] 배열

3순위 * 포인터

4순위 +n 주소연산


순서대로 읽어주면 됩니다 (함수포인터는 이 글에서 다루지 않으므로 제외합니다)


이 글에서 알아볼 사항은 크게 3가지입니다


1. 알아보기 쉽게 그림으로 그리는 법

2. 원하는 노드까지 찾아가는 법 

3. 특정 위치에서 타입을 결정하는 법







1. 알아보기 쉽게 그림으로 그리는 법


image


그림을 그리기 전에 위 3가지 표시방법을 알아 둘 필요가 있습니다


이것은 제가 1학년 때 C언어를 배우면서 독자적으로 개발 한 방법입니다


int (*((*(*fg)(int,double))[3])[2])(); 와 같이 복잡한 구문들이 시험에 많이 나왔거든요


이러한 복잡한 구문을 어떻게 하면 빨리 읽고 가공할수 있을까? 생각했습니다


저는 이것을 '에로망가 다이어그램' 이라고 부르고 있습니다 












image


int (**(*(*a)[2])[3])[4]; 라는 문장은 언뜻 보기에 매우 복잡해 보입니다만


그림을 그려서 보면 구조가 매우 간단합니다


그림을 그리는 순서는 먼저 상단의 표기연산자 우선순위라는 것을 봐주세요


이것은 위에서 설명한 Clockwise Rule 또는 Spiral Rule의 우선순위를 따릅니다


그 우선순위 결정방법을 이용해


int (**(*(*a)[2])[3])[4]의 우선순위를 탐색한 결과가 상단의 박스에 표시되어 있습니다 


이 우선순위 대로 바로 위 문단에서 설명한 3가지 표시방법을 이용해 그리면 됩니다


각 노드끼리는 _ 이음표로 이어집니다


배열은 [2]의 경우 두개의 □가 세로로 배치되며


포인터는 그저 * 표시만 하면 됩니다


이 다이어그램을 그리는 것까지는 매우 쉽게 할 수 있습니다


이제 이 다이어그램을 읽을 수 있어야 겠죠?


동시에 원하는 노드까지 찾아가는 법도 배워보도록 하겠습니다
















2. 원하는 노드까지 찾아가는 법



image



원하는 노드까지 찾아가는 법을 배우기 전에


먼저 위의 3가지 연산을 알아 둘 필요가 있습니다 (이동연산, 점프연산, 주소연산)


직관적이기 때문에 그림만 봐도 의미를 잘 알수 있을 거에요


하지만 1) 번은 조금 설명이 필요합니다


1)번을 봐주세요 


여기서 이음표의 의미는 A의 값이자 B의 주소를 뜻합니다


또한 A의 값은 주소입니다


왜 A의 값이 주소라는 말이 나오느냐?


에로망가 다이어그램의 마지막 노드를 보면 


int, double, char 등 마지막 노드에서 실제 데이터 할당 타입이 결정됩니다 


우리가 잘 아는 정수 int나 문자 char 등의 주소가 아닌 실제 값은 가장 마지막에 노드에서 결정되고 할당되는 것입니다


또한 마지막 노드를 제외한 모든 다른 노드들은 값으로 주소를 가진다는 것을 주목해 주세요


이 문장은 아주 중요한 의미를 지닙니다


마지막 노드가 아니라면 배열이라도 원소값으로 주소를 가집니다


이걸 깨닫는 것은 상당히 중요합니다


이 점은 2차원 int arr[2][3]에서 arr[1]의 값이 왜 주소인가를 설명하는 것과 정확히 일맥상통합니다


또 한가지 알려드릴 점은 이음표는 우리의 현재 위치를 뜻합니다


image


그도 그럴 것이 우리가 현재 위치라고 말하는 것이 보통 '값'을 말하는 것이기 때문이죠


현재 위치가 *나 □같은 노드 그 자체로 받아들이면 절대 안됩니다


우리의 현재 위치는 바로 '이음표 _' 부분입니다


왜 그런지 살펴볼까요?


_ 이음표 위치는 왼쪽 노드의 연산을 한 값인 동시에 오른쪽 노드의 연산을 하기전의 주소입니다


(이 문장이 잘 이해 되지 않으면 현재 위치가 이음표 부분이다라는 것만 알고 일단 넘어가세요)


우리는 보통 a[2][3]; 과 같이 어떠한 연산을 한 결과값을 현재 위치라고 생각할 것입니다


그렇기 때문에 현재 위치는 에로망가 다이어그램에서


*나 □같은 노드 그 자체가 아니라 노드 사이에 있는 이음표가 되는 것입니다 













image

위의 3지 연산(이동연산, 점프연산, 주소연산)을 이용해 위와 같이 도착지점에 이동할 수 있습니다


위 문단에서 _ 이음표가 현재 위치라고 말했기 때문에


출발지가 a가 아니라 a의 오른쪽 _ 이음표부터 시작합니다


주목할 점은 마지막 도착지점을 보세요


도착은 □의 오른쪽, int의 왼쪽에서 끝이 납니다


중간 노드들에서는 값이 주소만 들어갈 수 있었는데 마지막 노드는 아닙니다


여기서는 int, double, char 등과 같은 실제 프리미티브 타입이 결정되는 곳입니다


따라서 마지막 노드의 값은 더 이상 주소값을 가지지 않으며 int, double 등과 같은 타입을 갖게 됩니다


또한 도착지는 int의 왼쪽 이음표에서 끝이 납니다


이 마지막 이음표가 의미하는 바는


'□연산을 한 값의 타입은 int 타입이다' 를 의미합니다


이제 한 번 제대로 되는지 실제 코딩을 통해 알아볼까요?





 




image


위와같이 밑에서부터 데이터를 입력해주면서 int (**(*(*a)[2])[3])[4] 타입까지 끌어 올렸습니다


그 이후 printf에 위에서 알아본 바와 같은 주소 연산을 통해


가장 끝부분 노드에 입력된 0, 1, 2, 3을 출력해 내는데 성공했습니다


한가지 더 알려드릴 점은


위의 printf 구문에서는 주소 연산을 +1, +2와 같이 표기했는데


[1], [2] 표기할 수도 있습니다









image


C언어는 g[1]을 컴파일하면서 *(g+1)로 바꿔버립니다


하지만 인간이 읽기엔 g[1]이 훨씬 쉬울 것입니다


위 코드를 봐주세요


g[0]이라고 해도 되지만


1[g]나 2[g]라고 표기해도 정상적으로 컴파일 합니다


이것은 C언어가 컴파일 시 배열 표기를 모두 주소연산과 참조연산으로 바꿔버리기 때문입니다


이걸 활용하여 아래와 같이 구문을 줄일 수도 있습니다













image


*a는 a[0]대신 *a로 적는게 의미론적으론 나을수 있겠습니다만은


*와 배열이 섞이게 되면 표현연산식의 우선순위에서 배열이 포인터보다 높기에


중첩된 괄호를 써야 합니다


*는 변수명의 왼쪽에 써야하므로 중첩된 괄호 문장에서 제대로된 의미를 읽는 것은 매우 힘들엉집니다 


하지만 *a까지 [0]으로 표기하면 위와 같이 아주 읽기 쉽게 표기할 수 있습니다


배열이 연속해서 있을 경우 왼쪽 배열이 큰 우선순위를 가지기 때문에 왼쪽에섯부터 직선으로 읽어나가면 되겠습니다 


이제 마지막으로 특정 노드의 위치에서 타입을 결정하는 법을 배우도록 하겠습니다 













3. 특정 위치에서 타입을 결정하는 법




image







현재 위치가 파란색 동그란 부분이라고 해보겠습니다


이 파란색 부분에 도달할때까지


a라는 변수는


*연산


배열연산([0])


*연산


배열연산([1])


을 순서대로 거쳐 파란색 위치에 도달하였습니다


이 지점에서의 타입은 무엇일까요?


함수의 파라미터로 넘길때 현재 위치의 타입을 알 필요성이 있습니다


타입을 결정하는 법은 간단합니다


파란색 원 부분에서 위로 직선을 그으세요


그 다음 표기 연산자 우선순위 박스가 나오면 오른쪽으로 과감히 선을 그으세요


이 때 직선 부분 오른쪽에 있는 연산자들은 순서대로 


* * [4] int 입니다


이 우선순위대로 타입을 결정지어 주게 되면 됩니다


타입을 결정짓는 방법은 Clockwise Rule 또는 Spiral Rule의 우선순위대로 결정지어 주면 됩니다


( ) 괄호가 우선순위가 가장 높고


그다음이 배열, 그 다음이 포인터 입니다


* * [4] int의 순서대로 결정짓기 위해서


두개의 포인터 연산을 먼저 하고


**


그 다음에 배열을 써야하는데 **[4]로 쓰게 되면 포인터보다 배열부터 먼저 연산을 하겠죠


그러므로 (**) 포인터 2개를 괄호로 묶읍시다


(**)[4] 끝났습니다 마지막에 남은 int만 써주면 됩니다


파란색 위치에서의 타입은 int (**)[4]입니다


함수의 파라미터로 넘길 변수명은 int (**p)[4]와 같이 써 주면 됩니다







이제 모든 설명을 끝마쳤습니다


제가 말씀드린 과정이 숙달되면 암산으로 하거나 종이에 그려 


30초도 안되는 시간내에 원하는 바를 얻어낼 수 있습니다


함수포인터가 섞인 문장이나


그 외에도 몇가지 중요한 팁이 있습니다만


그것은 다음 기회에 적도록 하겠습니다 



[원본 갤러리에서 보기]

S : 161118 div추가 -->
[신고하기]

댓글(18)

1 2

이전글 목록 다음글

12 3 4 5
    
제목 내용