Even Idiots Can Make Game
포인터를 어떻게 선언해야 할까
#1 포인터 선언 관습의 차이
C++ 코드를 보다 보면 포인터 변수를 다음과 같이 선언하는 것을 볼 수 있다.
int* p;
반면, C 언어를 주로 사용해 온 경험 많은 프로그래머들은 다음과 같은 방식으로 선언하는 경우가 많다.
int *p;
즉, *
기호를 변수명 앞에 붙이느냐, 자료형 뒤에 붙이느냐의 차이가 존재한다.
#1 C++ 프로그래머들의 멘탈 모델
int* p
는, a
의 타입을 int*
라는 한 덩어리로 간주하여 코드의 가독성을 높이겠다는 의도를 반영한 표현이다. 이는 얼핏 보면 타당한 방식처럼 보인다.
하지만 다음과 같은 상황에서는 어떨까?
int* a, b;
원래 한 줄에 두 개 이상의 변수를 선언하는 것은 좋지 않은 습관이지만, C++ 프로그래머들이 제시하는 멘탈 모델에 따르면, 위 코드는 int*
형식의 변수 a
와 b
를 선언한 것이 되어야 할 것이다.
하지만 실제로는 a
는 int*
형식이지만, b
는 int
형식이다.
배열 선언에서도 비슷한 문제가 발생한다.
int a[10];
위 선언은 크기 10의 int
배열을 생성한다. a
의 형식은 int[10]
이다. 그런데 C++ 프로그래머의 멘탈 모델을 따른다면 다음과 같이 작성해야 할 것 같다:
int[10] a;
하지만 이는 문법적으로 유효하지 않으며, 컴파일 오류를 발생시킨다. 포인터 선언과 배열 선언에서의 일관성이 존재하지 않는다.
즉, 이러한 멘탈 모델에 뭔가 문제가 있다는 뜻이다.
#1 선언의 문법적 구조
C/C++에서 선언(Simple Declaration)은 Specifier와 Declarator의 조합으로 이루어진다. 구체적인 구조는 cppreference에서 볼 수 있으며, 간단히 요약하면 다음과 같은 구조이다:
// 단순 선언의 구조 (Simple Declaration):
[Specifier의 목록] [Declarator의 목록];
Specifier
Specifier에 해당하는 것들은 정말 많은 것들이 있다. const
, volatile
, static
과 같은 키워드들이 여기에 속하며, int
, float
과 같은 자료형의 이름들 또한 여기에 속한다.
우리가 선언의 좌측에서 으레 기대하는 것들이 여기에 속한다고 보면 된다.
Declarator
Declarator는 변수의 이름을 포함하며, *
나 []
같은 문법 요소 또한 Declarator의 일부이다. 이 요소들은 해당 요소가 어떻게 접근될지를 나타낸다.
여기에서 C++ 프로그래밍 식 멘탈 모델에 문제가 문법적으로 드러난다.
*
나int
이 모두 동일한 문법적 토큰인 줄 알았는데, 사실 아니었다.*
는 Declarator이고, 식별자의 이름과 동일한 소속이다. 반면int
는 Specifier 소속이다.
#1 새로운(?) 멘탈 모델!
왜 이러한 괴리가 발생할 것인지 감히 추측해보건데, 다른 고수준 언어들의 영향이 아닐까 싶다.
C# 에서는 다음이 문법상 아무런 문제가 없다.
int[] a1, a2;
a1
과 a2
는 모두 int[]
라는 완전한 형식이다.
Java 에서는 두 방식을 모두 허용한다.
int a1[], a2[];
int[] a1, a2;
여기서도 a1
과 a2
는 모두 int[]
라는 완전한 형식이다.
C#과 Java와 같은 고수준 언어들은 자료형을 보다 추상적인 개념으로 캡슐화하고 있으며, 선언 방식에서도 일관성을 유지하려고 한다.
반면, C/C++는 보다 저수준의 메모리 제어를 중시하며, 값이 메모리에 어떻게 배치되어 있는지를 강조하는 문법적 구조를 가지고 있다. 즉, *
와 []
와 같은 요소들은 자료형의 일부가 아니라, 해당 변수에 어떻게 접근할지를 나타내는 표기법이다.
예제 분석
int *p
라는 선언은 다음과 같은 의미이다.p
라는 식별자를 지금부터 선언 할 것인데, 이 식별자와 연관이 있는 메모리 블록의 값은*
연산자를 통해 접근할 수 있으며,int
형식으로 메모리 블록을 읽어야 유의미한 값이 나온다.
추가적인 예를 보자.
const int **p, a; // (1)
float fa[10]; // (2)
bool bar(int, int); // (3)
- 예제 (1)
p
에 들어있는 값은*
연산자를p
에 두 번 적용하면 얻을 수 있고, 얻어낸 값의 형식은const int
이다.a
에 들어있는 값은a
를 통해 바로 얻어낼 수 있으며, 얻어낸 값의 형식은const int
이다.
- 예제 (2)
fa
는float
이 10개 연속한 메모리 블록이며, 각 메모리 블록에는[]
연산자를 통해 접근할 수 있다. 그렇게 얻어낸 메모리 블록의 형식은float
이다.
- 예제 (3)
bar
가 가리키는 값은int
형 매개변수 두 개로 호출()
연산자로 접근할수 있고, 접근 하면 그 값의 형식은bool
이다.
이처럼 C/C++의 선언 방식은 단순히 자료형을 묶는 것이 아니라, 변수의 접근 방식까지 고려하는 구조로 이루어져 있다.
#1 결론
C++ 스타일의 int* p
표기법은 고수준 언어 트렌드에 영향을 받아 가독성을 높이려는 의도를 가지고 있지만, 문법적으로 완벽한 일관성을 갖추고 있지는 않다.
반면, int *p
표기법은 C/C++의 기본적인 선언 규칙과 더 잘 부합하며, C/C++의 핵심 중 하나인 메모리의 저수준 제어를 드러내는 표기법이라고도 볼 수 있다.
하지만 이러한 표기법을 포함한 전체적인 코딩 스타일에 대해서는 일관성을 유지하는 것이 가장 중요하며, 특정 스타일을 따르더라도 팀에서 합의된 코딩 스타일 가이드를 준수하는 것이 우선이라고 볼 수 있겠다.
#1 Reference
- Declarations/cppreference
- What are declarations and declarators and how are their types?/StackOverFlow