Chapter. 7

김동현·2022년 10월 17일
0
post-thumbnail

포인터 완전 정복을 위한 포인터 이해하기

💡 포인터는 변수이므로 값을 언제든지 바꿀 수 있다. 💡 주소는 “상수”이고 포인터는 “변수”이다.

  

주소와 포인터 이해하기

주소는 변수에 할당된 메모리 저장 공간의 시작 주소 값 자체이고,

포인터는 그 값을 저장하는 또 다른 메모리 공간이다.

  • 따라서, 특정 변수의 주소 값은 바뀌지 않지만 포인터는 다른 주소를 대입하여 그 값을 바꿀 수 있다.
int a, b;     // 일반 변수 선언
int *p;       // 포인터 선언
p =&a;        // p가 a를 가리키도록 설정
p =&b;        // p가 변수 b를 가리키도록 바꿈

위의 경우 변수 a와 변수 b는 각각의 주소값을 갖고있다. 프로그램 실행 중에는 그 값이 바뀌지 않는 것이 특징이다
 

주소와 포인터의 크기

포인터도 저장공간이기 때문에, 크기가 존재한다.

포인터의 크기는 저장할 주소의 크기에 따라 결정되는데, 크기가 클수록 더 넓은 범위의 메모리를 사용할 수 있다.

모든 주소와 포인터는 (가리키는 자료형에 관계없이) 크기가 같다!

#include <stdio.h>

int main(void)
{
    char ch;
    int in;
    double db;

    char *pc = &ch;
    int *pi = &in;
    double *pd = &db;

    printf("char형 변수의 주소 크기 : %d\n", sizeof(&ch));
    printf("int형 변수의 주소 크기 : %d\n", sizeof(&in));
    printf("double형 변수의 주소 크기 : %d\n", sizeof(&db));

    printf("char * 포인터의 크기 : %d\n", sizeof(pc));
    printf("int * 포인터의 크기 : %d\n", sizeof(pi));
    printf("double * 포인터의 크기 : %d\n", sizeof(pd));

    printf("char * 포인터가 가리키는 변수의 크기 : %d\n", sizeof(*pc));
    printf("int * 포인터가 가리키는 변수의 크기 : %d\n", sizeof(*pi));
    printf("double * 포인터가 가리키는 변수의 크기 : %d\n", sizeof(*pd));

    return 0;
}

  

주소 연산자

주소란?

변수가 할당된 메모리 공간의 시작 주소를 의미한다.

시작 주소를 알면 그 위치부터 변수의 크기만큼 메모리를 사용할 수 있다

#include <stdio.h>

int main(void)
{
    int a;                                      // int형 변수 선언
    double b;                                   // double형 변수 선언
    char c;                                     // char형 변수 선언
 
    printf("int형 변수의 주소 : %u\n", &a);         // 주소 연산자로 주소 계산
    printf("double형 변수의 주소: %u\n", &b);        
    printf("char형 변수의 주소 : %u\n", &c);         

    return 0;
}

주소 연산자 &를 사용하면 변수가 할당된 메모리 공간의 시작 주소 값을 알 수 있다.
 

포인터와 간접 참조 연산자

#include <stdio.h>

int main(void)
{
    int a;                                  // 일반 변수 선언
    int *pa;                                // 포인터 선언

    pa = &a;                                // 포인터에 a의 주소 대입
    *pa = 10;                               // 포인터로 변수 a에 10 대입

    printf("포인터로 a값 출력 : %d\n", *pa);
    printf("변수명으로 a값 출력 : %d\n", a);    // 변수 a값 출력

    return 0;
}

일반 변수명을 만드는 규칙에 따라 포인터 이름을 짓고, 변수명 앞에 *를 붙인다.

*는 포인터임을 나타내는 기호이다. 포인터의 자료형은 변수의 자료형을 적는다.

포인터 pa는 변수 a가 메모리 어디에 할당되어 있는지 그 위치를 기억한다.

포인터가 어떤 변수의 주소를 저장하는 경우 ‘가리킨다’라고 표현하며, “pa → a” 라고 표현할 수 있다.

“pa → a” 가 의미하는 바: 포인터 pa는 변수 a를 가리킨다.

  *pa = 10;     

여기서, 포인터 pa는 a를 가르키므로 *pa에 10을 대입하면 결국 a에 10을 대입하는 것과 같은 셈이다.
 

여러 가지 포인터 사용해보기

#include <stdio.h>

int main(void)
{
    int a = 10, b = 15, total;                    // 변수 선언과 초기화
    double avg;                                   // 평균을 저장할 변수
    int *pa, *pb;                                 // 포인터 동시 선언
    int *pt = &total;                             // 포인터 선언과 초기화
    double *pg = &avg;                            // double형 포인터 선언과 초기화

    pa = &a;                                      // 포인터 pa에 변수 a의 주소 대입
    pb = &b;                                      // 포인터 pb에 변수 b의 주소 대입

    *pt = *pa + *pb;                              // a의 값과 b값을 더해 total 저장
    *pg = *pt / 2.0;                              // total 값을 2로 나눈 값을 avg에 저장

    printf("두 정수의 값 : %d, %d\n ", *pa, *pb);    // a 값과 b값 출력
    printf("두 정수의 합: %d\n", *pt);               // total 값 출력
    printf("두 정수의 평균 : %.lf\n", *pg);          // avg 값 출력

    return 0;
}

  

조심하자!!!

1번 경우 (맞는 사례)


    printf("두 정수의 값 : %d, %d\n ", *pa, *pb);    // a 값과 b값 출력
    printf("두 정수의 합: %d\n", *pt);               // total 값 출력
    printf("두 정수의 평균 : %.lf\n", *pg);          // avg 값 출력

2번 경우(잘못된 사례)

  
    printf("두 정수의 값 : %d, %d\n ", *pa, *pb);    // a 값과 b값 출력
    printf("두 정수의 합: %d\n", &total);               // total 값 출력
    printf("두 정수의 평균 : %.lf\n", *pg);          // avg 값 출력

나의 경우 total 값을 출력하면 된다기에 2번의 경우처럼

[ printf("두 정수의 합: %d\n", &total); ] 이런 식으로 코드를 작성했다. 하지만 내가 작성한 코드의 경우

total의 주소값을 물어보는 것이기에 원래 원하던 값과는 다른 값이 나오게된다.

int *pt = &total;

위와 같은 경우일지라도,

💡 &는 주소 연산자이기에 결국에는 주소값을 가져온다는 것을 잊지 말자!!
  • 예를들어, 준혁이를 찾으려고 했지만, 준혁이를 찾은 것이 아닌 준혁이의 집 주소를 찾은 것과 같은 경우이다. 준혁이 = “a + b” 이고, 준혁이의 집 = “total”인 것이다.
💡 포인터를 선언할 때에는 반드시 *를 각각 입력해주어야 한다!

int pa, pb 이 경우에는 pa 와 pb모두 int형 변수의 주소를 저장하는 포인터로 인식된다.

하지만,

int pa, pb 이 경우에는 pa는 포인터로 정상 인식되지만, pb는 오직 int형 변수로만 인식된다.
 

const를 사용한 포인터

const 예약어를 포인터에 사용하면 이를 가리키는 변수의 값을 바꿀 수 없다는 것이다.

#include <stdio.h>

int main(void)
{
    int a = 10, b = 20;
    const int *pa = &a;                 // 포인터 p는 변수 a를 가진다.

    printf("변수 a의 값: %d\n", *pa);     // 포인터를 간접 참조하여 a 출력
    pa = &b;                            // 포인터가 변수 b를 가리키게 한다.
    printf("변수 b의 값: %d\n", *pa);     // 포인터가 간점 참조하여 b값 출력
    pa = &a;                            // 포인터가 다시 변수 a를 가리킨다.
    a = 20;                             // a를 직접 참조하여 값을 바꾼다.
    printf("변수 a의 값: %d\n", *pa);     // 포인터로 간접 참조하여 바뀐 값 출력

    return 0;
}

pa가 가리키는 변수 a는 pa를 간접 참조하여 바꿀 수 없다는 것이다.
 

키워드로 끝내는 핵심 포인트

  1. 포인터는 메모리를 사용하는 또 다른 방법이다.
  2. 주소 연산자 &로 변수가 할당된 메모리의 위치를 확인한다.
  3. 포인터로 가리키는 변수를 사용할 때 간접 참조 연산자 *를 쓴다.
profile
디자인과 개발을 모두 할줄 아는 능력있는 개발자가 되고싶습니다.

0개의 댓글