포인터

Pointer

1 레이저포인터

레이저를 이용해서 무언가를 가리키는 용도로 쓰는 물건. 물론 원래의 용도만으로 쓰이지는 않는다. 항목 참고.

2 개의 품종

품종 중 하나. 원산지는 영국으로 추정하고 있다. 총을 든 사냥꾼과 팀을 이뤄서 사냥하는 개인 Gun Dog이다. 새 사냥에 특화되어 있으며, 수컷은 성체가 60~70cm에 25~34kg, 암컷은 58~66cm, 20~30kg 정도의 크기인 중형견에 속한다.

포인터(Pointer)라는 이름이 붙은 것은 사냥감을 찾으면 특수한 자세를 취해서(이 자세를 Point라고 한다) 사냥꾼에게 사냥감의 위치를 가리키기 때문이다. 루니툰의 엘머퍼드가 몇 에피소드에서 데리고 다니는 사냥개가 이 자세를 취하는 것으로 보아 포인터 종인 것으로 추측된다. 앞다리 한쪽을 들고 남은 세 다리로 버티며 무슨 전진무의탁 자세마냥 앞으로 쓰러진듯 버티고 서는 자세다. 총을 맞은 사냥감을 물어 오는 것도 이 개의 일.

사냥 견종이라서 힘이 넘치는 걸 제외한다면 공격성이 낮거나 거의 없는 순한 성격이라 아이들과도 잘 어울려 주고, 다른 개와 같이 키우는 것도 쉽다. 단, 저 넘치는 힘을 소모시켜주지 않으면 난리를 치고 다니므로(...) 적절히 운동을 시켜주는 것도 필수. 평균 수명은 12~15년 정도이다. 생각보다 분양가도 낮고 분양받기 쉽다.

반려견으로도 많이 키우지만, 공격 목적으로 길러진 견종이 아니라서 경비견으로는 인기가 없다.

3 프로그래밍 용어

C 위주의 자세한 설명이 필요하신 분은 이 문단 을 참고해주십시오.

C에 포인터가 있다면 JavaOOP와 레퍼런스가 있단다 C++엔 셋 다 있단다
컴퓨터 프로그래밍에서 이용되는 용어로, 어떤 값을 가리키기 위한 형식을 뜻한다.

시초는 기계어(와 어셈블리어)에서 이용되는 간접 참조인데, 이는 명령어에서 이용할 값을 명령어 코드에 직접 쓰는 것이 아니라 특정 메모리 번지에 있는 값을 읽어서 이용하라고 하는 것이다. 예를 들어 더하기를 할 때 '3'을 더해라"고 할 수도 있지만 '메모리 1000에 저장된 값'을 더해라"고 해야 할 수도 있다. 이 때 메모리의 값을 먼저 읽는 명령을 쓰고, 더하는 명령을 따로 쓰면 아무래도 효율이 떨어지기 때문에 같이 처리하는 명령이 생기게 되었다. 이후 이러한 아이디어가 CC++ 등 다른 프로그래밍 언어에도 적용되면서 현재의 포인터가 되었다.

처음 배울때는 그냥 값을 사용하면 되지 왜 포인터처럼 복잡하게 주소를 써서 머리를 아프게 하냐고 생각할 수 있는데 포인터가 실제로 사용되는 예를 들어 간단히 설명하자면
서로 다른 함수에서 선언된 변수들은 함수의 변수 공간이 서로 다르기 때문에 해당 변수를 직접 사용할 수는 없다. 이때 포인터를 이용하면 서로 다른 함수 내의 변수값을 주소를 통해서 제어할 수 있게 만드는 도구로 쓸 수 있다. 미적분을 배울때 이걸 함수를 이해하는 도구로 써먹느냐 단순히 머리 아픈 수학공식으로 이해하느냐의 차이라고 볼 수 있다

프로그래밍 언어에 쓰이는 다른 기능들이 2차원 평면적이라면, 포인터는 거기에 수직적인 축을 하나 더 만들어 수직으로 움직이는셈이 되기때문에 포인터가 사용되면 말그대로 '입체적으로' 복잡해진다. 아무래도 한 단계를 더 거치는 특성상 생각해야 할 것이 많아지기 때문에 이용하기도 쉽지 않다. 특히 포인터가 이중, 삼중으로 쓰이거나 하면 난이도는 수직상승하기 때문에 프로그래머의 골머리를 썩히는 일등공신으로 꼽힌다. 게다가, 포인터 관련 에러는 디버깅으로 잡기 힘든 버그들도 많고, 컴파일러에서 행하는 여러가지 최적화들이 문제가 생길 여지도 많아진다. 여러모로 단점이 상당히 많아보이지만 하드웨어 컨트롤이나 그래픽관련 퍼포먼스에 관해서는 그런걸 전부 감안해도 포인터를 사용해서 얻어지는 이득[1]이 워낙 크기때문에 일종의 양날의 검이라 할 수 있다.

수많은 전자공학, 컴퓨터공학을 지망하여 들어온 대학생 1학년들이 여기서 좌절하고, 전공을 바꾸려한다. 특히 C 포인터에서 가장 악마같은건 포인터와 배열을 왔다갔다하는 장난질. 이쯤에 오면 그들에게는 언어가 아니고, 외계어가 된다. 여기를 넘어가느냐 안가느냐에 따라 남은 대학생활의 학점이 결정된다고 해도 과언이 아니다. 그리고 자바 프로그래머가 될지 C++프로그래머가 될지도 결정된다.[2]

크고 아름다운 데이터-특히 객체-를 다루는 현대의 프로그래밍 언어에서 포인터가 아주 없기란 사실상 불가능하지만, 자바C\#과 같이 최근의 고생산성 언어에서는 포인터를 가능한 한 직접적으로 건드릴 필요가 없는 방향으로 가고 있다. 하지만 막 C에서 Java로 넘어온 학생들은, 포인터가 없어서 더 불편해한다

위에서 사람을 엄청 겁주고 있는데, 하지만 포인터 자체는 그냥 숫자 변수일 뿐이다. 정말로. 하지만 그 숫자가 메모리 주소를 가리킨다는 점이 다른 숫자 변수와 다를 뿐이다. 메모리 주소는 정수이므로 정수형 타입을 사용하고 있으며, 32비트 아키텍처 상에서는 32비트 정수, 64비트 아키텍처 상에선 64비트 정수를 사용한다. 끝. 포인터의 포인터? 숫자 보고 찾아간 주소 안에 다른 주소를 가리키는 숫자가 들어있는 것이다. 3차원 이상 포인터도 똑같다. 함수 포인터도 마찬가지다. 다만 마지막 주소가 함수의 시작 주소를 가리킨다의 차이. 그래서 모든 포인터의 크기는 같은 것이다. char든 int든 구조체든 뭐든.

포인터의 진짜 어려움은 스택 공간과 힙 공간의 차이를 이해하느냐 마느냐에서 결정된다. malloc함수는 어째서 반복 수행할 때마다 서로 다른 포인터를 리턴하는가? 그리고 함수 안에서 선언된 변수는 배열 포함해서 신경 안 써도 되는데 어째서 malloc계열 함수는 꼭 free를 해 줘야 하는가? 이에 대한 답을 찾아야 포인터를 이해할 수 있게 되는 것. 그리고 포인터 변수가 단지 숫자값이라는 점을 이해하고 있어야 포인터의 포인터(2차원 포인터)나 함수 포인터의 개념을 이해할 수 있다. 그리고 크기가 10만을 넘어가는 배열을 선언하면 안된다고 하면서 malloc으로는 기가바이트 단위의 공간을 할당해도 잘 돌아가는가? 그리고 그렇게 할당한 공간에 배열처럼 접근해서 숫자 천만이 넘어가는 인덱스를 넣어도 왜 멀쩡한가에 대해서도 이해할 수 있다.

위에서 설명한 것들을 이해하려고 네이버 검색하고 있어봤자 나올 턱이 없으므로 위키에 직접 설명한다. 털털하군
C언어에서 malloc 함수, C++언어에서 new 연산자는 OS의 서비스를 사용한다. 즉, 저들 함수는 프로그램 내부를 떠나 외부 세계인 OS와 통신한다.[3][4] 스택 공간이라고 함은 함수나 메소드 안에서 사용할 수 있는 격리된 공간인데 이 공간은 20kb나 64kb정도로 작으며 컴파일러 옵션으로만 조정이 가능하다. 그러니까 스택 공간은 따로 얘기 안해도 주는 기본 용량이라 할 수 있다. 일반적인 함수는 이 기본 용량 가지고 충분하지만 데이터를 다루려고 하면 택도 없는 용량이 된다. 그래서 데이터용 공간은 메모리에 별도로 할당해달라고 OS에 요청할 필요가 있는데 이 때 OS는 힙 공간에서 그 별도의 메모리를 예약해주고 그 시작 번지를 포인터 주소로 리턴한다. 그러면 프로그램은 그 포인터 주소로 가서 할당된 공간만큼 자기 맘대로 사용하는 것이다. 그리고 사용이 끝난 공간은 OS에게 반납하겠다고 선언한다. 가비지 콜렉션은 이 반납 절차를 생략해도 프로그램이 알아서 반납 처리를 진행하는 것을 말하고(OS가 아니다!), 프로그램이 종료하면 OS가 그 프로그램이 임대한 힙 공간 전체를 자동 반납하게 설계돼있다. 그래서 프로그램 강제 종료 이벤트 이후에는 프로그램이 아무리 막장으로 짜여 있더라도 메모리를 영구적으로 갉아먹히지 않는 것이다.[5] 디바이스 드라이버 등 커널 공간에서 수행되는 프로그램은 커널 자체가 종료해야 반납이 이루어지는데 커널의 종료는 곧 시스템 셧다운을 의미하는지라 디바이스 드라이버에서 메모리 누수가 일어나면 치명적이다.[6]

스택은 말 그대로 접시쌓듯이 데이터를 포개 쌓아서 보관하므로 스택이고, 힙 공간은 어원은 알 수 없으나 그냥 아무렇게나 할당할 수 있는 자유 공간이다. 자유에는 책임이 따르는 법. 스택은 함수가 리턴할 때 pop을 적당히 해 주면 메모리가 알아서 정리되지만 힙 공간은 그런 관리 매커니즘이 없으므로(가비지 콜렉션이 있다면 얘기가 다르다) 할당을 요청한 쪽에서 책임지고 해제해줘야 하는 것이다.

OS는 힙 공간 내부에서 데이터가 어떻게 다루어지는지 모르기 때문에 할당해 준 힙공간을 해제해도 되는지 판단할 수 있는 근거는 둘 뿐이다.

  1. 프로그램 스스로가 다 썼다면서 반납한다(명시적 메모리 해제, 가비지 콜렉터 작동)
2. 프로그램이 종료되었다.

하지만 스택 공간을 해제해도 되는지 판단할 근거는 아주 직관적이다.

  1. 함수가 return문을 실행했다.
2. 함수가 끝났다.
3. 프로그램이 종료되었다.

물론 세마포어 같은 걸로 들어가면 더 복잡해지지만 일단 공유메모리는 빼고 얘기하자면 저렇다.

그러면, OS를 사용하지 않는 펌웨어등은 어떻게 메모리를 관리할까? 정답은 예상한 그대로. 기기의 전체 메모리를 자기가 직접 관리해야 한다. 스택이고 힙이고 없다. 자기 자신이 OS니까 자기가 메모리 관리까지 책임져야 한다. malloc같은 것은 없고 그냥 포인터 주소를 기준으로 한정된 메모리 공간을 적절히 나눠서 알아서 관리해야 한다. 이 작업이 너무너무 빡세기 때문에 사람들이 OS를 사용하는 것이다.[7]뭔소린지 1도 모르겠다

포인터의 대한 설명은 불경에서도 찾을 수 있다[8]
포기하면 편하다


바로 위에서 참 쉽죠식으로 설명해놨지만 포인터 참조 오류는 멀티스레드 동기화 이슈에 버금갈 정도로 굉장한 골칫거리이다. 사실상 프로그램이 오류를 일으키는 주범이자 압도적인 비율을 차지한다. C 언어 오류의 90% 는 포인터에서 나온다는 말이 괜히 나오는게 아니다. 구조적으로 포인터는 기계 제어에는 꼭 필요한 요소이기에, 이쪽 분야에서는 자유롭게 포인터를 사용할 수 있는 C가 절대적인 지위를 차지하고 있다.

반대로, 기계 제어가 아닌분야에서는 굳이 포인터에 목 매일 필요가 없다. 포인터로 온갖 문제를 야기하느니, 포인터를 아예 없애는 게 더 효율이 높다고 판단하기 때문이다. 그래서 최신 언어인 경우 포인터가 없다. 정확히는 내부적으로는 포인터를 사용하겠지만, 외부적으로는 프로그래머가 직접 포인터를 다룰 필요가 없도록 숨겨 놓은 것이다. 또한, 레퍼런스라는 대안이 있기 때문에, 포인터 비슷한 동작을 흉내내는 것은 가능은 하다.
  1. C에서의 연결 리스트, 동적 메모리 할당 등이 그러한 예이다.
  2. 특히 자신의 전공이 임베디드 개발쪽이라면 진짜로 토나올 정도로 깊게 파고들어야되는 관계로 나중가서는 거의 코드가 메모리 주소로 뒤덮혀서 어셈블리어 수준으로 변해가는 모습을 보고 기겁하게 될지도 모른다.
  3. 그래서 이들 함수는 어셈블리어 이외의 방법으로는 작성이 불가능하였지만 최근의 라이브러리에서는 syscall 래퍼를 제공해주기에 C로도 작성할 수 있다.
  4. 정확히 이야기하면 malloc이나 new는 운영체제로부터 얻어온 메모리 페이지를 쪼개서 요청한만큼만의 메모리 블럭을 리턴해주고 나머지 메모리는 차후에 사용할 수 있도록 보관해두는 일을 한다. 보관해둔 메모리 페이지보다 할당 요청 크기가 클 때에만 (유닉스/리눅스를 예로 들자면) sbrk, 혹은 mmap 시스템 콜을 이용하여 메모리 페이지를 얻어온다. 따라서 malloc을 호출했을 때나 new로 객체를 생성할 때 항상 OS의 기능을 활용하는 것은 아니다.
  5. 프로그램 종료 후에도 메모리 누수를 일으키게 의도적으로 만들어진 프로그램은 있을 수 있다.
  6. 이 때문에 디바이스 드라이버를 제작할 때 하드웨어와 직접적으로 통신할 때에만 커널의 도움을 받고 나머지 기능은 사용자 공간에서 작동하도록 만들 수도 있다.
  7. 오해가 있을 수 있는데 펌웨어의 상당수가 OS(임베디드 리눅스 등)를 사용한다. OS가 못 올라갈 정도로 메모리가 극단적으로 작은 데서나 직접 메모리를 제어한다. 임베디드 프로그래밍
  8. 능엄겸에서 석가모니아난다에게 한 말.