|
http://www.hackerschool.org/HS_Boards/zboard.php?AllArticle=true&no=1770 [복사]
아마도 이 스터디를 읽고 계시는 분들 대부분은 C를 처음 접하시는 분들일겁니다. 그리고 특히, 포인터를 사용하는 데 어려움을 겪는 분들도 계실거라고 믿습니다. 제가 이 스터디를 시작하게 된 계기는 사실 포인터때문이었다고 해도 과언은 아닐겁니다. 저 역시 여러분이 겪으시는 비슷한 어려움을 겪었고, 무엇보다, 입문서를 읽어도 읽어도 포인터라는게 뭔지 도저히 감이 잡히지 않았던 경험도 있었기에, 그런 악순환(?)을 끊고 싶은 마음이 무엇보다 우선했었습니다. 이런 까닭에... 질문 게시판에 올라오는 글 들 중 유독 포인터와 관련 된 질문은 그냥 지나치기 어려웠지요. 하지만... 답변을 이어가면 이어갈 수록 느끼는 건... 항상 비슷한 질문들이 올라온 다는 사실입니다. 제가 볼 때, 이 말은 둘 중의 하나라고 생각됩니다. 가르치는 사람(혹은 책)이 어설프거나 혹은 문법 자체가 잘못 됐다는 것. 이 둘은... 제가 여전히 초보라는 사실을 떠나서도 '나'라는 개인이 어찌 할 문제는 아니라고 생각 됩니다만... 적어도 제가 이해한 만큼이라도 알고 넘어가셨으면 하는 바램에 이 보충 글을 씁니다.
제가 포인터에 관한 질문에서 높은 빈도로 접하게 되는 문제의 원인은 주로 선언식에서의 * 에대한 오해였습니다.
일반적인 입문서들은 물론이고, 내로라하는 C 관련서적이 공통적으로 사용하는 포인터 변수 선언 및 포인터 변수의 사용 예는 다음과 같습니다.
char a;
char *ch;
ch = &a;
*ch = 'z';
먼저 일반 변수를 하나 선언하고, 포인터 변수를 하나 선언합니다. 그리곤 포인터 변수에 일반 변수의 주소를 대입하지요. 그리곤 *연산자를 써서 그 값을 읽어오거나 변경합니다. 여기까지는 사실상 크게 문제 될 것이 없어보입니다만.... 사실은 초보자들의 입장에서는 매우 당황스러운 표현들이 등장합니다. 아래 코드를 보시죠.
int number, result;
int *pNum;
int *res = &result;
number = 10;
pNum = &number;
*res = *pNum -5;
포인터를 배우신 분들이라면 지금 이 상황이 어떤 상황인지 이해하는 게 전혀 어렵지 않을겁니다. 하지만... 이제껏 일반 변수'만' 선언해보고, 사용해보신 초보분들이시라면 여기서부터 헷갈리기 시작하실겁니다. 예를 들어보면,
int a=10, b=5;
int j;
int result;
j=8;
result = a - b + j;
라는 코드가 있을 때, 선언식인 첫 번째, 두 번째, 세 번째 줄에서 int를 생략해 보죠.
int a=10, b=5;
int j;
int result;
j=8;
result = a - b + j;
선언식이 아니라는 점만 제외하고는 사실 똑같은 코드라고도 할 수 있습니다. 변수의 사용에도 사실 별 문제가 없지요. 초보 분들은 다 이런 시각에서 코드를 배워왔고, 익숙해져 있습니다. 자료형 키워드인 int나 char, double 같은 것들만 빼면 별 차이가 없는 것들이지요. 이제 다시 위에 있던 포인터 코드를 봅시다.
int number, result;
int *pNum;
int *res = &result;
number = 10;
pNum = &number;
*res = *pNum -5;
여기서 초보들의 시각에서 '자료형'만 빼 보도록 하죠.
int number, result; ------------ 1
int *pNum; ------------ 2
int *res = &result; ------------ 3
number = 10; ------------ 4
pNum = &number; ------------ 5
*res = *pNum -5; ------------ 6
여기서 3번과 5번, 6번 줄을 눈여겨 봐 주세요.
*res = &result; ------------ 1
pNum = &number; ------------ 2
*res = *pNum -5; ------------ 3
대충 느낌에 *res가 뭔가 포인터랑 관련이 있어보이고, pNum도 어쨌거나 포인터라는 건 맞는 거 같습니다. 그런데, 똑같이 *가 붙어 있는 res에 한 번은 주소값이(1), 다른 한 번은 변수값이 대입(3)되고 있습니다. 이번엔 1 번과 2번을 비교 해 보세요. res도 pNum도 포인터 변수라는 것 까지는 알겠는데... 하나는 *가 붙은 상태(1)에서 &가 붙은 변수의 값을, 다른 하나는 *도 없는 상태(2)에서 &가 붙은 변수의 값을 각각 입력 받습니다. 초보자들은 여기서 포인터의 일관성을 잃어버리게 되는겁니다. 즉, 자료형과 * 기호를 떼어놓은 것으로도 모자라 아예 *를 변수명에 붙여 놓은 선언식을 씀으로써 초보분들 머릿속에는 [*변수명]이라는 희안한 변수가 창조되어 버린 셈입니다. 일반 식에서 [*변수명]의 형태로 변수 값 접근을 하는 걸 생각해 보면 일리는 있어보입니다만, 이 역시 근본적인 해결은 해 주지 못합니다. 위의 1번식과 3번 식 간의 괴리 말이지요.
이를 해결하기 위한 방법으로 int* pNum 과 같이 자료형에 *를 붙이고 변수명만 떼어 놓는 선언식을 쓸 수 있습니다. 제가 처음 포인터를 제대로 이해할 수 있었던 방법이기도 했고, 여전히 선호해서 사용하는 방법입니다. 다만, 이 역시 골치아픈 문제점을 가지고 있습니다. 여러개의 변수를 선언 할 때 생기는 문제점이지요. 아래 두 선언식들을 비교해 보세요.
int a, *b, *c, d; -------------- 1
int* i, j, k, l; -------------- 2
얼핏 보기에도 1번 줄은 정수형 변수 2개(a, d)와 정수형 포인터 변수 2개(b, c)를 선언하고 있다는 걸 알 수 있지요. 그런데 2번 줄을 보면 대충 봐서는 정수형 포인터 변수 4개를 선언하고 있는 것 같지만, 사실은 포인터 1개(i), 나머지 정수형 변수 3개(j, k, l)를 선언하고 있습니다. 즉, int*, char* 등 포인터 선언 형식이라는 것은 하나의 완전한 선언형식으로 자리잡지 못한 자료형이라는 겁니다. 때문에 두개 이상의 포인터 변수를 한 줄의 선언 형식에 넣으려면 어쩔 수 없이 1번과 같은 방식을 쓸수 밖에 없게 되는 거죠. 아니면 이런 식으로 써야 합니다.
int i, * j, * k, l;
이 형태는 원래 [자료형_*_변수명](_는 빈칸) 형태에서 온겁니다.
int * a;
int * b;
int c;
이런 식으로 말이죠. 제가 한국에서 공부하고 있었을 때까지만 해도, 이 선언식은 거의 쓰이지 않는 줄 알았는데, 미국에 와서 보니 이곳에서는 적지 않은 사람들이 쓰고 있더라구요. 심지어 제가 공부하고 있는 곳의 교수님도 이 방법을 쓰고 계셨습니다. 아무튼.. 포인터 변수를 선언하는 데 있어서 한 가지 방법만 존재하는 게 아니라 여러가지가 존재하고, 저마다의 특징이 존재합니다. 하지만, 제 생각에는 포인터를 사용하는 데 있어서 많은 초보분들이 헤메게 되는 원인이 저런 일관적이지 못한 표현때문이지 않나 싶습니다.
이와 관련해서 'C언어 포인터 완전제패'의 저자인 카즈야 마에바시는 아예 'C언어의 선언 구문은 부자연스럽고 기묘하고 변태적(?)인 구문'이라고 까지 표현하고 있지요.(저만 그렇게 생각하는 게 아니라는 걸 알게 되어 다행이었습니다 ㅎㅎㅎ)
아직 배울게 한참 많이 남아있는 저이기에 감히 이래라저래라 할 수 없다는 건 알지만... 적어도.. 같이 배우는 입장에서 경험적으로나마 앞선 사람(?)으로서 처음 포인터를 접하시는 분들에게 권하고 싶은 선언식 표현이 있다면, 위에서 제가 이야기 드린 바와 같이, 자료형과 *를 붙이고 변수명을 띄우는 표현을 쓰시라고 하고 싶습니다. 네.. 여러개의 포인터를 선언하는데는 여전히 문제가 있습니다. 하지만... 저는 이런 식으로 문제를 해결하고 있습니다.
void main()
{
int a, b, c; // 어쩌구저쩌구에 필요한 변수
int i, j, k, l; // 저쩌구와 어쩌구 변수
int* pNum; // 어쩌구저쩌구를 저장하기 위한 포인터
int* pCnt; // 저쩌구에 어쩌구를 하기 위한 포인터
int* pVar; // 이렇게 저렇게 요런 조런 포인터
: : : :
이런식으로 너무 복잡해지지 않는 선에서만 변수를 한 줄로 묶어서 선언하고, 포인터는 각각 따로 선언을 합니다. 변수 하나 선언하는데 줄 하나정도 더 는다고 해서 코드가 복잡해지는 것도 아니고, 무엇보다... 프로그램이 커지면 오히려 변수를 줄 단위로 나눠 선언 한 다음에 일일이 주석을 달아놓는 것이 훨씬 유리하더라구요. 제가 게시해 둔 몇몇 다른 프로그램들의 소스 역시 저 기준을 그대로 지키고 있습니다. 뭐... 한 마디로 하자면... 제 스타일입니다. 그렇지만.... 워낙 머리가 나쁜(?) 저조차 이해하기 쉽게끔 머리 싸매가면서 찾아낸 방법이니(어쩌면 이 방법을 저보다 먼저 사용하고 계셨던 분이 계실지도 모르지만 말이죠;;;) 초보분들에게도 도움이 되지 않을까 싶습니다.
[출처] [C기초] 05 보충: C의 기괴한(?) 포인터 선언 구문 (C언어를 배우자) |작성자 때릴꺼야
|
Hit : 7929 Date : 2011/05/25 09:53
|