포인터와 참조를 통해 구조체나 클래스의 멤버에 접근할 수 있다.

포인터는 ->를 사용하여 멤버에 접근한다.
(*포인터명)으로 접근할 수 있으나 잘 사용하지 않는다.

	Person *ptr = &person;
	ptr->age = 30;
	(*ptr).age = 40;


참조는 기존 구조체,클래스와 같이 사용할 수 있다.
참조변수명.멤버 형식으로 사용가능하다.

	Person &ref = person;
	ref.age = 20;


참조에 포인터를 de-reference해서 값을 넣어줄 수 있다.

	Person ref2 = *ptr;
	ref2.age = 50;

포인터,참조사용 멤버접근 cout찍어보기

#include <iostream>

using namespace std;

struct Person {
	int age;
	double weight;
};

int main()
{
	Person person;
	cout << "addr : " << &person << endl;
	//멤버 선택 연산자
	person.age = 10;
	

	//참조 주소값 가져오기
	Person &ref = person;
	ref.age = 20;
	cout << "addr ref : " << &ref << endl;

	Person *ptr = &person;
	cout << "addr &ptr : " << &ptr << endl;
	cout << "addr ptr : " << ptr << endl;
	ptr->age = 30;
	//()를 꼭 해줘야한다.
	(*ptr).age = 40;

	Person ref2 = *ptr;
	cout << "addr ref2 : " << &ref2 << endl;
	ref2.age = 50;

	return 0;
}

동적 할당 배열 Dynamically Allocation Arrays

동적 할당 배열은 메모리를 나중에 할당 받을 수 있다.
배열에 들어갈 값을 미리 정해놓을 수 있지만 할당받을 크기보다 크다면 에러가 난다.

동적 할당 배열 반납방법 :  delete[]

#include <iostream>

using namespace std;

int main()
{
	//정적 할당 배열은 컴파일될때 배열의 크기가 고정되어야한다.
	const int length = 5;
	//정적 할당 배열
	int arr[length] = { 1,2,3,4,5 };
	
	int dlen;
	cin >> dlen;

	//동적 할당 배열 받기
	int *ptrArr = new int[dlen];

	for (int i = 0; i < dlen; i++) {
		cout << "i : " << i << endl;
		ptrArr[i] = i;
		cout << (uintptr_t)&ptrArr[i] << endl;
		cout << ptrArr[i] << endl;
	}

	delete[] ptrArr;
	ptrArr = nullptr;

	return 0;
}

포인터에도 const를 사용할 수 있다.

 

const의 위치마다 결과가 다르다.


주소에 접근해 데이터를 변경할 수 없다.

const int *ptr = &value; 


메모리 주소를 변경할 수 없다.

int *const ptr = &value; 


메모리주소,주소안의 데이터를 모두 변경할 수 없다.

const int *const ptr = &value;

const활용 자세한 설명

#include <iostream>

using namespace std;

int main()
{

	int value = 5;
	

	const int *ptr2 = &value;
	cout << *ptr2 << endl;
	//역참조는 불가능하다.
	//포인터를 이용해 값을 변경하지 않겠다는 표현으로 생각할 수 있다.
	//*ptr = 6;


	int value2 = 3;
	//주소에 있는 값을 바꾸지 않겠다는 말이다.
	ptr2 = &value2;
	cout << *ptr2 << endl;
	//에러가난다.
	//*ptr2 = 6;

	//포인터 자체를 상수로 만들기
	int *const ptr = &value;
	//주소값을 변경할 수 없다.
	//ptr = &value2;

	//모든것이 변경 불가능하다.
	const int *const ptr3 = &value;
	return 0;
}

포인터와 정적배열의 관계를 이해하면 포인터의 성질을 이해할 수 있다.
포인터와 배열은 매우 비슷하다. 
배열이 편의성기능이 몇가지 추가되어있다.
ex) 배열은 sizeof해보면 배열은 총 Byte가 출력된다. 포인터는 포인터의 Byte가 출력된다.

array변수는 포인터와 비슷하다. 배열의 첫번째 주소를 담고 있다.

	int arr[5] = { 9,7,5,3,1 };
	//배열의 첫번째 주소가 출력된다.
	cout << arr << endl;


확인방법 : de-reference 해보면 배열의 첫번째 데이터가 출력된다.

문제가 되는부분
함수 파라미터로 배열을 넘겨줄때 포인터로 넘어간다.
확인방법 : sizeof()를 활용해보면 포인터의 Byte가 출력된다.

함수안에서 포인터로 직접접근하여 값을 변경할 수 있다.

void printArray(int arr[]) {
	//(생략)...
	//직접접근하여 값변경하기
	*arr = 1000;
}

배열이 struct나 class에 들어가있으면 포인터로 변환되지 않는다.

#include <iostream>

using namespace std;

void printArray(int arr[]) {
	cout << "======" << endl;
	cout << arr << endl;
	cout << *arr << endl;
	cout << sizeof(arr) << endl;

	//직접접근하여 값변경하기
	*arr = 1000;
}

struct A
{
	int arr[3] = { 1,2,3 };
};

void doSomethig(A a) {
	//포인터로 변환되지 않는것을 확인할 수 있다.
	cout << sizeof(a.arr) << endl;
}

int main()
{
	int arr[5] = { 9,7,5,3,1 };

	cout << arr << endl;
	cout << *arr << endl;
	cout << sizeof(arr) << endl;
	//같은 주소를 보고있는지 확인하기
	int *ptr_arr = arr;
	cout << ptr_arr << endl;
	cout << *ptr_arr << endl;
	cout << sizeof(ptr_arr) << endl;

	char name[] = "choi";
	cout << *name << endl;
	printArray(arr);
	printArray(ptr_arr);
	cout << arr[0] << "   " << *arr << endl;
	
	cout << "======" << endl;

	A a;
	cout << a.arr[0] << endl;
	cout << sizeof(a.arr) << endl;
	
	doSomethig(a);

	return 0;
}

 

 

 

포인터는 c,c++중요한 특징이다.

컴퓨터 내부에서 작동하는 방법 즉, CPU와 메모리의 협력방법을 알 수 있다.

포인터의 기본적인 사용법

변수를 선언할때 변수가 사용할 공간을 os로 부터 빌려온다.
이때 변수는 메모리의 주소를 가르키고 있고 주소에 접근 하여 데이터를 가져온다.

 

또 지역변수는 스택 영역을 사용하고 동적할당메모리는 힙 영역을 사용한다.

변수명 앞에 &(adrress-of operator)을 사용하면 주소값을 알 수 있다.

그 메모리주소를 담는 변수를 포인터라고 부른다.

변수를 사용할때 *(de-reference operator)는 포인터가 가르키고 있는것에 대해
직접적으로 접근하는 것을 말한다.  

간단하게 생각하자! 포인터는 그냥 메모리 주소를 담는 변수이다.
주소값을 직접적으로 입력하는것은 가능하지 않다.

int *ptr_x = &x;


형식으로 선언한다.

함수가 리턴타입으로 포인터를 가질 수 있다.

포인터를 선언하는 이유
기본적인 이유는 array때문에 사용한다.
함수에 array가 파라미터로 들어가면 복사가 된다.
100만개라고 생각한다면 복사를 하면 엄청 느려진다.

포인터로 주소와 데이터의 갯수만 알려주면 속도를 줄일 수 있다.

다른언어들에서도 내부적으론 포인터를 사용하고 있다.
사용자정의자료형에서도 포인터를 사용할 수 있다.
포인터의 주소는 비트의 값다를때 크기만 다르지 저장하는 값은 고정이다.

 

#include <iostream>
#include <typeinfo>

using namespace std;

int* nullPoint(int* a)
{
	return nullptr;
}

struct A {
	int a, b, c;
};

int main() {

	int x = 5;

	cout << x << endl;
	cout << &x << endl;
	cout << *(&x) << endl;

	//포인터 선언
	//변수의 주소를 포인터에 넣어준다.
	//데이터 타입과는 상관없이 중립적이다.
	//다만 de-reference할때 타입을 알아야하기에 타입을 정해준다.
	int *ptr_x = &x;

	cout << ptr_x << endl;
	//de-reference 가리키고있는 메모리안에있는 데이터를 가져온다.
	cout << *ptr_x << endl;
	//포인터임을 확인 할 수 있다.
	cout << typeid(ptr_x).name() << endl;

	A a;
	A *ptr_a;

	//12Byte이다.
	cout << sizeof(a) << endl;
	//포인터는 4Byte이다.
	cout << sizeof(ptr_a) << endl;

	return 0;
}

'개발 소발 > 개발 C++(기초)' 카테고리의 다른 글

c++ 포인터와 정적배열  (0) 2019.07.25
c++ 널 포인터 Null Pointer  (0) 2019.07.25
c++ 정적 다차원 배열  (0) 2019.07.25
c++ 배열 선택정렬,버블정렬  (0) 2019.07.24
c++ 배열 array 배열반복문사용  (0) 2019.07.24

포인터의 개념

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


- 포인터(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