기초CS-포인터 1

킴스코딩클럽·2022년 10월 12일
1

CS기초 시리즈

목록 보기
30/71

포인터(pointer)의 기본 개념

  • 포인터 변수
  • 실제 메모리에 다양한 변수들이 있음
  • 변수의 어디있느냐를 가리키는 용도
  • C 언어의 가장 큰 장점 중에 하나로 사용됨(속도가 빠름)
  • 다루기 어려운 단점이 있음
  • 게임 개발에 필수적임(속도 때문)
  • 다른 언어를 배우는 것에 도움
  • 이해 안되면 다시 처음으로 돌아와서 공부해보기

메모리 공간은 일차원 배열
메모리 배열의 원소는 1byte씩 할당
int를 할당하면 1,2,3,4개의 공간을 할당하는 것임
(0은 다른 용도로 사용하기 위해서 빼 놓음)
이것을 주소라고 부름(메모리 주소-address)
변수의 주소에 접근하는 개념이 바로 포인트


포인터 연산자

포인터와 관련된 연산자(operator): 모두 단항 연산자

  • 주소 연산자 &(ampersand) : 우측 변수의 주소를 구해옴
    타입 선언

포인터 타입 *(asterisk)

  • 우측 변수가 주소를 가리키는 용도라는 것을 명시
  • 주소를 다룰 수 있는 변수를 만드는 것
    cf) #(hash)

int x{10};
	int	*p; //int* p;도 가능함
	p = 19;
	// 타입이 맞지 않는 다는 에러
	// int의 포인터 타입(int*)과 int는 서로 다른 타입
	// int 포인터 타입은 주소만 들어갈 수 있음 (정수는 들어갈 수 없음)
	p = &x;
	// x의 주소를 만드는 구문
	// x라는 변수에는 값 10이 들어가고 
	// p라는 변수에는 x의 주소값(0x~)가 들어가는 것


	std::cout << &x << std::endl; 

	std::cout << &p<< std::endl;
	//바이트단위의 주소가 나옴 0x~~~~
	//위 두줄의 결과값 000000D9E20FFBC4(x) 000000D9E20FFBE8(p) : 서로 다른 곳엠 만들어 짐
	std::cout << p << std::endl;
	//출력하면 x의 주소값 000000D9E20FFBC4이 출력됨
	//포인터 p는 x라는 변수의 주소를 가리키는 것
   

포인터 변수

  • 참조: p라는 포인터 변수가 x를 참조하고 있는 것

역참조(de-reference)

  • 참조하고 있는 변수의 값을 꺼내오는 것 - 들어가서 안에 있는 것을 알 수 있음

역참조 연산자 *

  • 우측 변수가 가지고 있는 주소 안에 들어 있는 값에 접근하는 것
	int a{ 1 }, b{ 2 };
	int* p;
	p = &a; 
	std::cout << *p << std::endl; //역참조 : 결과는 a의 값이 1

	*p = 10;
	//a의 값이 p를 통해서 10에서 1로 바껴짐
	//p가 가르키는 대상이 a이므로
	std::cout << a << std::endl; //결과 10
    
왜 포인터가 어려운가?? 
참조와 역참조의 관계를 생각해보자
	x * y; //x곱하기 y
	int* p; //포인터 변수만 가능한 참조
	*p;//포인터 변수 역참조(안으로 들어가서 값에 접근)
    
 int x{ 2 }, y{ 2 };

	int *p1{ &x }, *p2{ &y };

	std::cout << *p1 * *p2 << std::endl;  // 4
 
 

int x{ 1 };
	char c{ 'a' };
	int* p;
	p = &x;
	p = &c; 
	//p라는 변수는 int를 가리킨다는 의미 하지만 위 문장은 char
	char* p2;
	p2 = &c;

포인터의 크기는 고정이다
--
x64 : 8바이트
x86 : 4바이트

int * 변수;
포인터 변수의 선언에 사용된 타입은 역참조를 위해서 반드시 필요한 정보(값을 가져오기 위해서 타입의 정보를 알아야함)
반드시 가리킬 대상의 타입을 써줘야함

   int x{ 1 };
	char c{ 'a' };
	int* p;
	p = &x;
	p = &c; 
	//p라는 변수는 int를 가리킨다는 의미 하지만 위 문장은 char
	char* p2;
	p2 = &c;
	//둘 다 크기는 8바이트(포인터 변수는 크기가 고정됨 : 메모리의 주소가 들어오기 때문)
	//레지스터의 크기와 동일(32비트라면 4바이트)
	//왜 가리키는 대상의 타입을 써줘야하나요? 크기가 동일한데
	//메모리는 단순히 바이트로 나열된 배열 그런데 주소를 저장하면 주소밖에없는데
	//역참조를 해서 들어가려면 현재 가지고 있는 주소에서 몇 개까지 바이트를 엮어서 값으로 인식해야하는지 포인터가 알아야함
	//그렇기 때문에 타입의 정보가 들어가게 되는 것
 

포인터의 연산
--

  • 더하기 빼기만 가능함
  • 포인터의 변수가 가리키는 타입만큼 좌우로 이동하는 것
   int x{ 1 };
	std::cout << x + 1 << std::endl;
	// 변수를 만들면 연산이 가능

	int* p{ &x };
	std::cout << p << std::endl;
	std::cout << p + 1 << std::endl;
	//포인터 변수도 변수라서 연산이 가능
	//결과값 : 00000093438FFC64
	//00000093438FFC68
	//4바이트 차이 : int(4바이트)를 가리키는 포인터이기 때문
    	//+1의 의미 int가 한개 건너뛴다의 느낌
    	//그래서 포인터가 가리키는 타입이 필요
       //곱하기 나누기는 안됨 : 정확히 나올 수 없기 때문

포인터의 기본값 (nullptr)
--

int main()
{
	int x{}; // 기본값 0 {0};

	int* p{}; // 주소의 0번을 가리킴 그래서 0 : {0};
}
void MyFunc(int x)
	{

	}

	void MyFunc(int* x)
	{

	}

	int main()
	{
		MyFunc(nullptr); //포인터의 기본값
	}

	//함수의 모호함
	//그래서 cpp에서 포인터의 기본값으로 
	nullptr = Null Pointer 

타입이 없는 포인터 (void *)
--

  • 누구나 가리킬 수 있다

  • 역참조 불가능함 (타입을 모르기 때문)

  • 포인터의 연산도 불가능함(타입의 크기만큼 연산하기 때문)

  • 프로그래머가 명시적으로 변환하기는 가능함

    int main()
    {
    int x{ 1 };
    
    	void* p;
    
    	p = &x;
    
    	std::cout << *p; 
    	//가리키는 것은 가능하지만 역참조가 불가능함
    	std::wcout << *(int*)p << std::endl; 
    	//void포인터를 int포인터로 변환해서 역참조해서 들어감
    }

포인터의 다양한 용도

int main()
{
	int x{ 1 };

	void* p;

	p = &x;

	char r = *(char*)p;
	char g = *((char*)p + 1);

	p = (char*)&x;

	char r = *p;
}
profile
공부 기록용

0개의 댓글