C++/C++ : Study

c++ 포인터 참조자 (1) 개요

더블유제이플로어 2024. 9. 22. 02:54

Today Study

◆  포인터

◆  참조자


(Summary) Pointers

◆  포인터 개요

◆  포인터의 정의

◆  주소로의 접근

◆  역참조

◆  동적 메모리 할당

◆  포인터와 배열

◆  포인터와 const

◆  포인터의 pass-by-address

◆  주의사항


Pointers

stack 메모리는 자동으로 정리(해제) 된다.

함수가 끝나면 함수 구분선과 지역변수의 메모리가 해제된다.

Heap 메모리는 직접 해제해주어야 한다.

우측에 있는 스택메모리는 1 byte가 아니라

만일 지역 변수가 int 일 경우 4byte로 걸쳐서 데이터가 저장되는 것이다.

각 칸의 주소는 따로 따로 있으나 우측 하단에 주소값을 기입하였다. 

int x 는 stack 메모리 4칸 중 첫번째 칸이 예를 들어 0x1000이라는 주소값을 가지고 있다면

두번째 변수 int y는 4개의 칸 중 첫번째 칸이 0x1000 중 4칸이 이미 할당되었기 때문에  0x1000 - 4로 표기 하였다.


알아두면 좋을 고급 내용

Stack

  • 매우 빠른 접근 속도
  • 변수를 명시적으로 해제할 필요가 없음
  • 공간이 CPU에 의해 효율적으로 관리되며, 메모리가 조각화되지 않음
  • 지역 변수만 사용 가능
  • 스택 크기에 제한이 있음 (운영체제에 따라 다름)
  • 변수를 크기 조정할 수 없음

Heap

  • 변수를 전역적으로 접근할 수 있음
  • 메모리 크기에 제한이 없음
  • (상대적으로) 느린 접근 속도
  • 메모리 공간이 효율적으로 사용된다는 보장이 없으며, 메모리 블록이 할당되었다가 해제되면서 조각화될 수 있음
  • 메모리를 직접 관리해야 하며(변수 할당 및 해제를 직접 처리), 실수할 가능성이 있음
  • 변수를 realloc()을 사용해 크기 조정 가능

 

C++ 메모리 관리의 Stack과 Heap에 대한 차이점을 분석한 이미지로

Stack은 빠르지만 제한적이고, Heap은 유연하지만 관리가 복잡하다는것을 강조한다.


Pointer

중요한 Part 이다.

◆  포인터 변수변수의 타입 중 하나

  ●  포인터 변수의 값은 메모리의 주소(memory address)

        ☞  지금까지 사용한 변수와 포인터 변수와의 차이 : 값을 저장하는지 vs 주소를 저장하는지

  ●  포인터는 가리키는 주소에 저장된 데이터의 타입을 알아야 한다.

 

◆  포인터를 쓰는 이유

  ●  동적 할당을 통해 힙 영역 메모리를 사용

  ●  변수의 범위 문제로 접근할 수 없는 곳의 데이터를 사용 ( 참조자와 유사한 목적 )

  ●  배열의 효율적인 사용

  ●  다형성은 포인터를 기반으로 구현됨

  ●  시스템 응용 프로그램 / 임베디드 프로그래밍에서는 메모리에 직접 접근이 필요하다.

 

포인터는 메모리 주소를 저장하는 변수이다.

포인터를 쓰는 이유는 힙 영역에 메모리를 사용하기 때문이다.

그렇기에 동적 할당을 사용한다.


Definiton of Pointers

◆  포인터의 정의

  ●  기존 변수 타입 뒤에 "*"를 붙여 포인터 변수 정의

        ☞   "int*" 까지를 타입으로 생각

variable_type * pointer_name

◆  포인터의 정의 및 초기화

variable_type * pointer_name = nullptr;

int* int_ptr;
double* double_ptr = nullptr;

  ● 초기화를 하지 않으면 쓰레기 값이 들어있는 상태이므로 방지가 필요하다.

  ●  Nullptr 은 "nowhere" 개념

       ☞   임의의 메모리 주소를 가리키고 있는 상태가 아니라, 아무것도 가리키지 않는 상태를 의미

변수에 int 를 선언해야 하는 이유는 네개의 칸에 걸쳐서 a 데이터를 저장하기 때문이다.

포인터는 기본 타입에 " * " 를 붙여서 선언한다.

포인터를 선언할때 쓰레기값을 넣지 않기 위해서

초기화를 할때 nullprt로 선언하는게 좋다.


Accessing Pointer Address

◆  변수의 주소값 얻어오기

  ●  포인터 변수는 주소값을 저장하므로, 주소값을 얻어올 수 있어야 한다.

  ●  이를 위해 주소 연산자 ("&") 를 사용한다.

        ☞  연산자가 적용되는 피 연산자의 주소값이 반환된다.

        ☞   피연산자는 주소값을 얻을 수 있는 종류여야 한다. (l-value)

int num = 10;

cout << "Value : " << num << endl;  // 10
cout << "Address : " << &num << endl; // num 의 주소값
cout << "Address : " << &10 << endl; // ERROR! 10은 주소가 없다.

이런 개념이 된다.

&10에서 10은 r-value 이기 때문에 선언조차 에러가 난다.

    int* p;
    cout << "Value : " << p << endl; // garbage - 컴파일러 에러가 나와 실행 되지 않음
    cout << "Address : " << &p << endl; 
    cout << "Size : " << sizeof(p) << endl; // 4 for address

    p = nullptr;
    cout << "Value : " << p << endl; // 0

디버그 x86 에서는 Size 가 4가 나오며,
x64 에서는 Size가 8이 나온다.

포인터도 변수처럼 이런식으로 선언 된다고 생각하면 된다.

포인터도 메모리에 값만 다를뿐이지 할당은 똑같다고 보면 된다.

 

◆  주소값의 이해

  ●  " 포인터 변수의 크기" 와 " 포인터가 가리키고 있는 대상의 크기 " 는 별개이다.

  ●  포인터 변수들은 모두 같은 크기

        ☞  x86 에서는 4byte

        ☞  포인터는  "주소값"을 저장하기 때문이다.

  ●  타입은 왜 필요할까?

        ☞  해당 주소의 값에 접근할 때 몇 바이트 크기인지 알아야 한다.

포인터는 주소만 저장하기 때문에

x86 에서는 4바이트로 크기가 동일하다.

 

https://youtu.be/CcuhhF76Qmw?si=mzH1w_VCf3ZtxcR3