Java Onboarding도중에 pointer에 대한 언급이 나와서 또 재미있는 고민을 하기 시작했다.
안그래도 지금 C언어의 보안형인 Rust 언어를 공부하면서 포인터를 다룰 일이 많아졌는데, 학부 1학년 과정에서 배운 포인터의 필요 이유는 동적 메모리 할당이라고 기계적으로 암기만 했지, 실제로 하드웨어 전반적인 플로우 상에서 이 포인터의 필요성을 정확히 이해하고 넘어간 건 아니었다.
그래서 이번 기회에 포인터에 대해 완전 뜯어보려고 한다.
Java에서는 C나 C++에서 사용하는 포인터 기능을 따로 쓰지 않는다고 한다. (영어 원문에 대한 내 해석이 맞다면)
그러면 이 포인터라는 개념이 왜 등장한 것인가??
C 포인터는 메모리 주소를 직접 접근해서 하드웨어 제어, 동적 메모리 할당, 효율적인 자료 구조 조작 및 함수 매개변수 전달에 사용된다.
다른 언어와 달리 C는 추상화가 되어 있지 않어 기계어에 가깝게 작용하며, 이를 통해 불필요한 연산을 줄여 성능을 향상시킬 수 있다.
실제로 C언어가 파이썬에 비해 실행 속도가 압도적으로 빠른 이유 역시 개발자가 그만큼 하드웨어 레벨 가까이에서 작업하기 때문인 것과 동치.
그래서, 이 포인터는 왜 쓰는가
특정 메모리 위치를 직접 지정하고 조작할 수 있어 하드웨어에 대한 상세한 제어가 가능하다.
갑자기 든 생각
2000년대 핸드폰은 보조배터리가 있어서 핸드폰에 배터리를 갈아끼우는 방식으로 사용했었다.
그러다가 배터리가 일체형으로 바뀌면서 사용자는 핸드폰만 충전하면 되는 방식으로 바뀌었다.놀라운 점은, 최근에 어느 SNS 게시글에서 봤는데, 초등학교 학생이 '이런 걸 발명하면 어떨까?' 하는 생각에 '교환형 배터리'라는 걸 착안해서 내놨다는 것이다.
즉, 역사는 반복된다... 결국 돌고돌아 다시 이렇게 오는 건가.. 싶은 느낌
위의 얘기가 갑자기 왜 나왔느냐, 결국 Rust는 C언어에서 메모리 직접 접근으로 인해 생기는 보안 문제들을 해결하기 위해 더욱 엄격한 Privilege 시스템을 고안해낸 언어라고 알고 있다.
그런데 어떻게보면 C/C++에 비해 더 유저 인터페이스에 가까운 언어들인 Java, Python같은 언어들도 결국엔 같은 목적을 갖고 한 건 아니더라도 이 복잡한 기능을 더 유저 친화적으로 바꿨던 것 아닌가..그냥 위의 예시와 비슷한 흐름인 것 같았다 :)
아무튼, 그래서 메모리에 직접 접근이 가능하다. (보안 관점에서 보면 최악)
사실 이게 메인이라고 배웠다. 실제 런 타임 동안에는 메모리가 얼마나 필요한 지 알 수 없다. 하지만 이렇게 메모리 주소를 직접 접근하게 되면, 힙 영역에 내가 원하는만큼 언제든 할당해서 사용할 수 있다.
즉, 포인터를 쓰지 않은 채 변수를 선언한다면 이는 임의로 설정된 데이터 크기를 바탕으로 컴파일 시점에 알아서 시작점(주소)이 고정되어버려 사이즈 변경이 불가능한 데 비해, 포인터로 선언한다는 것은 시작점(주소)을 내가 설정하는 것이므로 얼마든 다른 주소로 변경이 가능한 것이다.
-> 고정 배열 : '나는 여기서부터 10칸(예시)짜리 메모리를 쓴다' -> 시작점이 컴파일 시점에 결정, 변경 불가.
-> 포인터 : '나는 어떤 메모리 블록의 시작 주소를 저장한다' -> 실행 중에 얼마든 다른 주소로 변경 가능.
정리하자면, 두 방식의 차이는 '변수의 시작점(주소)을 내가 바꿀 수 있느냐 없느냐'에 있는 것이다.
배열, 구조체 등 복잡한 데이터 구조의 데이터를 효율적으로 접근하고 조작하는 데 용이하다.
Pass by value 방식과 달리, 함수에 변수의 메모리 주소를 전달하여 함수 내에서 원본 변수의 값을 직접 변경할 수 있다.
-> 아마 Java 정신으로 보면 극혐할만한 것이겠네요.
크기가 큰 구조체나 배열을 복사하지 않고, 포인터만 전달하므로 메모리 이득 및 전달 효율성 증가를 가져갈 수 있다.
그래서 결론적으로 포인터는 기계어와 매우 가깝게 추상화되어있기 때문에 불필요한 연산이나 메모리 소모가 거의 없으므로 훨씬 효율적인 코드를 생성하고 성능을 높일 수 있다.
C는 다른 언어와 달리 메모리 주소를 직접 다를 수 있는 유일한 언어이며, 포인터는 이러한 C의 핵심 기능이다. 다른 언어에서 제공하는 참조 기능보다 훨씬 간결하고 효율적으로 동작한다.
이제야 왜 C언어가 Python에 비해 어려운 지 깨달았다.
C는 내가 직접 하나하나 찾아서 구현해야 하기 때문인데, 그 '찾아서 구현한다'는 개념이 기계어에 가까운 레벨에서 메모리 주소를 찾아가며 코딩한다는 의미인 것 같다.