컴퓨터 구조/C, C++

[C/C++]포인터 기초

JiHxxn 2024. 8. 4. 23:19

👉 포인터

왜 포인터를 사용해야 하는가?

  • 메모리 영역이 다른(중괄호) 변수의 값을 변경하고 싶다.

어떻게?

  • 가상 메모리에 저장된 변수 주소만 알 수 있다면 다른 메모리 영역에서도 값을 변경할 수 있다.

절대 주소란

  • RAM 메모리의 절대 주소(실제 물리적 메모리). 바뀌지 않는.

가상 메모리 주소란

  • 복사해 준 메모리 주소 ex. (0x0000000) 의 0x

대표 주소란

  • 복사해준 메모리 첫 번째 주소.

ex. 함수에 2가 저장된 int형 변수 메모리 저장 형태

RAM    가상 메모리     stack메모리(후입선출)

[ ]              [ ]                  0x4[00000000]

[ ]              [ ]                  0x3[00000000]

[ ]        >    [ ]        >        0x2[00000000]    -> int(4byte) 공간에 2진수 형태의 2를 대입

[ ]              [ ]                  0x1[00000010]

  • 참조 : 주소 값을 이용하여 메모리 공간에 접근하여 읽기 또는 쓰기를 하는 행위

포인터 선언 방법

  • int* p;

🤔 포인터 초기화 시 NULL, nullptr 차이점

  • 함수를 오버로드하여 Print(int* pNum); Print(int Num); 이란 함수를 작동시켰을 때, 파라미터로 NULL과 nullptr을 넣는다면 결과는 어떨까?
void Print(int* pNum) { std:: cout << "int 포인터 : "<< pNum << endl;}
void Print(int Num) { std:: cout << "int : " << Num << endl;}

int main()
{
	print(NULL);     // 결과 값 : int : 0
	print(nullptr);  // 결과 값 : int 포인터 : 00000000
	return 0;
}
  • 결과는 NULL은 매개변수 int인 Print 함수를 호출하였고, nullptr은 int*인 Print 함수를 호출하였다.

정리

  • int* p = NULL;
    • NULL = 순수한 0(리터럴 상수)
    • NULL은 포인터가 아니라 상수 0이다.
  • int* p = nulptr;
    • 포인터 전용 초기화 키워드 nullptr
    • nullptr은 포인터이다.

메모리 0번지

  • RAM의 0번지를 여러 곳에서 참조를 걸어버리면 문제가 생길 수 있다.
  • 읽기만 가능하고 쓰기는 불가능하다.
  • 초기화 용도로만 사용하고 다른 용도론 사용하지 않는다.

포인터의 크기는 컴퓨터의 역사에 따라 달라진다. *모든 종류의 포인터의 사이즈(자료형 구분 x) 16bit 기반 > 2byte 32bit 기반 > 4byte 64bit 기반 > 8byte

0x == 16진수

ex. 32bit 기반 최소 주소 번지 0x00000000 (가장 빠른 번지) 최대 주소 번지 0xffffffff (가장 큰 번지)

  • 포인터를 만들 때 가장 큰 번지를 기준으로 만든다.
  • f는 10진수로 15이고 15를 2진수로 변환하면 1111 -> 4bit
    • f가 8개면 32bit
      • 32bit는 4byte

& : [주소] 추출 연산자, [address] 연산자

  • 변수의 주소를 추출하여 반환한다.
  • L-Value 타입에 붙었을 경우 연산자의 의미가 달라진다.

* : [참조] 연산자, [포인터] 연산자

  • 주소를 이용하여 메모리 공간에 접근하여, 읽기 쓰기를 가능하게 한다.

직접 참조 : 실제 메모리 주소에 접근해서 읽고 쓴다. call by reference

간접 참조 : 매개자인 포인터 통해서 메모리에 접근해서 읽고 쓴다.

🍎 포인터 예제

// int 변수 초기화
int iTemp(10);

// int 포인터 선언 및 초기화
int* p = nullptr;

// iTemp 주소값을 불러와 포인터 변수 p에 저장한다.
p = &iTemp;

// iTemp 주소값에 100을 대입
*p = 100;

// 포인터 연산자를 사용해 주소의 값을 불러와 출력한다.
cout << *p << endl;

포인터 사용 시 꿀팁(상쇄 방법) ex. int iNum(0); int* p = &iNum;

iNum 값 == *p == *&iNum == iNum

포인터 연산 ex. ++p;

  • 포인터가 가진 주소값에서 연산의 회수만큼 메모리 공간을 이동한다.
    • 참조하는 자료형의 크기만큼 이동한다. char = 1byte, short = 2byte, long long = 8byte
  • 참조하는 자료형의 크기만큼 메모리 이동을 한다.

댕글링 포인터

  • 사용 허가받지 않은 주소에 접근하고 쓰는 행위
    • 이럴 경우 다른 운영체제는 해당 메모리는 비어있는 공간으로 인식해 다른 프로그램에게 할당해 줄 수 있다.
  • 반드시 null 값으로 변경해야 된다.

메모리가 변수를 만들 때 공백 메모리를 만드는 이유

  • 연속된 메모리로 인식해 잘못 값을 가져올 수 있기 때문

연속적인 메모리 - [배열] 비연속적인 메모리 - [개별 자료형 변수]

랜덤 액세스(임의 접근, 무작위 접근)

  • 순차 접근과는 대비되는 집합 내의 주소를 알고 있다면 요소의 개수와 무관하게 모든 요소에 쉽고 효율적으로 동일한 시간에 접근할 수 있는 방식이다.

📒 참고 자료

C++에서 nullptr을 사용해야 하는 이유

c++에서의 NULL과 nullptr