관리 메뉴

기억을 위한 기록들

[Fundamental C++] 4. 포인터 본문

C & CPP/Fundamental C++

[Fundamental C++] 4. 포인터

에드윈H 2021. 4. 27. 17:32

- 메모리의 주소를 가리키는 객체를 포인터라고 부른다.

- 메모리 블록은 주소, 크기, 상태의 정보를 가지고 있다.

- 메모리 블록을 쉽게 관리할 수 있도록 포인터에는 주소뿐 아니라 타입 정보까지 들어가게 되었다.

   타입 정보를 통해 메모리 주소로부터 타입 크기만큼 블록에 값을 읽거나 쓸 수 있게 된 것이다.

 

 

포인터 타입

- int 타입 객체를 기리 키기 위해 int* 타입 포인터를 사용하는 것이 쉽게 편해서 장점이 많다는 것이지, 반드시 int* 을 사용해야만 하는 것은 아니다. 필요에 따라 다른 타입이 int타입을 가리킬 수도 있다.

 

같은 경우

int a = 0;
int* ptr = &a;
*ptr = 3; //역참조 값 변경 

std::cout<< a << std::endl; //3 출력

 

다른 경우

int a = 0;
char* ptr = &a; // 컴파일 에러!
char* ptr = (char*)&a; //성공

타입이 다르기에 컴파일 에러가 발생하게 되고, 강제 타입 변환으로 성공하였다. 이어서 같은 경우에서와 같이 역참조를 해줄 경우

int a = 0;
char* ptr = &a; // 컴파일 에러!
char* ptr = (char*)&a; //성공

*(ptr + 0) = 'A';
*(ptr + 1) = 'B';
*(ptr + 2) = 'C';
*(ptr + 3) = '\0';

char는 1바이트 단위로만 메모리 블록에 값을 쓸 수 있다. 따라서 a가 가리키는 4바이트(int) 메모리 블록에 각각 값을 쓰기 위해서 (ptr + 0), (ptr + 1), (ptr + 2), (ptr + 3)으로 4번에 걸쳐서 메모리 블록에 접근해야 한다.

 

- 각각 0, 1, 2, 3을 더했는데 해당 바이트만큼 이동한다는 의미가 아니라 sizeof(char) 크기의 0 배수, 1 배수, 2 배수, 3 배수만큼씩 이동한다는 의미이다. 그리고 각각의 1바이트 블록에 A, B, C,\0을 대입했다.  마지막 \0을 문자열 종료 표시이다.

0x0133FE5C는 변수 a의 주소값 41부터 차례대로 들어간것을 확인할 수 있다.

 

	int a = 0;
	//char* ptr = &a; // 컴파일 에러!
	char* ptr = (char*)&a; //성공

	*(ptr + 0) = 'A';
	*(ptr + 1) = 'B';
	*(ptr + 2) = 'C';
	*(ptr + 3) = '\0';

	cout << (char*)&a << endl; //ABC 출력
	cout << *ptr << endl; //A 출력 

 

- 예제를 통해서 확인할 수 있듯이 포인터의 원시 타입은 포인터가 가리키는 메모리 주소로부터 타입 크기만큼의 블록을 타입 기준으로 읽고, 쓰기 위해서 사용된다.

즉, 어떤 TYPE* ptr 이 나타내는 의미는 ptr가 가리키는 주소를 기준으로 sizeof(TYPE) 크기의 블록의 비트 상태를 TYPE에 의해서 의미(값)를 부요하여 대응시키겠다는 것이다. 따라서 ptr를 통해 해당 블록의 값을 읽거나 쓸 수 있다.

 

배열 포인터 타입

- typedef를 이용한 배열의 포인터 타입 나타내기.

#include <iostream>
using namespace std;

typedef int(TARR)[2];
typedef int(*PARR)[2];

int arr[2] = { 1,2 };
int main()
{
	TARR* ptr = &arr;
	PARR ptr2 = &arr;

	cout << (*ptr)[0] <<", "<<(*ptr)[1]<< endl;
	cout << (*ptr2)[0]<<", " <<(*ptr2)[1]<< endl;	
	
	return 0;
}

출력 결과

- typedef 사용하지 않고 배열의 포인터 타입 나타내기

#include <iostream>
using namespace std;

template<typename T>
void ArrayPrint(T arg)
{
	cout << (*arg)[0] <<", "<< (*arg)[1] << endl;
}
int main()
{
	int arr[2] = { 3,4 };
	ArrayPrint<int(*)[2]>(&arr);


	return 0;
}

 

 

- 함수의 포인터 타입 살펴보기

#include <iostream>
using namespace std;

int DoSomething(int arg)
{
	return arg;
}

template<typename T>
void CallFunc(T arg)
{
	cout << arg(15)<< endl;
}

int main()
{
	CallFunc<int(*)(int)>(DoSomething);

	return 0;
}