포인터도 변수다 그러므로 메모리주소를 가지고 있다.

즉, 변수이기에 포인터에대한 포인터를 생성할 수 있다.

2중포인터 기본개념

 

int **ptr은 int *(*ptr)로 이해할 수 있다.
즉, *ptr이 가지고 있는 주소값을 또 포인터로 보게하는 것이다.

 

2중포인터 예

ptr은 int val의 주소값을 가진다.

int val = 5;
int *ptr = &val;

ptrptr은 ptr(val주소값을 가지고있는 포인터)의 주소값을 가진다.

int **ptrptr = &ptr;

de-reference도 가능하다.

cout << **ptrptr << endl;

 

동적 메모리 할당을 이용해 다차원배열 생성

 

동적 메모리 할당을 이용해 다차원배열생성을 알아보기전에

배열과 포인터는 같다는걸 잊지말자!


2차원 배열을 동적할당으로 생성한다고 보면 우선 포인터 3개를 생성한다.

int *(*rows) = new int *[3]; 
//포인터 찍어본결과 임의의 주소값이 들어있다. 


그다음 for문으로 포인터에 접근해 포인터에 배열을 선언해준다.

for (int i = 0; i < 3; i++) {
	rows[i] = new int[5]{};
}

아래 배열과 비슷하게 생성된다.

int rows[3][5] = {};


비슷하다고한 이유는?

메모리주소 형식이 다르다.
위에 선언된 정적2차원배열은 메모리주소가 이어진다.

 

하지만 동적 메모리 할당을 이용하면 메모리 주소가 이어지지 않는다.
위에 말했듯이 포인터도 메모리 주소가 있다.
동적할당 3개를 받은 포인터에 

rows[i] = new int[5]{}; 


의 새로운 포인터주소를 정의해주면 주소가 이어지진않지만 2차원배열이 생성되는걸 알 수 있다.

 

즉, 동적할당된 3개의 포인터에 새로운 배열의 주소를 정의해준다.

 

동적할당은 사용한후 메모리를 반납하는걸 잊지말자!

for (int r = 0; r < row; r++) {
	delete[] rows[r];
}
delete[] rows;

 

#include <iostream>

using namespace std;

int main()
{
	int *ptr = nullptr;
	int **ptrptr = nullptr;

	int value = 5;
	//value의 주소 ptr포인터에 저장
	ptr = &value;
	//ptr포인터의 주소 ptrptr포인터에 저장
	ptrptr = &ptr;

	cout << "==============================================" << endl;
	cout << "다중 포인터 기본예" << endl;
	cout << "value의 주소값 : " << &value << endl;
	cout << endl;

	cout << "ptr포인터의 자신의 주소값 : " << &ptr << endl;
	cout << "ptr포인터에 저장된 주소값 : " << ptr << endl;
	cout << "ptr포인터 de-reference : " << *ptr << endl;

	cout << endl;
	cout << "ptrptr포인터의 자신의 주소값 : " << &ptrptr << endl;
	cout << "ptrptr포인터에 저장된 주소값 : " << ptrptr << endl;
	cout << "ptrptr포인터 de-reference(ptr이 가지고있는 주소) : " << *ptrptr << endl;
	cout << "ptrptr포인터의 double de-reference : " << **ptrptr << endl;
	cout << endl;

	//배열과 포인터는 같다.
	int a[3] = { 1,2,3 };
	int *aa = a;
	cout << "==============================================" << endl;
	cout << "배열과 포인터 비교" << endl;
	cout << "==============================================" << endl;
	cout << "a[3]" << endl;
	for (int i = 0; i < 3; i++) {
		cout << "\t배열안의 값 a : " << a[i] << endl;
		cout << "\t배열의 주소 &a : " << &a[i] << endl;
		//cout << *ptrTest[i] << endl;
	}
	cout << "==============================================" << endl;
	cout << "*aa" << endl;
	for (int i = 0; i < 3; i++) {
		cout << "\t배열안의 값 aa : " << aa[i] << endl;
		cout << "\t배열의 주소 &aa : " << &aa[i] << endl;
		//cout << *ptrTest[i] << endl;
	}
	cout << "==============================================" << endl;

	cout << "1차배열 동적할당" << endl;
	cout << "*ptrTest = new int [3]" << endl;
	int *ptrTest = new int [3];
	for (int i = 0; i < 3; i++) {
		cout << "\t배열안의 주소 &ptrTest[i] : " << &ptrTest[i] << endl;
		cout << "\t배열안의 값 ptrTest[i] : " << ptrTest[i] << endl;
		ptrTest[i] = 3;
		cout << "\t배열안의 값 ptrTest[i] : " << ptrTest[i] << endl;
		//cout << *ptrTest[i] << endl;
	}
	cout << "==============================================" << endl;
	cout << "2차배열 동적할당" << endl;
	cout << "int *(*ptrTest2) = new int *[3]" << endl;
	
	int *(*ptrTest2) = new int *[3];
	cout << "ptrTest2 : " << ptrTest2 << endl;
	for (int i = 0; i < 3; i++) {
		cout << "\t배열안의 주소 &ptrTest2[i] : " << &ptrTest2[i] << endl;
		cout << "\t배열 동작할당 전 ptrTest2[i]: " << ptrTest2[i] << endl;
		//에러가난다
		//cout << "\t배열 동작할당 후 *ptrTest2[i]: " << *ptrTest2[i] << endl;
		//cout << *ptrTest2[i] << endl;
		ptrTest2[i] = new int[5]{};
		cout << "\t배열 동작할당 후 ptrTest2[i]: " << ptrTest2[i] << endl;
		cout << "\t배열 동작할당 후 *ptrTest2[i]: " << *ptrTest2[i] << endl;
	}
	cout << "==============================================" << endl;
	
	const int row = 3;
	const int col = 5;

	int (*arr2)[col] = new int [row][col];
	for (int i = 0; i < row; i++) {
		cout << "상수로 동적할당" << endl;
		cout << &arr2[i] << endl;
		for (int s = 0; s < col; s++) {
			cout << &arr2[i][s] << endl;
		}
	}
	cout << "==============================================" << endl;
	int **arr3 = new int *[row];
	for (int i = 0; i < row; i++) {
		cout << "다중포인터 주소" << endl;
		cout << &arr3[i] << endl;
	}
	cout << "==============================================" << endl;
	for (int i = 0; i < row; i++) {
		cout << "다중포인터 생성" << endl;
		arr3[i] = new int[5]{};
		for (int s = 0; s < col; s++) {
			cout << &arr3[i][s] << endl;
		}

	}
	cout << "==============================================" << endl;
	int arr[row][col] = 
	{ 
	{1,2,3,4,5},
	{},
	{} 
	};
	
	int *(*rows) = new int *[row];
	cout << "rows에 저장된 주소값 : " << rows << endl;
	cout << " *de-reference : " << *rows << endl;
	cout << "&포인터의 주소값 : " << &rows << endl;
	cout << endl;

	
	//초기화
	for (int r = 0; r < row; r++) {
		rows[r] = new int[col];
		cout << "초기화" << endl;
		cout << "&rows[" << r << "] : " << &rows[r] << endl;
		cout << "rows[" << r << "] : " << rows[r] << endl;
		int *a = &rows[r][0];
		cout << "a : " << a << endl;
		cout << "rows[" << r << "][0] : " << rows[r][0] << endl;

	}
	cout << endl;

	cout << "*de-reference : " << *rows << endl;
	cout << "**de-reference : " << **rows << endl;
	cout << "pointer : " << rows << endl;
	cout << endl;

	for (int r = 0; r < row; r++) {
		cout << "&rows["<< r <<"] : " << &rows[r] << endl;
		
		cout << endl;
		for (int i = 0; i < col; i++) {
			rows[r][i] = arr[r][i];
			cout << "&rows[" << r << "]["<< i <<"]" << &rows[r][i] << endl;
			cout << "rows[" << r << "][" << i << "]" << rows[r][i] << endl;
		}

		cout << "===================================" << endl;
	}
	//메모리 제거
	for (int r = 0; r < row; r++) {
		delete[] rows[r];
	}
	delete[] rows;

	//다중배열 사용하지 않기
	int *arrx2 = new int[row*col];
	for (int r = 0; r < row; r++) {
		for (int i = 0; i < col; i++) {
			cout << " i + col * r : " << i + col * r << endl;
			arrx2[i + col * r] = arr[r][i];
		}
	}

	
	for (int r = 0; r < row; r++) {
		for (int i = 0; i < col; i++) {
			cout << arrx2[i + col * r] << " ";
		}
		cout << endl;
	}

	delete[] arrx2;

	return 0;
}

포인터의 개념

- 변수는 그 자체로 자신의 자료형에 맞는 값을 저장한다.


- 포인터(Pointer) 변수는 특이한 변수로, 메모리 주소를 저장한다.


- 포인터는 특정한 변수 자체가 존재하는 메모리 주소값을 가진다.

+ int a = 5; 의 주소값 : 0xAFB03954

  int *b = &a; 의 값 : a의 메모리 주소 0xAFB03954

  int *b 도 변수이기때문에 주소값을 가진다.


- int *b = &a;에서 *(포인터 변수 선언)은 포인터 변수임을 알려주기 위한 목적이다.

  &는 변수 앞에 붙어서 변수의 메모리 시작 주소값을 구한다.

- 따라서 위에 *b는 5라는 값 자체가 된다. 


- *(간접참조연산자) : 현재 포인터가 가르키고있는 주소에 들어있는 값을 참조한다.


 실습내용

실습결과


- 실제로 int a = 5; 와 같이 변수를 할당하면 메모리 주소상에서는 4Byte를 차지한다.

+ 메모리주소를 한칸에 1Byte씩 표현한다면 4칸을 차지한다.


실습내용 : 배열 각 원소의 주소 값 출력하기

십습결과 : 주소값이 4씩 증가하는것을 확인할 수 있다.




포인터의 강력한 기능

- 포인터는 컴퓨터 시스템의 특정한 메모리에 바로 접근할 수 있다.


- 기존에 존재하던 중요한 메모리 영역에 접근하지 않도록 해야한다.

+ int *a = 0x1234123;

  *a = 0;

  이렇게 구현한다면 메모리 0x1234123가 무슨 일을하는지 모르기에 위험하다.


다중포인터

- 포인터의 포인터도 존재할 수 있다.


- 포인터도 메모리에 기록되는변수이다. 


- 포인터를 여러개 겹칠 수 있다.


실습내용

실습결과



배열과 포인터관계

- 배열과 포인터는 사실 동일하다.


- 배열을 선언한 이후에는 그 이름 자체를 포인터 변수처럼 쓸 수 있다.


포인터(핵심)

- 포인터는 특정한 변수가 메모리 상에 존재하는 위치 주소를 저장한다.


- 포인터는 특정한 메모리 주소에 바로 접근할 수 있으므로 조심스럽게 사용해야한다.



+ Recent posts