티스토리 뷰

리눅스 C-언어 초급과정

13. 포인터

다유데브 2022. 11. 16. 14:07
반응형

1) 목표

  • 포인터 개념 이해하기
  • 이번 강의에서는 포인터의 기본 개념을 배우고 계속되는 강의에서 자세히 다루겠습니다.

2) 설명

    ▶ 포인터는 c언어에서 제공하는 타입중 하나입니다. 
         다른 타입과 다르게 포인터 변수는 그값을 주소(어드레스)를 가진다는점이 다릅니다.

// 포인터 변수 선언
char *ptr;

// 포인터 변수에 값을 할당
char sbuf[6];
char *ptr;
ptr = sbuf;

int nVal;
int *nPtr;
nPtr = &nVal;

    ▶ 포인터 변수는 변수명 앞에 * 를 붙여서 선언합니다.
        포인터 타입은 앞에서 살펴본 타입으로 선언 가능합니다.

        char *ptr, int *ptr, long *ptr, double *ptr  ...
        포인터 변수의 타입은 대상이 되는 변수의 타입과 일치해야겠지요.
        char에 대한 포인터는 char *, int는 int * ...

    ▶ 변수가 선언이 되면 메모리가 할당된다고 말씀을 드렸는데 이렇게 메모리가 할당이 되면 메모리의 주소를 갖게 됩니다. 바로 이런 주소를 다루는 변수가 포인터입니다.

    ▶ char sbuf[6];  이렇게 선언이 되면 메모리에 6바이트(char가 1바이트, 배열크기가 6)가 할당됩니다.
        char *ptr; 포인터 변수가 선언되면 이 변수도 메모리에 8바이트(포인터크기)가 할당됩니다.
        sbuf가 메모리에 할당된 주소를 포인터 변수에 대입해서 사용합니다.
        ptr = sbuf;   sbuf에 할당된 메모리 주소를 ptr변수에 대입합니다.

    ※ 포인터 변수는 단독(버퍼 없이)으로 사용할 수 없습니다.
        포인터 변수는 반드시 어떤 메모리의 주소를 할당(값)해서 사용됩니다.  (ptr = sbuf 처럼)

        이처럼 어떤 메모리(sbuf[6])가 바로 버퍼라는 용어로 사용됩니다.
        즉, 포인터 변수는 버퍼와 같이 사용됩니다.

3-1) 변수와 포인터 예제 프로그램

    ▶ vi pointer.c

#include <stdio.h>

int main() {
    int nval = 10;
    int *nptr;

    // 단일 변수에 대한 포인터
    printf("대입전 메모리주소:[%p][%p]\n", &nval, nptr);
    nptr = &nval;
    printf("대입후 메모리주소:[%p][%p]\n", &nval, nptr);
    printf("값: nval=[%d] nptr=[%d]\n", nval, *nptr);

    return 0;
}

    ▶ 컴파일/실행

~/c-lecture (master ✘)✭ ᐅ gcc -o pointer pointer.c
~/c-lecture (master ✘)✭ ᐅ
~/c-lecture (master ✘)✭ ᐅ ./pointer
대입전 메모리주소:[0x7ffeb6cc6b3c][0x7ffeb6cc6c40]
대입후 메모리주소:[0x7ffeb6cc6b3c][0x7ffeb6cc6b3c]
값: nval=[10] nptr=[10]
~/c-lecture (master ✘)✭ ᐅ

    ▶ 분석

  • 배열이 아닌 변수가 선언될때 크기 만큼 메모리가 할당되는데 이 메모리를 참조할때는 
    변수앞에 & 를 붙입니다. 변수앞에 &를 붙이면 이 의미는 변수의 메모리 주소라는 의미입니다.
    c언어의 문법입니다. 변수앞에 &를 붙이면 메모리주소다 라고 외워주세요!! 
  • 포인터는 주소값을 갖는 변수라고 말씀드렸는데요. 이렇게 변수의 주소를 포인터에 할당할 수 있습니다.
    nptr = &nval;   --> nval 변수의 주소를 포인터 nptr에 대입
  • 포인터는 주소값을 갖는 변수지요. 그럼 그 주소에 들어있는 값을 참조할때는 포인터 변수명 앞에
    * 를 붙입니다.
    nval 의 값은 10입니다. *nptr의 값도 10이 됩니다.
    &nval의 주소는 nptr과 같습니다.
변수 주소(어드레스)
nval &nval 10
*nptr nptr *nptr
  • 예제 실행에서 nptr = &nval; 문 전 printf문입니다.
    printf()에서 %p 서식은 주소를 출력하는 서식입니다. 포인터 공부 하실때 사용하시면 유용합니다.
    대입전 메모리주소:[0x7ffeb6cc6b3c][0x7ffeb6cc6c40]
    2개의 주소가 다르다는걸 아실수 있습니다. nptr값은 선언되면서 무작위의 값이 할당됩니다.(가비지)
    nptr = &nval; 할당후 출력되는걸 보시면
    대입후 메모리주소:[0x7ffeb6cc6b3c][0x7ffeb6cc6b3c]
    같아진걸 보실수 있습니다.
  • 값을 출력하는 printf문을 보시면
    printf("값: nval=[%d] nptr=[%d]\n", nval, *nptr);
    nval은 변수 자체이지만 포인터 변수 nptr의 값을 출력할때는 *nptr로 사용됩니다.

3-2) char 배열과 포인터 예제 프로그램

    ▶ vi pointer2.c

#include <stdio.h>

int main() {
    char sbuf[5] = {'h', 'e', 'l', 'l', 'o'};
    char *sptr;
    int i;

    // 배열 변수에 대한 포인터
    printf("대입전 메모리주소:[%p][%p]\n", sbuf, sptr);
    sptr = sbuf;
    printf("대입후 메모리주소:[%p][%p]\n", sbuf, sptr);

    // 포인터를 사용해서 sbuf값을 출력
    for (i = 0; i < 5; i++) {
        printf("sbuf[%d]:주소-[%p][%p] 값-[%c][%c]\n", i, &sbuf[i], sptr+i, sbuf[i], *(sptr+i));
    }

    return 0;
}

    ▶ 컴파일/실행

~/c-lecture (master ✘)✭ ᐅ gcc -o pointer2 pointer2.c
~/c-lecture (master ✘)✭ ᐅ ./pointer2
대입전 메모리주소:[0x7ffda177f183][0x562caed4d080]
대입후 메모리주소:[0x7ffda177f183][0x7ffda177f183]
sbuf[0]:주소-[0x7ffda177f183][0x7ffda177f183] 값-[h][h]
sbuf[1]:주소-[0x7ffda177f184][0x7ffda177f184] 값-[e][e]
sbuf[2]:주소-[0x7ffda177f185][0x7ffda177f185] 값-[l][l]
sbuf[3]:주소-[0x7ffda177f186][0x7ffda177f186] 값-[l][l]
sbuf[4]:주소-[0x7ffda177f187][0x7ffda177f187] 값-[o][o]
~/c-lecture (master ✘)✭ ᐅ

    ▶ 분석

  • char sbuf[5]; 배열로 선언하면 5바이트 크기의 메모리가 할당됩니다. 
    일반 변수에서 할당된 메모리를 참조할때는 & 를 붙여서 참조를 했는데
    배열에서는 배열 이름 자체가 메모리의 주소가 됩니다. 이부분도 외워주세요!
    char sbuf[5]에 할당된 메모리 주소의 시작은  sbuf입니다.
    그런데 배열에서 3번째 주소를 알고 싶은 경우는 일반 변수처럼 & 를 붙여줍니다.
    sbuf[2] 의 주소는 &sbuf[2] 입니다.
  • sptr = sbuf; 포인터 변수 대입전에는 2개의 주소가 서로 다릅니다.
    여기서 배열에서는 변수명이 메모리 주소가 되어서 sbuf 를 그대로 포인터 변수에 대입합니다.
    sbuf는 배열의 시작 주소라 말씀을 드렸는데요. 그래서 대입문은
    sptr = &sbuf[0]; 으로도 가능합니다. 즉, sbuf = &sbuf[0] 입니다.
  • 포인트 변수 대입전

  • 포인트 변수 대입

  • printf("sbuf[%d]:주소-[%p][%p] 값-[%c][%c]\n", i, &sbuf[i], sptr+i, sbuf[i], *(sptr+i)); 출력문에서
    배열 각각의 주소를 출력합니다. &sbuf[i] 로 출력합니다. 또한 포인터 변수로도 출력하는데 
    그 문법은 sptr+i로 하고 있습니다. 즉, &sbuf[i] == sptr + i 와 같습니다.
    값을 출력할때 배열은 sbuf[i] 으로 배열의 값을 출력하고, 포인터로 값을 출력할때는 
    *(sptr+i) 로 출력합니다. sptr+i 가 각 배열의 주소를 뜻하고 포인터 값에 접근할 경우는 * 를 붙인다고 말씀을 드렸지요. 그래서 *(sptr+i) 주소앞에 * 를 붙인겁니다. 연산자 우선순위가 * 가 + 보다 앞서서 ()로 감싸줍니다.
  • 주소 출력 부분에서 눈여겨 보실게 있는데요.
    sbuf[0]:주소-[0x7ffda177f183]
    sbuf[1]:주소-[0x7ffda177f184]
    sbuf[2]:주소-[0x7ffda177f185]
    sbuf[3]:주소-[0x7ffda177f186]
    sbuf[4]:주소-[0x7ffda177f187] 
    주소를 보시면 +1씩 증가한것을 보실수 있습니다. 이것은 char 타입이 1바이트라서 배열의 다음 주소가 1씩 증가합니다. 이렇게 메모리 주소를 다루는 변수가 포인터입니다.

3-3) int 배열과 포인터 예제 프로그램

    ▶ vi pointer3.c

#include <stdio.h>

int main() {
    int nbuf[5] = {1, 2, 3, 4, 5};
    int *nptr;
    int i;

    // 배열 변수에 대한 포인터
    printf("대입전 메모리주소:[%p][%p]\n", nbuf, nptr);
    nptr = nbuf;
    printf("대입후 메모리주소:[%p][%p]\n", nbuf, nptr);

    // 포인터를 사용해서 sbuf값을 출력
    for (i = 0; i < 5; i++) {
        printf("nbuf[%d]:주소-[%p][%p] 값-[%d][%d]\n", i, &nbuf[i], nptr+i, nbuf[i], *(nptr+i));
    }

    return 0;
}

    ▶ 컴파일/실행

~/c-lecture (master ✘)✭ ᐅ gcc -o pointer3 pointer3.c
~/c-lecture (master ✘)✭ ᐅ ./pointer3
대입전 메모리주소:[0x7fffeafb5cb0][0x55e4fac89280]
대입후 메모리주소:[0x7fffeafb5cb0][0x7fffeafb5cb0]
nbuf[0]:주소-[0x7fffeafb5cb0][0x7fffeafb5cb0] 값-[1][1]
nbuf[1]:주소-[0x7fffeafb5cb4][0x7fffeafb5cb4] 값-[2][2]
nbuf[2]:주소-[0x7fffeafb5cb8][0x7fffeafb5cb8] 값-[3][3]
nbuf[3]:주소-[0x7fffeafb5cbc][0x7fffeafb5cbc] 값-[4][4]
nbuf[4]:주소-[0x7fffeafb5cc0][0x7fffeafb5cc0] 값-[5][5]
~/c-lecture (master ✘)✭ ᐅ

    ▶ 분석

  • char 배열을 int 배열로 바꿘본 예제입니다.
    실행한 주소를 보시면 char 는 주소가 1씩 증가를 했는데 int 배열은 4씩 증가한 걸 보실수 있습니다.
    int 타입의 크기가 4바이트입니다. 이렇게 타입에 따라서 메모리가 할당된 모습을 확인하실 수 있습니다.
printf("nbuf[%d]:주소-[%p][%p] 값-[%d][%d]\n", i, &nbuf[i], nptr+i, nbuf[i], *(nptr+i));

    이문장을 

printf("nbuf[%d]:주소-[%p][%p] 값-[%d][%d]\n", i, &nbuf[i], nptr, nbuf[i], *nptr);
nptr++;

  이렇게 변경 가능합니다.
  nptr주소로 *nptr은 주소에 대한 값으로 사용합니다.
  nptr++; ++연산자로 포인터를 다음 데이타로 이동시킵니다.

  nptr = &nbuf[0];    -> 1번째 배열 주소
  nptr++;                 --> 2번째 배열 주소

  nptr++;                 --> 3번째 배열 주소

  ...

  포인터의 ++, -- 연산자는 struct 문법에서 다시 자세히 다루겠습니다.

반응형

'리눅스 C-언어 초급과정' 카테고리의 다른 글

14. 함수 파라메타 유형  (0) 2022.11.17
14. 함수  (0) 2022.11.16
12. 문자열(string)  (1) 2022.11.16
11. 다차원 배열  (0) 2022.11.16
10. 배열  (0) 2022.11.16
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/08   »
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
31
글 보관함