STUDY.md
  • (C/C++) 헷갈리는 포인터 개념 한번에 정리하기
    2024년 06월 12일 02시 23분 32초에 업로드 된 글입니다.
    작성자: 방세연

     

    Contents. 클릭하면 이동합니다. 🚀

       

       

       


       

       

       

       

      포인터 (*)

      변수의 주소를 가지고 있는 변수

      (나 이제 주소를 받아낼 준비가 되었다)

       

      주소 연산자 (&)

      변수의 주소를 계산함

      (변수의 주소 그 자체)

       

       

      기본적으로 알고있어야 포인터에 대한 정의는 이렇다.

       

       

       

       


      Q1. 왜 *p에는 &(변수)를 꼭 할당해야 하나요?

      (그냥 변수만 할당하면 안되나요?)

       

       

       

      int number = 10;
      //정수 변수 a선언 및 초기화
      
      int *p;
      //포인터 변수 p를 선언했다. (나 주소를 받을 준비가 됐어!!)
      p = &number;
      //&number : "나 들어간다. 포인터 p야, 날 대입해줘."

       

      먼저 이 코드는 기본적인 좋은 코드이다. 혹은

      int *p = &number;

      이 코드는 위와 같은 의미라는 것을 들었을 것이다.

      하지만 얼렁뚱땅 넘어가면 포인터에 대해 헷갈리는 순간이 찾아온다.

       

       

      - 잠깐!! 잘못된 포인터 사용의 경우. (&를 사용하지 않는 경우)

      int number = 10;
      
      int *p;
      //포인터 변수 p 선언 : "나는 주소를 받을 준비가 됐어. 다 들어와!!!"
      p = number;
      //문제의 구간
      //아닛.. number은 주소가 아니라 그냥 '정수'인데..? 아닛 젠장 타입이 불일치 하잖아!​

       

       


      하지만 p에 포인터 변수를 선언하고 주소가 아닌 값을 할당하면, 저렇게 컴파일 오류가 나타나버린다.
      이런 오류가 나타나는 이유는 *p와 number의 타입이 불일치하기 때문이다.

      그러니 이런 코드는 사용할 수 없는 코드이다.

       

      -  배열은 왜 &를 사용하지 않나요?

      int j = 0;
      int num[4] = { 10, 20, 30, 40 };
      int* pnum = num;  // num 배열의 첫 번째 요소의 주소를 가져옴​
      이 코드를 보면, int* pnum = num; 에서 num에 &를 붙이지 않은 것을 확인할 수 있다.
      그런데 코드에는 문제가 없다.

      아니 &를 꼭 붙여야한다매요

      이 코드가 가능한 이유는 '배열의 이름'그 자체로 배열의 첫 번째 요소의 주소를 가리키기 때문이다.
      따라서 * p도 이 배열의 주소를 인식하고 어떠한 컴파일 에러를 일으키지 않는다.

       

       

       

       

       

       


      Q2. 포인터 너무 복잡해요. 할당한 변수를 출력하면 주소밖에 안나와요.

      (혹은 저는 정수로[원래형태로] 다시 출력하고 싶어요.)

       

       

      이 코드에서 정수로 출력되는 부분과, 주소로 출력되는 부분 두 가지로 나눌 수 있다.

       

       

      ...

      먼저 이 코드는 주소로 출력되는 부분이다.

      int number = 10;
      int *p = &number;
      
      printf("%u", &number);
      int number = 10;
      int *p = &number;
      
      printf("%u", p);
      💡출력 결과
      3313366900

       

      printf에서 주소 출력을 원할 때

       

      1. &number을 넣는다.

      2. p를 넣는다.

       

       

       

      ...

      그 다음, 이 코드는 정수로(원래의 형태로) 출력되는 부분이다.

      int number = 10;
      int *p = &number;
      
      printf("%d", number);
      int number = 10;
      int *p = &number;
      
      printf("%u", *p);
      💡 출력 결과
      10

       

      printf에서 정수 출력(원래의 타입)으로 출력을 원할 때

       

      1. number을 넣는다.

      2. *p를 넣는다.

       

       

       

      - 잠깐!! 왜 포인터 *를 또 할당해요..? 아까 포인터 선언할 때 썼잖아요. 이해가 안돼요.

      그 이유는 포인터 선언의 *간접참조 연산자 *는 완전히 다른 의미를 지니기 때문이다.
      (둘이 독립변수라는 느낌으로 생각하면 편하다..)
      헷갈릴 수도 있으니 더 자세히 설명해보겠다.



       포인터 선언에서의 '*' 
      int *p;

      이게 당신이 생각했던 기본적인 포인터 선언에서 사용되는 *이다.
      다시 한번 말하지만 변수의 주소를 저장할 수 있는 준비를 하고 있다. (준비성이 좋다)

      그럼 간접참조 연산자는 뭘까?


       간접 참조 연산자로서의 '*' 

      printf("%d\n", *p);
      *p = 20;

      이 부분이 간접 참조 연산자가 수행되는 부분이다.
      즉, 포인터가 가리키는 주소의 값을 참조하거나, 수정할 때 사용된다.

      다시 말해 선언된 포인터를 마치 care💉 해주는 역할을 한다.


      printf("%d\n", *p);
      a의 값 10을 그대로 출력한다. (실제 값을 의미한다.)
      *p = 20;
      p가 가리키는 a의 값을 20으로 변경한다. (값을 수정한다)

       이 두 가지 case는 포인터 선언에 사용되는 *와는 많이 다른 형태를 띈다.
      이런 식으로 포인터를 이해하는 것이 좋다.

       

       

       

       

       

       

       

       


      Q3. 간접 참조 연산자에서 '()괄호'를 어디에 넣어야할지 모르겠어요.

      int a[2] = {10, 20};
      int *p = &a;

      예를들면 이런 코드가 있다고 치자.

       

       

      1. p가 가리키는 위치에서, 실제의 값을 가져온다. (a의 값은 10)

      2. p를 배열 자체에서 한 칸 증가시킨다. ( a[1]은 20 )

      a = 20

       

       

       

      1. a[0]의 실제의 값을 참조한다. ( a = 10 )

      2. a[0]의 값을 +1 후위 증가시킨다. ( a = 11 )

      a = 11

       

       

       

       

      1. 포인터 p의 주소의 값을 한 칸 증가시킨다. ( a[1] = 20 ) (이 값의 주소)

      2. 주소의 값의 실제 값을 참조한다. ( a[1] = 20 )

      a = 20

       

       

       

      우선 순위에 대한 이해

      연산자 규칙에서 이런 포인터의 경우들에는,
      1순위 우선순위 : 괄호
      2순위 우선순위 : * 연산자
      3순위 우선순위 : 나머지 등등..

      인 것을 확인할 수 있다. * 포인터 연산자는 매우 높은 우선순위를 가지고 있기 때문에,
      괄호()를 적절히 섞어 우선순위를 규정해주는 것은 좋은 도움이 된다.

       

       

       

       

       

       

       

       


      Q4. 포인터배열의 관계가 헷갈려요. 

      앞서 Q3.에서 괄호에 대한 설명을 읽어보았을 것이다.

      포인터와 배열의 대응 관계에 대해 설명해보도록 하겠다.

       

       

       

       

      int a[] = {10, 20, 30, 40, 50 };
      
      int *p;
      p = a;
      printf("%d %d %d", p[0], p[1], p[2]);

      예를들면 모든 코드를 적지 않았지만,

      포인터를 선언한 p에 배열의 첫 번째 주소 값을 넣어주었다.

       

      이후 포인터 p[0], p[1], p[2]를 출력해보면 배열 a[0], a[1], a[2]와 같다는 것을 알 수 있다.

       

       

       

       

       

       

       


      글을 마치며.

      c언어를 공부하며 주로 헷갈릴 것 같은 부분들을 위주로 정리해보았다.

      이 작성 글이 닿는 사람들에게 도움이 되길 바라며.. 이만 글을 마치도록 하겠다.

      댓글