C언어 포인터 기초 분석

lfs의 이미지

C언어 포인터 기초 분석

머리말

C언어 포인터를 배우는데 많은 사람들이 공부를 못하고 있다. 왜그럴까? 하면 내가 보기에는 C언어 포인터를 가르쳐주는 사람들이 설명을 하지 못해서 배우기가 어려웠던 것 같다. 인터넷 상에서 포인터에 대한 것을 물어봐도 그냥 외워라 하는 정도이고 책을 봐도 그냥 수박 겉?기 식으로 설명해 준다. 서울대 황희융 편저의 C primer plus책도 봐도 핵심적인 것은 정말 빠져 있다.

그러면 저자는 어떻게 배웠을까? 대학교 중퇴지만 대학교 동아리 선배에게 물어봐도 모른다는 말을 들었고 네이버 까페에 인터넷 채팅을 통해서 배울려고 해도 그냥 어셈블리 공부하거나 외우라는 말만하고 만다. 또한 책에서도 핵심적인 것은 설명하지 못하자 근 몇 년간 포인터에 관계된 C 기초책을 한 10권이상의책을 구입해서 읽어보고 또 보고 또 보고 해도 알지 못했다.

하지만 하늘은 나를 버리지 않았다. 책은 서점에서 꼬박꼬박 사서 봤고 대학교 다닐 때 동아리방에 있는 책도 가지게 되어서 그책을 여러번 봤다. 그런데 나중에 아는 사실이지만 서점에 가서 책을 사면 환마신,복마신이 있어서 열심히 하는 사람에겐 영감을 띄워주어 알음귀를 틔워 준다는 사실을 알았다. 그러자 신기한 일이 벌어졌다. C primer plus책을 여러번 보는데 환마신,복마신이 환영을 보게 해주었던 것이다. 그 환영이 아니었으면 지금도 C기초 책을 보고있을런지도 모른다. 그 환영은 무엇이었을까?

환영은 바로 포인터 선언자와 간접 참조 연산자를 구별하라는 것이다. 이것을 알자 모든 C포인터가 순식간에 알게 되었다. 이 사실을 알자 3년간 C언어를 공부하지 못했다. 얼마나 기뻤던지 그 환희는 지금도 잊지 못한다.그후 3년이 지나자 내가 공부했던 것 확인 할려고 해도 확인을 못했고 지금 이글을 쓰면서 확인을 받고 싶다.

마지막으로 C언어 책을 쓸사람은 아주 자세히 포인터에 관계된 설명을 하였으면 한다. 아는체를 하지 말고 진심어리게 책을 썼으면 한다. 나의 이 문서가 한국의 소프트웨어 업계에 중요한 밑거름이 되었으면 한다.

2019년 10월 7일

김진섭

1.변수의 연구

보통 C언어를 프로그래밍 할려면 변수를 선언한다. 변수의 형을 선언하고 변수명을 입력하게 된다. 변수의 형에는 char,int,double,float와 typedef형의 형등이있다. 그가운데 보통 많이 쓰이는 int형을 예를 들터인즉 다른 형도 똑같이 생각했으면 한다.

int형과 변수를 선언하는 예제를 보자

예제:

int I;

여기서 int는 변수의 형을 선언하는것이고 I는 변수명을 뜻하게 된다. 이렇게 선언하면 변수명 I에게는 값을 저장할수 있는 변수 공간이 있게되고 또한 변수 공간이 있을 주소가 생기게 된다. 여기서 주소란 메모리 주소라고 보면 알수 있다. 다시 말하자면 변수를 선언하면 변수 공간과 변수 주소가 발생하게 되는 것이다.

int형과 변수를 정의하는 예제를 보자

예제:

int l = 10;

여기서 이문서를 보는 사람은 변수 선언자와 정의자를 잘 구분할줄 알아야 한다. 선언자는 값을 할당하지 않는것이고 정의자는 값을 할당한 것이다. 그렇다면 이 예제에서는 값을 할당하는 정의자라 할수 있다. 이점 잘 참고 하였으면 한다.

2.포인터 연구

변수를 공부하였으니 이제는 포인터를 이해해 보자. 포인터라 하니 사람의 간장이 덜덜 떨릴수 있지만 전혀 덜덜 떨지 않았으면 한다. 아주 아주 쉽게 설명 하겠다.

포인터 선언자 예제를 보자

예제:

int *M;

여기서 포인터를 선언하였다. 여기서 착오를 일으킬 사람은 포인터 선언자 *(별표)는 간접 참조 연산자가 아니라는 것이다.(간접 참조 연산자는 포인터 변수가 갖고 있는 주소에 있는 값을 참조하는 것이다.) 단지 이예제에서는 변수 M을 포인터 변수라고 선언하는 것뿐이다. 그냥 간단하게 int *M에서 별표는 포인터를 선언하는 별표라고 보면된다.

포인터 정의자 예제를 보자

예제:

int *M=10;

여기서 포인터 변수 M은 컴퓨터가 임의로 지정해주는 메모리 주소값을 가지게 되는 것이다. 그리고 그 주소값이 있는 곳에 정수 10을 저장해 주는 것이다.

실제적으로 간단하게 더하기 프로그래밍 예제를 보자.

예제:

#include <stdio.h>
 
int main()
{
    int I = 1;
    int M = 2;
    int SUM;
 
    int *K;        // 9
    int *F;        // 10
 
    K = &I;        // 12
    F = &M;        // 13
 
    SUM = *K + *F; // 15
 
    printf("SUM=%d", SUM);
 
    return 0;
}

12,13줄의 &는 번지 연산자이다. 번지 연산자는 변수의 주소를 추출해주는 연산자라 할수 있다. 보통 scanf()함수를 사용할 때 쓰여지며 포인터를 다룰때도 사용되어진다. 12,13줄은 변수 I,M의 변수 주소를 추출하여 포인터 변수 K,F에 저장하라는 뜻이다.

여기서 가장 헷갈린게 있다면 15줄의 별표이다. 15줄 별표는 간접참조 연산자이다. 15줄은 무슨 뜻이냐면 포인터 변수 K,F가 갖고 있는 주소값을 간접 참조하여 주소에있는 정수를 갖고와서 계산에 이용하라는 뜻이다.

또 강조 하지만 9,10줄의 별표와 15줄의 별표는 다른역할을 한다. 9,10줄의 별표는 포인터 변수를 선언하는 역할을 하고 15줄의 별표는 간접참조연산자인 것이다. 이것만 구별 할수 있으면 포인터의 절반을 알았다고 할수 있다.

3.포인터의 포인터 연구

이제는 포인터의 포인터를 알아보자. 간략하게 알자면 포인터의 포인터는 포인터 변수의 메모리 주소를 값으로 갖는 변수라고 보면된다. 이게 어렵다면 차차 설명해 놓은 것을 보면 알수 있다.

포인터의 포인터 예제를 보자.

예제:

#include <stdio.h>
 
int main()
{
    int I = 1;
    int M = 2;
    int SUM;
    int P_SUM;
 
    int *K;
    int *F;
 
    int **R;                           // 13
    int **W;                           // 14
 
    K = &I;
    F = &M;
 
    R = &K;                            // 19
    W = &F;                            // 20
 
    SUM = *K + *F;
    P_SUM = **R + **W;                 // 23
 
    printf("SUM=%d\n", SUM);           // 25
    printf("P_SUM=%d\n", P_SUM);       // 26
 
    printf("*R-->%p *W-->%p", *R, *W); // 28
 
    return 0;
}

13,14 줄에 포인터의 포인터 변수를 선언하였다. 여기서 별표(*)두개는 포인터의 포인터를 선언하는 것뿐 다른 역할은 없다. 19,20줄을 보면 포인터 변수 K,F의 메모리 주소를 포인터의 포인터 변수 R,W에 지정하는 것 밖에 없다. 여기서 주의 할게 있다면 포인터의 포인터 변수는 포인터 변수의 주소값만 가질수 있지 일반 변수의 메모리주소 값은 가질수 없다는 것이다.

23줄의 **R,**W는 더블 간접참조 연산자라 할수 있다. 여기서 더블이란 주소값을 두 번 지나서 값을 간접 참조한다는 것이다. 25와 26줄의 \n은 다음줄 처음으로 커서를 움직여라는 뜻이다. 28줄의 *R,*W은 포인터 변수 K,F의 메모리 주소값을 가진다. %p는 메모리 주소를 표시해주는 변환 문자이다.

좀더 쉽게 도표로 메모리 주소가 어떻게 전달 되는지 파악해 보자.

도표:

I의 메모리 주소 -> K의 메모리 주소 -> R의 값
M의 메모리 주소 -> F의 메모리 주소 -> W의 값

이렇게 하면 이해하기가 쉬울거라 본다.

그렇다면 포인터의 포인터의 포인터를 선언해 보자.

예제:

#include <stdio.h>
 
int main()
{
    int I = 1;
    int M = 2;
    int SUM;
    int P_SUM;
    int PP_SUM;
 
    int *K;
    int *F;
 
    int **R;
    int **W;
    int ***Q;                                // 16
    int ***B;                                // 17
 
    K = &I;
    F = &M;
 
    R = &K;
    W = &F;
 
    Q = &R;                                  // 25
    B = &W;                                  // 26
 
    SUM = *K + *F;
    P_SUM = **R + **W;
    PP_SUM = ***Q + ***B;                    // 30
 
    printf("SUM = %d\n", SUM);
    printf("P_SUM = %d\n", P_SUM);
    printf("PP_SUM = %d\n", PP_SUM);
 
    printf("*R-->%p *W-->%p\n", *R, *W);     // 36
    printf("**Q-->%p **B-->%p\n", **Q, **B); // 37
    printf("*Q-->%p *B-->%p\n", *Q, *B);
 
    return 0;
}

16,17줄에 포인터의 포인터의 포인터 변수를 선언하였다. 별표(*) 세 개는 포인터의 포인터의 포인터를 선언한다는 선언자라 할수 있다. 25,26줄은 포인터의 포인터의 포인터 변수 Q,B에 포인터의 포인터 변수 R,W의 메모리 주소값을 지정해 주는 것이다. 30줄의 별표(*) 세 개는 더더블 간접 참조 연산자라 할수 있다. 여기서 더더블이란 주소값을 세 번 지나서 값을 간접 참조한다는 것이다. 다시 말하자면 포인터의 포인터의 포인터 변수 Q,B에 더더블 간접 참조하여 합계를 내는 것이다. 36,37줄의 메모리 주소가 똑같은 값을 갖는다. 왜 그럴까?

이럴때는 간접 참조 연산자에 대해 이해를 해야 할게 있다. 즉 포인터 변수를 선언할때의 별표의 개수가 정해지는데 그 별표의 개수 그대로 간접참조 하면 포인터 변수가 아닌 일반 변수의 값을 가져서 계산하는 것이다. 그렇다면 37줄의 별표 두 개의 간접 참조는 무엇일까?

그건 메모리 주소를 더블 간접참조하는 것 뿐이다. 그리고 38줄의 별표 한 개의 간접 참조는 한 개의 간접참조를 한다. 이것도 어렵게 느낀것일까?

더욱 쉽게 설명해 보겠다.

포인터의 포인터의 포인터 변수 Q,B가 어떤 값을 갖는지 도표로 이해해 보자.

도표:

I의 메모리 주소 -> K의 메모리 주소 -> R의 메모리 주소 -> Q의 값
I -> *K -> **R -> ***Q

여기서 *R==&K 와 같고 **Q==&K 와 같으며 *Q==&R 와 같다.

M의 메모리 주소 -> F의 메모리 주소 -> W의 메모리 주소 -> B의 값
M -> *F -> **W ->***B

여기서 *W==&F 와 같고 **B==&F 와 같으며 *B==&W 와 같다.

지금까지는 포인터의 포인터의 포인터를 설명하였다. 그렇다면 포인터라는 단어가 4개나 5개를 덧붙인 포인터를 한번 생각해 보라. 그러면 금방 쉽게 이해하리라 본다.

이제까지 공부한 것이 포인터 기초다. 이 기초를 아주아주 잘닦으면 실전 프로그래밍은 아주 쉽게 쓸수 있다. 하지만 엄청나게 어려운 포인터가 있는데 이것은 자기 자신만 아는 프로그래밍을 할수 있으며 옛날 책에 씌워져 있는데 지금도 생각하면 무슨말인지 이해가 않갈 정도 이다.

포인터 기본이 되는 것은 포인터 연산, 함수 포인터, 파일 포인터, 배열 포인터 등 몇 개는 일반책을 보기 바란다. 저자가 설명하고 싶어도 책이 별로 없다. 왜냐면 저자의 부모가 컴퓨터 책이라면 진절머리가 난다며 보지 말라한다.그래서 책을 볼수가 없다. 그점 이해하고 이문서를 보는 독자들도 열심히 공부하길 바란다.

Forums: 
김정균의 이미지

안녕하세요? 혹시 해당 강좌 직접 작성 하신 것인가요?
만약 펌이라면, 출처와 해당 강좌 라이센스 확인이 필요 합니다.

일단, html code 가 잔뜩 들어 있는 것으로 보아 펌인 것 같아 확인 차 문의 드립니다.

lfs의 이미지

이 강좌는 제가 독학한것입니다. 머리말에서 나오듯이 환영을 본것입니다.
포인터 가르쳐 준 사람 제 주변에는 한명도 없습니다.
그리고 HTML은 한글 워드프로세서에서 작성한것입니다.
김정균님 HTML 코드 제대로 보십시오.

김정균의 이미지

본인 작성글이라는 내용이나 출처가 포함되지 않아서 확인 차 여쭈어 본 것입니다. 기분 나쁘셨다면 사과 드립니다.

그리고, 이 게시판에 HTML 양식의 글은 xss attack 방지를 위하여 fintering 을 많이 하게 되므로, 포맷만 더 이상하게 될 뿐입니다. 일반 텍스트로 작성(filtered html)을 해 주시고 강조할 부분 같은 일부 부분만 html 코드를 사용해 주세요. 그리고 code 는 <code> ~ </code> 블럭으로 감싸 주시면 됩니다. 일단 제가 시범으로 본문글 format 을 변경해 놓았으니 수정하기로 어떻게 했는지 참고해 보세요.

HTML 을 언급한 이유는 대부분의 펌글들이 그냥 copy & paste 로 같다 붙이는 경우가 대부분이라서 펌글이 아닐까 판단한 사유로 언급한 것입니다.

좋은 자료 올려 주셔서 감사합니다.

ssh983620의 이미지

이해가 잘 안 됩니다. 혹시 제가 이해를 한 건지 확인해 주실 수 있나요?

K = &I 는 I의 주소값을 번지해서 포인터 변수 K에 지정한 것이고
R = &K 는 K의 주소값을 번지해서 포인터 변수 R에 지정한 것이면

처음에 생각했을 때에는 *R = K = &I 라고 생각했었는데 조금 더 생각해 보니
R에 별표(*) 하나가 들어가서 K의 주소 값을 가리키고 (**이라면 정수를 가리키나요?) K의 주소값은 I가 가진 정수의 주소 값을 가리키므로 최종적으로 *R은 I의 주소 값을 가리킵니다.
여기서 Q = &R을 추가해서 ***Q가 I의 정수의 주소를 가리킨다면 **는 K의 주소 값을 가리키고 *는 R의 주소값을 가리킵니다.

제대로 이해했나요..?

익명 사용자의 이미지

잘 이해 하신거 같은데요

int main()
{
 
        int I=100;
        int *K;
        int **R;
        int ***Q;
 
        K=&I;
        R=&K;
        Q=&R;
 
        printf("I: %d, my_addr %p\n",I,&I);
        printf("K: %d, dst_addr %p, my_addr %p\n",*K,K,&K);
        printf("R: %d, dst_addr %p, my_addr %p\n",**R,R,&R);
        printf("Q: %d , dst_addr %p, my_addr %p\n",***Q,Q,&Q);
 
        return 0;
}
ssh983620의 이미지

적어주신 코드를 보고 확실해졌습니다. 코드까지 적어주시구.. 도움주셔서 감사합니다!

이태현의 이미지

주제 넘은 댓글일지 모르겠습니다만, 왜? 필요한가? 라는 필요성의 관점을 조금 부각 시켜 주시는 것이 더 좋지 않을까 생각 듭니다.
열심히 적어 주신 내용 잘 보았습니다. 수고하셨습니다.

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.