Post

C++: 포인터(Pointer) & 레퍼런스(Reference)

C++: 포인터(Pointer) & 레퍼런스(Reference)
  • 포인터(Pointer)

    • 메모리상의 주소값을 저장하기 위한 자료형, 모든 자료형은 포인터로 만들 수 있다.
    • 동적인 메모리 접근과 관리를 가능하게 하고, 큰 객체를 복사하지 않고 주소만 전달할 수 있게
      1
      2
      3
      4
      5
      6
      7
      8
      
        int main(){
            int val = 5;
            //선언: 포인터자료형* 이름
            //변수의 주소 접근: &변수
            int* ptr = &val; //val의 주소를 ptr에 저장
            cout << ptr << endl; //val의 메모리 주소 출력
            cout << *ptr << endl; //ptr의 역참조, val의 값을 출력
        }
      
    • 역참조(Dereference)

      • 포인터에 *을 붙이는 를 통해 주소값의 변수에 직접 접근 가능하며, 수정도 해당 주소값 원래 변수에 적용됨.
      • 포인터가 가리키는 위치에서 연산을 통해 다른 주소로 이동할 수 있음.
        • 자료형마다 메모리 크기가 다르므로 포인터도 자료형을 통해 움직여야하는 크기를 알기위해 자료형을 가짐.
        • 배열이 아니어도 움직일 수 있으나, 동적할당 된 메모리나 구조에 대해 파악이 되어있는 구역이 아니면 다른 변수를 가리키거나 잘못된 메모리 구역에 접근할 수 있음.
          1
          2
          3
          4
          5
          6
          7
          8
          9
          10
          
            int main(){
                int val[3] = {5,10,15};
                int* ptr = &val[0]; 
                cout << *ptr << endl; //5 출력
                (*ptr)++; //val이 6으로 증가
                cout << *ptr << endl; //6 출력
          					
                cout << *(ptr + 1) << endl; //10 출력
                cout << *(ptr + 2) << endl; //15 출력
            }
          
    • 배열 포인터(Array Pointer)

      • 배열을 가리키는 포인터, 배열을 탐색하는데 사용 가능.
      • 배열 변수는은 배열 첫번째 원소의 주소로 변환된다는 점에서, 주소를 다루는 변수인 포인터와 유사.
      • 다만 차이점도 존재:
        • 자료의 사이즈: 배열은 배열 전체에 대한 사이즈를 가지고, 포인터는 자료 1개에 해당하는 사이즈를 가짐.
        • 변경 가능 여부: 그리고 배열 이름에는 새로운 값을 할당하면 배열에 대한 참조를 잃어버리기에 할당할 수 없지만, 포인터 변수에는 필요에 따라 다른 주소를 할당할 수 있음.
          1
          2
          3
          4
          5
          6
          7
          8
          9
          
            int main(){
            int val[5] = {1,2,3,4,5};
            int (*ptr)[5] = &val;	//val 주소를 저장
            cout << &val[0] << endl; //val[0] 주소를 출력
            cout << ptr << endl; //val 주소 => val[0] 주소를 출력
            //모두 val[2] 출력
            cout << *(*ptr + 2) << endl;//포인터 주소값 이동 (*ptr은 val 안의 int값이 아닌 배열 val, 즉 주소를 반환)
            cout << (*ptr)[2] << endl;		//인덱스 문법 활용
            }
          
    • 포인터 배열(Pointer Array)

      • 포인터로 이루어진 배열도 가능, 여러개의 포인터를 가짐.
      • 원소인 각 포인터가 할당을 통해 각각 다른 변수를 가리키도록 설정할 수도 있음.
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        
          int main(){
              int a = 1, b = 2, c = 3, d = 4, e = 5;
              int val = 10;
              int* ptr[5] = {&a,&b,&c,&d,&e};
              cout << *(ptr[0]) << endl; //a 출력
              cout << *(ptr[1]) << endl; //b 출력	
              ptr[0] = &val;
              cout << *(ptr[0]) << endl; //val 출력
              cout << *(ptr[1]) << endl; //b 출력
          } 
        
    • 이중 포인터(Double Pointer)

      • 포인터를 가리키는 포인터. (3중, 다중포인터도 문법상 문제는 없으나, 가독성엔 좋지 않음)
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        
          int main(){
              int val = 1;
              int* ptr = &val;	//val 주소를 저장
              int** ptrX = &ptr; //ptr 주소를 저장, ptr을 가리킴.
        				
              cout << ptr << endl; //val의 주소값
              cout << *ptrX << endl;		//val의 값 => val의 주소값
        				
              cout << *ptr << endl; //ptr의 역참조 => val
              cout << **ptrX << endl;		//ptrX의 역참조의 역참조 => ptr의 역참조 => val
          }
        
  • 레퍼런스(참조자, Reference)

    • C++에서 포인터에 이어 도입된 문법으로, 주소를 다룬다는 점에서 유사하지만 더 간결하고 안전한 문법을 제공.
    • 기존에 이미 존재하는 변수에 다른 이름으로 별명을 지어주는 것과 같음.
    • 포인터와 비슷하다 해도 항상 유효하다는 강한 제약을 만드는 몇가지 차이점이 있다.
      • 선언과 동시에 초기화되어야 한다.
      • 나중에 다른 주소 값으로 수정할 수 없다.
      • 유효한 null이 아닌 객체를 이용해 초기화 되야 한다. nullptr에 바인딩 될 수 없다.
      • 레퍼런스 자체에 대한 포인터 연산이 없다. *를 통한 역참조는 안되며(이미 역참조인 형태), &를 이용한 주소값 접근은 레퍼런스가 가리키는 일반 변수의 주소값을 반환한다. 때문에 사용할 때 일반 변수처럼 사용된다.
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        
          int main(){
          int val = 1;
          int* ptr = &val;	//val 주소를 저장
          //자료형& 변수이름 = 가리킬변수
          int& ref = val;	//val을 저장
        			
          //아래 두 코드 모두 동일하게 작동, val의 주소값을 출력
          cout << ptr << endl;
          cout << &ref  << endl;
        			
          //아래 두 코드 모두 동일하게 작동, 1을 출력
          cout << *ptr << endl;
          cout << ref  << endl;
        			
          ref++;	//val을 증가, (*ptr)++와 동일
          cout << ref << endl; //2를 출력
          }
        
    • 상수 레퍼런스
      • 상수 레퍼런스로 선언하면 읽기만 가능하고 변수 수정은 불가능
        1
        2
        3
        4
        5
        6
        7
        
          int main(){
          int val = 1;
          const int& ref = val;	//val을 저장
          cout << ref  << endl; //1을 출력
        			
          ref++;	//에러!
          }
        
This post is licensed under CC BY 4.0 by the author.