프로그래밍 언어/C++

[자료형(data type)] C++ 파생형 포인터 총정리

순정법사 2023.09.09
더보기
자료형 중 포인터 공부 / https://ddanzimind.tistory.com/32

 

A. 포인터

1. 포인터의 개요

a. 주소값의 이해

🌟 데이터의 주소값이란 해당 데이터가 저장된 메모리의 시작 주소를 의미

 

C언어에서는 주소값을 1바이트 크기의 메모리 공간으로 나누어 표현

 

b. 포인터란?

🌟 메모리의 주소값을 저장하는 변수, 포인터 변수라고도 부름

 

포인터와 연관되어 사용되는 연산자는 & 주소연산자와 * 참조 연산자가 있음

 

 

2. 포인터 연산자

a. 주소(번지) 연산자

🌟 변수의 이름 앞에 사용하여, 해당 변수의 주소값을 반환

 

'&'기호는 앰퍼샌드(ampersand)라고 읽음

 

b. 참조 연산자

🌟 포인터의 이름이나 주소 앞에 사용해 포인터에 저장된 주소에 저장되어 있는 값 반환

 

'*' 기호는 역참조 연산자로 에스크리터(asterisk operator)라고도 불림

 

💙 * 연산자의 다양성

이항 연산자에 사용하면 곱셈 연산
포인터의 선언시
메모리 접근시에도 사용

 

 

3. 포인터의 선언과 참조

a. 포인터의 선언

포인터를 선언할 때 참조 연산자(*)의 앞과 뒤에 존재하는 공백은 무시

동시 선언 가능하지만 코드에 유의해야 함

 

📘 문법

타입* 포인터이름; //선언만 하는법
타입 포인터이름* 포인터이름* // 동시 선언하는 법

//타입 : 포인터가 가리키고자 하는 변수의 타입
//포인터 이름 : 포인터가 선언된 후에 포인터에 접근하기 위해 사용

 

⏳ 예제 : 포인터 동시 선언시 주의사항

int* ptr1, ptr2; // ptr1 : int형 포인터 ptr1 : int형 변수
int *ptr1, *ptr2; // 둘 다 int형 포인터

 

b. 포인터의 선언과 초기화

포인터를 선언 후 참조 연산자(*)를 사용하기 전에 포인터는 반드시 초기화해야 함

👉 초기화하지 않은 채로 참조 연산자를 사용하게 되면, 어딘지 알 수 없는 메모리 장소에 값을 저장하는 것임

💥 이러한 동작은 위험하고 오류는 디버깅하기도 매우 힘듦

 

📘 문법

타입* 포인터이름 = &변수이름;

또는

타입* 포인터이름 = &주소값;

 

c. 포인터의 참조

선언된 포인터는 참조 연산자(*)를 사용하여 참조

 

⏳ 예제 : 포인터의 주소값과 함께 포인터가 가리키고 있는 주소값의 데이터를 참조

int x = 7;            // 변수의 선언
int *ptr = &x;      // 포인터의 선언
int **pptr = &ptr; // 포인터의 참조

 

🎨 도식화

 

 

🧡 포인터의 크기와 워드(word)

워드(Word)란 컴퓨터(CPU)가 한 번에 처리 할 수 있는 정보의 양 = 데이터의 크기

포인터변수는 메모리에서 변수의 위치를 나타내는 주소를 다루는 변수
👉 크기는 CPU나 컴파일러의 정책에 의해 달라짐

⦁  32비트 CPU에서는 1word = 32bit = 4byte => 포인터 변수의 크기 또한 4byte
⦁  64비트 CPU에서는 2배라서 포인터의 크기가 8

 

정보의 처리단위 word와 레지스터

A. 정보의 처리 단위 1. 아스키 코드의 유래 2. 정보 처리 단위 word 3. 레지스터와 컴퓨터 출처 : https://m.blog.naver.com/PostView.naver?isHttpsRedirect=true&blogId=zozokjs&logNo=221219673205 정보의 처리 단위Word와 레지

monamu.tistory.com

 

⏳ 예제

#include <iostream>
using namespace std;

int main(void)
{
	int num1 = 1234;
	double num2 = 3.14;
	
	int* ptr_num1 = &num1;
	double* ptr_num2 = &num2;
	
	cout << "포인터의 크기는 " << sizeof(ptr_num1) << "입니다." << endl;
	cout << "포인터 ptr_num1가 가리키고 있는 주소값은 " << ptr_num1 << "입니다." << endl;
	cout << "포인터 ptr_num1가 가리키고 있는 주소에 저장된 값은 " << *ptr_num1 << "입니다." << endl;
	cout << "포인터 ptr_num2가 가리키고 있는 주소값은 " << ptr_num2 << "입니다." << endl;
	cout << "포인터 ptr_num2가 가리키고 있는 주소에 저장된 값은 " << *ptr_num2 << "입니다.";
	return 0;
}

✨ 실행결과

포인터의 크기는 8입니다.
포인터 ptr_num1가 가리키고 있는 주소값은 0x7fffe2bdbacc입니다.
포인터 ptr_num1가 가리키고 있는 주소에 저장된 값은 1234입니다.
포인터 ptr_num2가 가리키고 있는 주소값은 0x7fffe2bdbad0입니다.
포인터 ptr_num2가 가리키고 있는 주소에 저장된 값은 3.14입니다.

 

🎨 도식화

 

 

 

2. 포인터 연산

a. 포인터 연산의 의미

포인터는 정수 연산만이 의미가 있음 (나머지 다 x)

 

  • 포인터끼리의 덧셈, 곱셈, 나눗셈 : 아무런 의미 X
  • 포인터끼리의 뺄셈 : 두 포인터 사이의 상대적 거리
  • 포인터에 정수를 더하거나 뺄 수는 있지만, 실수와의 연산은 허용 X
  • 포인터끼리 대입하거나 비교할 수 있음

 

b. 타입별 포인터 연산

포인터 연산에서 포인터 연산 후 각각의 포인터가 가리키고 있는 주소는 포인터의 타입에 따라 달라짐

증가 폭은 포인터가 가리키는 변수의 타입의 크기와 같음 (뺄셈도 동일)

 

int형 포인터의 증가폭은 int형 타입의 크기인 4바이트만큼 증가

 

c. 포인터와 배열의 관계

배열의 이름은 그 값을 변경할 수 없는 상수라는 점을 제외하면 포인터와 같음

👉 즉, 배열의 이름을 포인터처럼 사용하거나 포인터를 배열의 이름처럼 사용할 수 있음

 

배열의 이름 = 첫 번째 요소의 주소

 

⏳ 예제 : 포인터를 배열의 이름처럼 사용

#include <iostream>
using namespace std;

int main(void)
{
	int arr[3] = {10, 20, 30};	// 배열 선언 
	int* ptr_arr = arr;			// 포인터에 배열의 이름을 대입함.
	
	cout << "배열의 이름을 이용하여 배열 요소에 접근 : " << arr[0] << ", " << arr[1] << ", "  << arr[2] << endl;
	cout << "    포인터를 이용하여 배열 요소에 접근 : " << ptr_arr[0] << ", " << ptr_arr[1] << ", "  << ptr_arr[2] << endl;
		
	cout << "배열의 이름을 이용한 배열의 크기 계산 : " << sizeof(arr) << endl;
	cout << "    포인터를 이용한 배열의 크기 계산 : " << sizeof(ptr_arr);
	return 0;
}

✨ 실행결과

배열의 이름을 이용하여 배열 요소에 접근 : 10, 20, 30
    포인터를 이용하여 배열 요소에 접근 : 10, 20, 30
배열의 이름을 이용한 배열의 크기 계산 : 12
    포인터를 이용한 배열의 크기 계산 : 8

 

하지만 배열의 이름을 이요한 크기 계산시에는 다르게 출력됨

📌 배열은 배열의 크기만큼, 포인터는 포인터의 크기만큼

 

d. 배열의 포인터 연산

⏳ 예제 : 배열의 이름을 포인터처럼 사용

#include <iostream>
using namespace std;

int main(void)
{
	int arr[3] = {10, 20, 30};	// 배열 선언 
	
	cout << "        배열의 이름을 이용하여 배열 요소에 접근 : " << arr[0] << ", " << arr[1] << ", " << arr[2] << endl;
	cout << "배열의 이름으로 포인터 연산을 해 배열 요소에 접근 : " << *(arr+0) << ", " << *(arr+1) << ", " << *(arr+2);
	return 0;
}

✨ 실행결과

        배열의 이름을 이용하여 배열 요소에 접근 : 10, 20, 30
배열의 이름으로 포인터 연산을 해 배열 요소에 접근 : 10, 20, 30

 

🎨 도식화

 

 

✒ 따라서 배열 이름과 포인터 사이에는 다음과 같은 공식이 성립

 

📘 공식

arr이 배열의 이름이거나 포인터이고 n이 정수일 때,

arr[n] == *(arr + n)

 

👉  위의 공식은 1차원 배열뿐만 아니라 다차원 배열에서도 언제나 성립함

 

💥 배열의 크기에 주의

배열에 관계된 연산을 할 때는 언제나 배열의 크기를 넘어서는 접근을 하지 않도록 주의

포인터 연산을 이용하여 계산하다가 배열의 크기를 넘어서는 접근을 하는 경우, C++ 컴파일러는 어떠한 오류도 발생시키지 않고 잘못된 결과만을 반환하므로 C++로 프로그래밍할 때에는 언제나 배열의 크기에 주의!!

 

 

3. 메모리의 동적 할당

a. 메모리의 동적 할당이란?

🌟 런 타임에 메모리를 할당받는 것

 

데이터 영역과 스택 영역에 할당되는 메모리의 크기는 컴파일 타임(compile time)에 미리 결정됨

하지만 힙 영역의 크기는 프로그램이 실행되는 도중인 런 타임(run time)에 사용자가 직접 결정 = 동적 할당

 

📌 포인터의 가장 큰 목적

 

런 타임에 이름 없는 메모리를 할당받아 포인터에 할당하여, 할당받은 메모리에 접근하는 것

 

C언어에서는 malloc() 함수 등의 라이브러리 함수를 제공하여 이러한 작업을 수행(C++에서도 가능)

C++에서는 메모리의 동적 할당 및 해제를 위한 더욱 효과적인 방법을 new 연산자와 delete 연산자를 통해 제공

 

b. new 연산자

🌟자유 기억 공간(free store)이라고 불리는 메모리 공간(memory pool)에 객체를 위한 메모리를 할당

 

  • new 연산자를 통해 할당받은 메모리는 따로 이름이 없으므로 해당 포인터로만 접근
  • 메모리가 부족해 못만들면 널 포인터를 반환
  • C의 malloc()이나 calloc() 함수와 동일

 

📘 문법

타입* 포인터이름 = new 타입;

//첫 번째 타입: 데이터에 맞는 포인터를 선언
//두 번째 타입: 메모리의 종류를 지정

 

c. delete 연산자

🌟 동적으로 할당받은 메모리를 다시 운영체제로 반환

 

  • new 연산자를 통해 생성한 메모리가 아닌 변수를 선언하여 생성한 메모리는 delete 연산자로 해제할 수 없음
  • delete 연산자는 반드시 new 연산자를 통해 할당된 메모리를 해제할 때에만 사용
  • 한 번 해제한 메모리를 다시 해제하려고 시도하면 오류가 발생
  • C의 free()함수와 동일

 

📘 문법

delete 포인터이름;

 

⏳ 예제 : 메모리를 할당받고 반환

#include <iostream>
using namespace std;

int main(void)
{
	int* ptr_int = new int;
	*ptr_int = 100;
	
	double* ptr_double = new double;
	*ptr_double = 100.123;
	
	cout << "int형 숫자의 값은 " << *ptr_int << "입니다." << endl;
	cout << "int형 숫자의 메모리 주소는 " << ptr_int << "입니다." << endl;
	
	cout << "double형 숫자의 값은 " << *ptr_double << "입니다." << endl;	
	cout << "double형 숫자의 메모리 주소는 " << ptr_double << "입니다." << endl;
	
	delete ptr_int;
	delete ptr_double;
	return 0;
}

✨ 실행결과

int형 숫자의 값은 100입니다.
int형 숫자의 메모리 주소는 0x55fa5bb1feb0입니다.
double형 숫자의 값은 100.123입니다.
double형 숫자의 메모리 주소는 0x55fa5bb1fed0입니다.

 

 

 


출처 : http://www.tcpschool.com/cpp/cpp_arrayPointer_pointerIntro

 

코딩교육 티씨피스쿨

4차산업혁명, 코딩교육, 소프트웨어교육, 코딩기초, SW코딩, 기초코딩부터 자바 파이썬 등

tcpschool.com