왜 C언어 포인터는 이해하기 어려울까?

왜 C언어 포인터는 이해하기 어려울까?

교수님! 진도가 너무 빠릅니다 😭 
C언어 입문의 최종보스, 들어는 봤나 포인터!

C언어 배워보신 분 손! 
프로그래밍에 관심이 있다면 한 번쯤은 ‘C언어에서 포인터가 그렇게 어렵다더라’ 하는 얘기를 듣곤 하는데요. 

특히 학교에서 C 강의를 듣는 사람들 사이엔 포인터가 일명 학점 브레이커, 재수강의 늪(!)으로 여겨지기도 하죠.

그럼 왜 그렇게 많은 사람들이 포인터를 이해하기 어렵다고 하는 걸까요?
C언어의 꽃 포인터가 도대체 무엇인지부터, C언어로 처음 프로그래밍에 입문했다는 인프런 팀원 3人의 “라떼는 말이야~” 추억 토크까지 생생한 이야기를 전해드릴게요!


가장 먼저, 포인터가 뭘까요?

포인터(Pointer)데이터가 저장된 메모리의 주소값을 저장하는 변수이며, 포인터 변수라고도 부릅니다.

👉 메모리의 주소, 즉 ‘어디’인지(=위치 정보)를 저장하는 전용 변수! 

  • 포인터를 통해 프로그램의 변수에 접근하여 읽고 쓰거나, 함수를 실행할 수 있어요.
  • 자료형을 선언할 때 해당 변수명의 앞에 별표*를 붙이면 앞 자료형을 가리키는 포인터 변수가 됩니다.
  • 이렇게 선언된 포인터 변수에서 주소를 얻고 싶은 변수의 앞에 앰퍼샌드&를 붙여주면 그 변수의 주소값이 저장됩니다.
  • 즉 포인터는 프로그래머가 컴퓨터 메모리에 직접 접근해서 제어할 수 있게 하는 도구예요!

그런데 이렇게만 이야기하면 뭔가 머릿속에 잘 그려지질 않죠. 🤔
조금 쉬운 비유를 들어볼게요. 여러분이 책상 앞에 앉아 있는데, 그 위에 상자가 두 개 놓여있다고 생각해볼까요?

  • 책상에 놓인 두 상자에는 각각 AB라는 이름이 붙어 있고, 서로 다른 내용을 담고 있어요.
  • 여기서 어떤 상자를 가리키고 있는 화살표가 여러분 손에 쥐어져 있어요.
  • 화살표로 A 상자를 가리키면, 여러분은 A 상자의 내용을 확인할 수 있어요.
  • A 상자를 가리키던 화살표가 B 상자를 가리키면, 반대로 B 상자의 내용을 확인할 수 있겠죠. (화살표 값 바꾸기)
  • 심지어 이 화살표는 똑똑해요. A 상자를 가리키는 화살표를 따라가서 A 상자의 내용을 변경할 수도 있어요. (상자 내용 바꾸기)

이제 책상을 메모리, 상자를 데이터, 화살표를 포인터로 각각 바꿔서 생각해보세요. 
C언어의 포인터는 말 그대로 가리키는(Point) 도구, 즉 메모리 상의 데이터를 가리키는 화살표라고요. 

“변수는 메모리를 사용하는 문법이라고 말씀드렸는데요. 그럼 포인터 변수(포인터)는 기존에 생각하는 변수랑은 뭐가 다를까요? 이게 메모리의 주소를 저장하기 위한 전용 변수예요.

엑셀로 예를 들어 생각해볼까요? 셀 한 칸을 1바이트라고 한다면, 여기 B에 2번 셀이 있죠? 이 셀에 만약에 대문자 A라는 값이 저장돼 있다고 생각해봅시다. 이 A를 저장하겠다고 하면 캐릭터 형식 변수에다 저장된 정보겠지만, 이 A가 저장된 메모리의 위치 즉 B2라는 정보도 있겠죠? 이 B2라는 위치 정보를 저장할 거예요, 하는 게 바로 포인터입니다.”

널널한 개발자 "독하게 시작하는 C 프로그래밍" 강의 中


왜 포인터가 어렵다고 하나요?

포인터가 C언어에서 메모리와 데이터를 다루는 중요한 개념 중 하나인 이유는, 포인터를 사용하면 특정 데이터를 직접 조작하거나 다른 데이터로 전환할 수 있기 때문이에요. 

게임 치트 엔진 써보신 분? 치트 엔진은 가동 중인 게임의 메모리에 직접 접근해서 16진수 형태로 이루어진 게임 내 변수값을 조작할 수 있는 도구예요.

꽤 많은 게임에서 프로그램 안에 있는 변수, 즉 메모리 주소는 프로그램을 껐다 켤 때마다 계속 바뀌게 되어 있는데요. (동적 할당) 그럼 게임을 재부팅할 때마다 어떤 값을 바꾸기 위한 메모리 주소를 매번 새로 찾아야 하니 번거롭겠죠.

하지만 포인터를 찾는다면, 포인터가 그 메모리의 위치를 가리키기 때문에 매번 바뀌는 메모리 주소값을 다시 구하지 않아도 값을 수정할 수 있어요! 

C언어는 기계어나 어셈블리어처럼 컴퓨터가 바로 읽을 수 있는 언어(저수준 언어)가 아닌, 사람이 이해하기 쉬운 문법을 가진 ‘고급(고수준) 언어’에 속하는 프로그래밍 언어인데요. 그런데 고급 언어인데도 포인터를 통해 ‘메모리에 직접 접근하고 제어하는’ 저수준 언어의 특성을 가진다는 게 C언어의 독특한 점이에요.

오늘날 흔히 사용하는 자바(Java)나 C# 같은 많은 프로그래밍 언어가 C 기반으로 이루어져 있는데요, 그런 언어들은 이미 내부적으로 C언어가 포인터를 이용해 메모리를 알아서 제어할 수 있게 되어 있어요. (매니지드(Managed) 언어: 프로그래머가 별도로 조작하지 않아도 언어 자체적으로 메모리를 관리해주는 언어) 

다시 말해 현대 프로그래밍 언어는 프로그래머가 직접적으로 포인터를 건드릴 필요가 없는 방향으로 발전했기 때문에, 마치 포인터를 안 써도 되는 것처럼 내부적으로 도와주고 있다는 뜻이에요. 프로그래머가 메모리를 직접적으로 다루지 않으면 프로그램 운용 안정성이 크게 증가하니까 편리하지만, 성능 저하나 엄청난 메모리 부하를 감당하게 될 수도 있어요. 

그래서 포인터를 이해한다는 건, 내가 짜는 프로그램이 컴퓨터 메모리와 직접적으로 상호작용하는 방식을 이해한다는 뜻이기도 해요. 하지만 그만큼 복잡하고 추상적인 문제이기 때문에 컴퓨터 구조에 대한 이해가 부족하다면 난해하게 느껴질 수밖에 없죠.

메모리에 직접 접근하는 위험성

포인터를 사용해 컴퓨터 메모리의 특정 위치에 직접 접근할 때, 포인터를 잘못 사용하면 오류가 발생하기 쉬워지거나 시스템에 치명적인 버그를 일으킬 수 있어요.

까다로운 메모리 관리

포인터를 쓰면 메모리를 직접 할당, 해제하는 책임이 프로그래머에게 주어져요. 메모리 누수 같은 복잡한 메모리 관리 문제 역시 프로그래머의 손으로 관리해야 한다는 뜻이죠.

복잡한 문법, 어려운 디버깅

포인터 조작에 필요한 문법을 이해하는 데는 많은 시간과 노력이 들어요. 포인터를 잘못 써서 프로그램이 예상대로 동작하지 않을 때, 오류를 찾아내고 수정하는 디버깅 능력도 필요하고요.


포인터를 잘 이해하면 뭐가 좋은가요?

우선 C언어를 사용해야 하는 사람이라면 당연히 포인터를 잘 알아야 해요. C언어가 쓰이는 분야 중에서도 메모리를 직접 제어하는 코드를 짤 일이 많은 임베디드 소프트웨어 개발이나 게임/그래픽 엔진 개발 등을 하려면 특히 그렇고요.

그럼 포인터를 직접 제어하지 않는 고급 언어를 배우는 개발자가 포인터 개념을 이해할 필요는 없을까요? 물론 모든 프로그래머가 포인터를 깊게 이해할 필요는 없습니다. 일상적인 작업에서 포인터를 사용하지 않을 가능성이 높으니까요. 

다만 메모리 관리가 요구되는 업무를 하게 된다면 포인터를 잘 알아야겠죠. 더욱이 넓게 보았을 때 포인터 개념은 컴퓨터 과학(Computer Science) 및 프로그래밍에 대한 이해를 깊게 하는 데 유용한 역할을 한다고 볼 수 있어요.

언어간 상호 운용성

포인터를 쓰지 않는 고급 언어로 개발하는 경우에도 C, C++ 같은 언어와 상호 운용해야 할 때가 생길 수 있고, 다양한 언어 간의 전환이나 운용성을 이해하는 데 도움이 돼요

알고리즘 및 자료구조 이해

일부 알고리즘 및 자료구조는 포인터를 사용하는 만큼, 포인터를 이해하면 더 효율적으로 프로그램을 작성할 수 있어요.

보안 및 취약점 이해

포인터와 관련된 보안 문제 및 취약점에 대한 이해는 소프트웨어 보안을 강화하는 데 도움이 돼요.

메모리 관리에 대한 이해

C언어 포인터를 이해하면 컴퓨터의 동작 방식에 대해 더 깊은 이해를 얻을 수 있어요.

디버깅 능력 강화

C언어에서 포인터를 다루는 데 익숙해지면 메모리 관련 오류나 메모리 누수 등의 문제를 식별하는 데 도움이 돼요.

더 넓은 취업 기회

시스템 프로그래밍, 임베디드 시스템 개발, 하드웨어 드라이버 등 시스템 레벨의 프로그래밍 분야에 관심이 있다면 필수적인 기술이죠.

C언어는 현대 프로그래밍 언어 중에서도 단연 오래 살아남은, 여전히 현역인 언어 중 하나로 꼽히죠. 높은 성능을 요구하는 프로그래밍 분야에서는 매니지드 언어가 (C언어 같은) 언매니지드 언어에 특화된 메모리 관리 등의 영역을 완전히 대체할 수 없기 때문입니다. 

즉 모든 개발자가 동일한 수준으로 깊게 익힐 필요는 없겠지만, 알고 있을 때의 장점이 분명히 있는데다 개념과 원리를 잘 파악한다면 이해할 수 있는 영역이니만큼 소문난 악명(?)에 너무 두려워하거나 척을 질 필요는 없을 것 같아요.


때는 바야흐로 n년 전... 
인프런 팀원들이 돌아본 포인터와의 첫 만남

인프런에서 직접 들어본 비하인드 스토리! 😆
첫 프로그래밍 언어로 C를 배웠다는 인프런 팀 세 분의 소소한 경험담을 전해드립니다.

4학년 때 C언어 재수강한 썰, 그래서 지금은 
예박 (프로덕트 매니저) 💁‍♀️

대학에서 컴퓨터공학을 전공했는데, 4학년 때 C언어 강의를 재수강했습니다. (ㅋㅋㅋ)
처음 1학년 때 C언어를 들었는데, C언어 자체에 제약이나 까다로운 문법이 많아서 전반적으로 어려웠기도 하고 특히 포인터는 거의 노이해였던 것 같아요. 동기 중에서 잘하는 애들은 잘했지만… 해킹 동아리 같은 데 들어가서 같이 복습도 해보는 경우도 주변에서 있었던 것 같고요. (저는 동아리도 포기를…)

제가 학교를 다닐 당시엔 1, 2학년 수업에서 C언어를 많이 썼거든요. 특히 알고리즘 · 자료구조 강의가 C언어로 진행됐는데, 대부분 배열과 포인터로 이루어져 있어서 기본 수업에서 포인터를 잘 이해하지 못하면 이어지는 다른 수업들을 이해하는 데 지장이 많았던 기억이 나요.

시간이 지나 C언어를 재수강하면서는 1학년들 사이에서 학점 갈이에는 성공했습니다. ㅋㅋㅋ 아마 커리큘럼 뒷부분이 포인터랑 함수 포인터였던 것 같은데 학기 일정상 다행히 너무 깊게 하지는 않았던 것 같아요. 

‘포인터 이해를 잘 하면 실무에 도움이 된다/되지 않는다’ 하는 논쟁에 대해서는, 업계에 따라 필요한 게 달라서 그런 것 같아요. C언어가 비교적 저수준 언어에 가까운 언어다보니 메모리 주소에 접근하는 포인터 개념이 하드웨어에 밀접한 개발 분야에 많이 쓰인다고 알고 있고요.

자바 같은 현대 매니지드 프로그래밍 언어에서, 오히려 메모리 접근을 하면 안 되는 경우에는 일부러 안전하도록 포인터를 프로그래머가 제어하는 단계에서 지원하지 않기도 하는 것 같아요. 결국 제 생각은 필요할 때 배우면 된다 (ㅋㅋㅋ) 입니다.

프로그래밍 언어가 동작하는 방식에 대한 시야를 넓혀줬어요 
준프 (프론트엔드 개발자) 🙋‍♂️

제 경우엔 첫 프로그래밍 언어 공부였어요. 대학에서 C언어 과목을 수강했는데, 그 즈음엔 대다수 학교에서 C언어가 1학년 필수 과목이었어요. 몇몇 학교에선 파이썬으로 바꾸기도 했지만요.

강의를 들을 때 실제로 ‘포인터가 벽이다, 포인터를 잘 이해했다면 앞으로 잘 해낼 거다’ 하는 말을 들은 경험도 있습니다. 그만큼 포인터를 이해하는 데 많은 사람들이 어려워했던 것 같아요. 실제로 포인터를 다루다 그만 두는 사람이 있기도 하고요.

저는 포인터 개념 자체를 이해하는 건 크게 어렵진 않았어요. 오히려 프로그래밍 언어가 어떻게 동작하는지 이해하는 데 도움이 많이 됐던 것 같고요. 하지만 이해하는 거랑 실제로 사용하는 건 다른 거 같아요. 사용할 때 내가 포인터 변수를 다루고 있는지, 포인터가 가리키는 곳의 값을 다루고 있는지, 포인터의 포인터인지… 등등 따져야 할 게 많거든요. 

실무에서 포인터에 대한 이해가 도움이 된 편입니다. 자바스크립트에서 참조 형태의 값들, 예를 들어 객체나 배열 등을 이해할 때 포인터를 알고 있으면 따로 공부할 내용이 많지 않은 거 같아요. 관련 문서를 읽을 때에도 끄덕끄덕하면서 넘어갈 수 있었던 것 같고요. 『자바스크립트 딥다이브』 처럼 자바스크립트 동작에 대해 세부적으로 다루는 책들이 있는데요, 그런 책에서 메모리 구조를 참고하여 설명하는 부분들이 있는데 이런 걸 이해하는 데 포인터가 도움이 됐던 것도 있어요.

결국 원리를 이해하는 게 중요하다고 생각해요
조슈아 (데브옵스 엔지니어) 👨‍🏫

1학년 2학기 때 C언어 수업을 들었어요. 당시엔 C언어가 저희 학과에서 입문 과정이었어요. 어떻게 말해야 할지 모르겠지만 선배들이 겁 주는 게 있잖아요. “어렵다, 지금은 쉽지?” 이러면서 나중에 포인터 들어가면 어렵다고. 그땐 다들 1학년이고, 특히 저처럼 프로그래밍 언어를 처음 접하는 새내기들은 포인터가 뭐길래 저렇게 덜덜 떠나 싶었죠.

그러다 어느 날 교수님이 오늘부터 포인터를 할 거다, 하시는데 오래돼서 기억은 잘 나진 않지만 좀 난해했던 것 같아요. 사실 포인터의 기본 개념은 되게 명확하거든요. 주소값도 변수로 다루겠다는 건데, 참조와 역참조 개념이 들어가면서 애스터리스크*랑 앰퍼샌드& 연산자가 사람들을 힘들게 했죠. 개념 자체가 추상적이기도 하고, 어떤 연산자가 앞에 붙을 때 변수의 주소값이 나오는지 실제 값이 나오는지도 많이 헷갈리고요.

특히 그 시점에는 정적 타입 체크에 대한 컴파일러들의 지원이 더 약했거든요. IDE(통합 개발 환경)인 비주얼 스튜디오(Visual Studio)가 대세였던 시절이었는데, 정적으로 잘못 참조한다거나 혼동이 일어나는 걸 막기 위해 변수명에 일일이 명시적으로 표기(*ptrData *pValue 등)를 해줘야 하는 것도 쉽지 않았고요. 처음 프로그래밍 언어를 접하는 사람 입장에서는 그 부분이 특히 어려웠겠다는 생각이 들어요. 

2학년 때 자료구조 수업이 C언어 기반이었는데, 자료구조라는 게 애당초 포인터로 서로 연결함으로써 최대한 메모리를 효율적으로 쓰는 구조를 가리키다보니 포인터를 잘 모르면 이해하기 어렵잖아요. 물론 일찍부터 스스로 프로그래밍을 해봤던 친구들은 그다지 힘들진 않았겠지만, 처음으로 접했을 때는 상황이 다르잖아요.

당시에 영어 원서로만 수업을 진행했었는데, 아무래도 어려우니까 한국어로 된 C언어 책들이 인기를 끌었단 말이에요. 그런데 영어로 알고 있던 개념을 한국어로 보기가 쉽지 않을뿐더러, 일본 서적을 중역하거나 오역 때문에 번역 질이 좋지 않은 경우도 많다보니 힘들어하는 친구들도 많았어요.

포인터가 어렵다고들 하는 건 개념 자체의 어려움도 있겠지만 두 가지 정도로 이유를 꼽아볼 수 있을 것 같아요. 보통 학교 과정에서라면 시험에서 일부러 꼬아 내는 문제들이 있는데, 사실 그런 건 실무에서는 거의 안 쓰거든요. 포인터의 특성을 얼마나 잘 이해하는지에 따라 풀이에 대한 실마리가 보이는데 제대로 이해가 안 되어 있다면 헷갈리죠. 하지만 잘 이해하고 있다면 시간이 걸리더라도 조금씩 맞춰갈 수 있고요. 

지금도 포인터 관련 코드를 볼 때 바로 이해가 되는 건 아니에요. C언어를 다뤘던 게 오래 전 일이기도 하고, 지금 업무에 주로 쓰는 Go(Golang)에도 포인터가 있긴 한데 훨씬 선진화되어 있어서 이해하거나 사용하기에 좀 더 나아요. Go를 쓰다 보니 포인터에 대한 이해가 실무에도 영향을 여러모로 미치고 있고요.

두 번째로 코드를 직접 작성하는 입장에서는, 포인터를 사용할 때 포인터의 사용성과 목적이 명확하거든요. 커널 프로그래밍을 하는 게 아니고서는 주로 간단한 프로그래밍 목적으로 구조체를 쓰고 넘길 때만 포인터를 주로 쓰니까, 그렇게 목적이 분명할 때는 사용성이 확실해지고 수월하게 짤 수 있게 됐던 것 같아요. 포인터의 특성을 최대한 활용해서 쓰는 분야는, C언어의 특징이기도 하죠? 리눅스 커널이나 임베디드 시스템처럼 하드웨어와 밀접한 분야에서 많이 쓴다고 알고 있어요.

그럼 사실 그런 분야가 아니고서는, 난해한 문제 같은 게 실질적으로 실무 능력으로 이어진다기보다는 원리를 제대로 이해하는 게 중요하다고 생각해요. 더러 겁을 먹는 이유는 개념이 생소하기 때문이고, *&이 많아지면서 코드를 직관적으로 이해하기 어려워지기 때문이죠. 물론 메모리 제어 같은 데까지 신경을 써야 하거나 포인터 제어를 프로그래머가 하는 언어를 쓴다면 제대로 이해하고 넘어가야겠죠.

막간 보너스, 조슈아가 알려주는 포인터 1분 해설 💡
  • 실 포인터 자체는 개념만 잡고 가면 이해는 어렵지 않다. “포인터는 변수의 주소를 저장하는 또 다른 변수이다!”
  • 진짜 문제는 코드에서 참조*와 역참조&를 사용할 때 발생한다. 너무 헷갈리고 이해가 어렵다. 직관적이지 않다. (나 죽어...)
  • 포인터 주소의 연산이 가능한 점도 난이도를 증가시킨다.
  • 그러나 포인터가 중요한 이유! (주관적 의견)
    • 대용량 구조체를 가볍게 함수간 전달할 수 있다.
    • 구조체가 엄청 커지면 이를 옮기는 데도 많은 메모리 자원이 들어간다. 
    • 포인터를 사용하면 구조체의 시작 주소만 가지고 모든 연산을 수행할 수 있다.

C언어, 포인터에 관한 더 많은 이야기
인프런에서 더 자세히 만나보세요!

이 글과 관련 있는 추천 C언어 강의 👇 

이 글을 읽고 있는 분이라면, 쿠폰함에 코드 c언어포인터를 입력해보세요.

댓글 4

댓글을 작성해보세요.

  • modolb
    modolb

    C언어는 70년대초 컴퓨터 하드웨어 개발자가 OS를 프로그래밍하려고 만든 언어입니다. 따라서 하드웨어와 밀접히 연관된 언어입니다. 포인터는 기억장소(RAM)의 주소를 나타내기 위한 개념이며, 주소의 대수적 연산이 가능하도록 한 자료구조입니다. 쓰다보니 컴퓨터 전공과목의 용어가 나올수 밖에 없는데요, 그래서 전공과목에 대한 이해가 없으면 자연히 이해하기가 어렵습니다. 자세히 이해하고 싶으신 분은 컴퓨터 기본과목을 공부할 것을 추천드립니다.

  • 로켓라쿤
    로켓라쿤

    완전 공감합니다!

    포인터를 이해한다는 것은

    기본적인 컴퓨터의 메모리 구조와 운영체제를 이해하는 것과도 연관이 있어서,

    컴퓨터의 기본 같아요~~

  • 서은정
    서은정

    공감하는 과거 기억 소환중입니다~ ㅎㅎ

    잘보고 갑니다~ :)

  • notyourkind
    notyourkind

    저같은 입문자에겐 재밌고 유익한 정보네요

    감사합니다😁