처음 포인터를 배우는 c 학습자들이 문법에서 막히는 일은 거의 없습니다. 애초에 c는 가장 간단한 문법을 가진 언어이기도 하고, 사실은 여러가지 프로그래밍적 하드웨어적 함정들과 이와 연계된 포인터를 접하고 난뒤로 가장 골치가 아파 집니다. 특히, 배열과 포인터를 배우고 난뒤에 이둘의 차이를 정확하게 인지하지 못하고 c를 대부분 포기하게 되지요.

하지만 포인터의 정의를 생각해 봅시다. 포인터의 정의는? ‘변수로써의 기능 뿐만 아니라 메모리 주소(변수가 메모리상 어디 있는가)또한 받아주는 것 동시에 프로그래머가 직접 메모리에 접근하겠다고 선언하는 것’ 예시로써 보여드리겠습니다.
 
 


int a=32;
int *c=&a;
&연산자는 여기서는 a의 메모리를 따오는 연산자라고 보시면 됩니다. 즉, *c라는 포인터에 a의 메모리 주소를 넣겠다는게 되겠지요. 그래서 위의 프로그램에서 *c를 출력하면 a가 그대로 출력 되고 c를 출력하면 a의 주소가 출력되는 겁니다. 그리고 두번째 프로그램에서는 *c가 변수로써의 기능또한 그대로 남아 있기 때문에 그냥 1을 대입하면 값 또한 바뀌는걸 확인 할 수 있습니다.

 
c에서는 문자열(글자 하나하나 말고 여러 개가 동시에 있는 것)을 따로 다루는 클래스나 형식이 없기 때문에 문자하나 하나의 모임, 배열로 취급해서 다루는게 일반적입니다.(아니, 애초에 문자열을 주로다룰거면 c를 안하는게 차라리 나을수도… 저도 문자열은 별로 다루고 싶진 않습니다만 ㅋㅋ) 그러나 여기서는 문자열을 데리고 나온건 문자열을 어떻게 다뤄 보자는 것이 아니라 배열과 포인터의 차이가 여기서 가장 극명하게 갈리기 때문입니다.

char a[]="hi i'm 번개쥐";
    char *b="bye 번개쥐";
    printf("%sn",a);
    a[0]='b';
    a[1]='y';
    a[2]='e';
위의 코드를 보시면 a라는 배열안에 hi i’m 번개쥐 라는 문자열이 들어 있는 것을 알 수있습니다. 저번 시간의 기억을 떠올려 보지요. 문자열은 어디에 저장된다고 했을까요? 정답은 텍스트 세그먼트 입니다. 그런데 a는 어떤 변수이지요? Main 함수 안에 있으므로 당연히? 지역변수입니다. 그렇다면 당연히 a(hi i’m 번개쥐라는 문자열)은 동시에 스택에 저장되어 있습니다. 스택은 rom인가요 ram인가요? ram이죠? 그렇기에 a[0], a[1]등으로 문자열에 접근하여 수정하는 프로그램 코딩이 가능 한겁니다.

 
  코드를 봅시다. b라는 포인터에 bye 번개쥐 라는 문자열을 넣어놨군요. 이렇게 까지만 보면 포인터 배열과 별 다를게 없어보이지요? 그리고 마찬가지로 컴파일을 해보면

 
에러도 워닝도 없이 깔끔하게 컴파일 됩니다. 그렇다면 실행을 하면? 와장창! 프로그램이 깨져버리고 런타임이 걸리고… 왜 이럴까요? 똑같은 모양인거 같은데 왜? 다시한번 차근차근 생각해 봅시다. 일단 문자열 bye 번개쥐는 텍스트 세그먼트 Rom에 저장될 겁니다. 그리고 char *c는? 자 앞에서의 코드를 다시한번 봅시다.
int a=32;
int *c=&a;

int *c에는 뭐가 대입이 됬습니까? a의 주소가 대입이 되었지요? 즉 char *c는 지금 즉, bye 번개쥐 라는 문자열의 주소가 대입되어 있는 겁니다. 그리고 스택에 있는 c에는 롬에서 잘먹고 잘살고 있는 bye 번개쥐 의 주소만 대입이 되어 있을뿐이지요. 즉, 스택에는 rom에있는 문자열을 복사가 안되있는 것이죠.

 
다시 한번 우리는 스택(rom)에 존재하는게 뭔지 그리고 텍스트 세그먼트(rom)에 저장되는게 뭔지 한번더 명백하게 보고 가도록 합시다. *c를 출력하니 당연히 hi라는 문자열의 첫 번째 글자가 나오고 c를 출력 시키니 h가 저장되어있는 메모리의 주소가 어딘지를 보여 줍니다.

자, 여기서 우리는 이코드의 의미를 알아야 합니다.
int a=32;
라는 코드를 봤을 때는 아 그냥 메모리상의 어떤 임의의 스택에 a라는 자리를 잡았고 그자리에 32를 썼구나.
int *c=&a;
*c=16;
이라는 코드를 추가했을때는 c에 저장된 a의 주소를 가르켜서 거기다가 16을 써라 라는 의미가 되는겁니다. 근데 이게 사실은 그래서 어쩌라고? 왜? 왜이러는 건데? 라는 말을 듣기 딱좋은 말입니다. 왜냐하면 말로 하기 힘든 부분이 존재하거든요. ㅎ 예제를 하나 더 봅시다.
 
 

위의 두 프로그램을 봅시다. 얼핏 보면 똑같은 프로그램 같은데 결과는 젼혀 다릅니다. 일단 프로그램 소개를 하자면 swap 즉 a,b값을 바꿔주는 프로그램입니다. 하지만 위에있는 프로그램은 정상적으로 값을 바꿔주지만 밑에 있는 프로그램은 값을 바꿔주지 않습니다. 왜그럴까요?

자 여러분 함수 안에 있는 변수들은 어떤 변수지요? 바로 지역 변수입니다. 지역변수들은 전부 스택에서 사이좋게 살고 있습니다. 그들은 너무 사이가 좋아서 절대 다른 지역에 사는애들은 건들지 않아요. 즉, 다른 곳에 사는 지역변수들은 서로 절대 간섭을 할 수없습니다.

하지만 포인터는 애초에 접근 원리가 그냥 메모리에 직접 접근을 해서 즉, 지역변수고 나발이고 내 하고싶은데로 접근하는 거기 때문에 바로 수정이 가능한겁니다.

단순 변수와, 포인터의 차이 이제 확실히 아시겠습니까? 그리고 포인터와 배열의 차이 아시겠습니까?

부제 Do you trust Programmer?
C언어는 사실상 문법적 오류를 제외한 오류는 거의 경고조차 하지 않는경우가 많습니다. 위에서 봤듯이 rom에 접근해서 수정하려고 하는 미친짓을 하는 프로그래머의 코딩조차 컴파일러는 아무런 군말없이 컴파일 해주죠. 이게 C의 가장큰 강점이자 약점입니다. 최대한의 자유도를 보장해서 롬에서 힙까지 모든 메모리를 다 개방해 주는 대신에 이에 따른 위험(c로짜다가 버그가 난다? 98% 포인터 접근 잘못입니다.) 그래서 임베디드 같이 열악한 환경(메모리가 128kbyte라면 당신은 믿으시겠습니까?, 64kbyte라면?, 메가 아닙니다.) c를 쓰는 이유는 여기서 나오는 것이지요. 물론 몇가지 이유가 더 있긴 합니다.

휴... 저도 쓰면서 사실은 이런글 쓰면 욕먹을 거 같다라는 생각이 드는 글이네요. 최대한 쉽게 쓰려고 노력했으니 이쁘게 봐주시면 감사하겠습니다.