c++

20210716 구조체 ~ 포인터1

TIN9 2021. 7. 17.
반응형

안녕하세요 틴구입니다!

어제는 빙고게임 코드 분석을 해보았는데요 오늘의 경우는 구조체와 포인터에 대해서 적어보겠습니다!

포인터가 그렇게 중요하고 어렵다던데 걱정이 이만저만이 아니네요 ㅠㅠ

우선

구조체

구조체란 다양한 타입의 변수들을 하나로 묶어서 사용할 수 있게 만들어주는 기능을 제공합니다

사용자 정의 변수 타입을 만들어주는 기능이다.

구조체의  형태는

struct 원하는 구조체 이름

{

    원하는 변수들;

}

의 형태로 이루어져 있습니다.

예를 들어볼까요

struct Monster

{

    char    Name[10];

    int      Attack;

    int      HP;

    int      MP;

}

이런 식으로 사용할 수 있습니다.

 

또 중요한 게 하나 있는데 위에 예시로 적은 구조체 안에 char Name[32];라고 캐릭터 배열을 선언했는데

문자열을 만들어줄 때 문자열의 끝은 반드시 0(Null 문자) 이어야 합니다.

만약에

char    Name[10];

 

Name[0] = 'A';

Name[1] = 'B';

 

std::cout << Name << std::endl;

이라고 코드를 출력하면

AB儆儆儆儆儆儆儆FJ?\?의 코드가 출력이 됩니다

뒤의 값들은 쓰레기 값으로 위에서 설명한 것처럼 문자열의 끝을 0으로 끝내거나 초기화를 해서 사용해야 합니다.

ex)

char    Name[10];

 

Name[0] = 'A';

Name[1] = 'B';

Name[2] = 0;

 

ex)

char    Name[10] = {};

 

Name[0] = 'A';

Name[1] = 'B';

 

이런 식으로 말이죠

 

또 구조체의 경우 코드 블록의 모든 구성을 하나의 원하는 구조체 이름으로 묶는 개념인데

그 구조체가 구조체만의 변수 타입이 된다.

그래서

Moster Monster = {};

구조체의 Monster변수 타입으로 Monster란 변수를 만들 수 있고

구조체 안의 모든 변수들을 0으로 초기화를 할 수 있다.

Monster의 경우 변수 타입이니깐

Monster MonsterArr[100] = {}; 의 배열 생성도 가능합니다.

구조체 타입으로 변수를 만들었을 때 구조체 내의 변수들에 대해서 접근하기 위해서는 . 을 이용해야 한다

ex)

Monster.Attack = 1000;

Monster.HP = 5000;

Monster.MP = 1000;

의 형태로 사용이 가능하다

배열이 인덱스를 이용해서 내가 원하는 요소에 접근을 했다면

구조체는 내가 원하는 요소에 접근할 때 구조체 타입의 변수를 만들어 놓고 .을 이용해서 접근을 할 수 있다.

구조체의 특성상 게임을 만들 때 엄청 많이 활용된다고 하니 꼭 알아두자.

포인터

포인터란 메모리에 공간을 만들면 반드시 주소가 부여되고 모든 변수 타입은 포인터 타입을 가질 수 있습니다

포인터 타입의 변수는 해당 타입의 메모리 주소를 가질 수 있습니다.

만약 int Number;를 선언했을 때 주소가 1000번지라면 int의 포인터 타입 변수에 Number의 주소인

1000번지를 저장할 수 있는 것이다.

포인터 변수 선언 방법

 

변수타입 *포인터이름;

으로 선언을 할 수 있고

변수 이름 앞에 & 을 붙여주면 해당 변수의 메모리 주소가 된다.

 

int타입의 주소는 int포인터 타입의 변수에 저장할 수 있다.

서로 타입이 다르다면 주소를 저장할 수 없는데 형변환을 통해서 저장은 가능하다.

ex)

int Number = 100;

__int64 Number64 = 3828;

 

__int64 * pNumber64 = &Number64;

int *pNumber = &Number;

 

pNumber = (int*)pNumber64;    ----  O

pNumber = pNumber64;  ----- X

 

또 역참조라는 것이 있는데 역참조는 포인터 변수 앞에 *을 붙여서 해당 포인터 변수가 가지고 있는

주소에 접근하여 값을 변경할 수 있다.

ex)

*pNumber = 3000;

 

std::cout << Number << std::endl;

의 값을 출력해 보면 위에서 int Number = 100;이라고 선언을 했지만

역참조 *pNumber = 3000;에 의해 Number의 값이 3000으로 바뀌게 된다.

 

x86으로 개발을 하게 되면 32bit 시스템으로 개발하는 것이고
x64로 개발을 하게 되면 64bit 시스템으로 개발하는 것이다.

32bit에서는 메모리 주소가 16진수 8자리로 표현되고 64bit 에서는
메모리 주소가 16진수 16자리로 표현이 된다.

그래서 포인터 타입의 변수는 메모리 주소를 저장하는 변수이므로 어떤 타입의
포인터 변수라도 무조건 32bit에서는 4바이트 64bit에서는 8바이트만큼의
공간을 차지하게 되는 것이라고 합니다.

ex)

std::cout << sizeof(int*) << std::endl;
std::cout << sizeof(__int64*) << std::endl;
std::cout << sizeof(char*) << std::endl;
std::cout << sizeof(bool*) << std::endl;

 x86 32비트로 출력을 하게 되면

4

4

4

4가 출력 이 되는데

x64 64비트로 출력을 하게 되면

8

8

8

8로 출력이 됩니다.

포인터와 배열의 관계

배열명은 포인터라고 할 수 있습니다. 배열의 이름은 해당 배열이 할당된 메모리의 시작 주소를 의미합니다.

ex)

int Array[10] = {};

 

std::cout << "Array start address : " << Array << std::endl;

을 출력하게 되면

Array start address : 005CFC48

이 출력되게 됩니다

Array가 해당 배열의 시작 주소이므로 이를 포인터 변수에 저장할 수도 있습니다.

ex)

int* pArray = Array;

 

pArray[2] = 3030;

이런식으로 Array 배열의 값을 바꿀 수도 있습니다.

 

*(pArray + 2) = 4040;

이런식으로도 바꿀 수 있습니다.

std::cout << Array[2] << std::endl; 를 출력해보면

4040이 출력되게 된다.

그리고 문자열도 포인터로 선언이 가능합니다

""안에 문자열을 적어주면 const char* 타입으로 인식이 된다고 합니다. 

자세한 내용은 다음에 알려주신다고 해서 우선은 이렇게도 할 수 있다는 것을 알고 있자고요.

cont char* pText = "문자열 예제";

 

std::cout << pText << std::endl;

을 출력하면 "문자열 예제"이 출력되게 된다.

포인터 연산

마지막으로 포인터도 포인터만의 연산법이 있는데

포인터 연산의 경우 +, - 2가지 만을 제공합니다.

포인터 연산의 경우 1을 더한다고 해서 무조건 메모리 주소가 1이 증가하는 개념이 아니라

포인터 변수에 1을 더해주게 되면 해당 변수 타입의 크기만큼 증가하게 됩니다.

만약에 위에서 설정한

pArray의 주소가 006FF8F8이라고 한다면

pArray + 1 = 006FF8FC가 되게 됩니다.

pArray + 2 = 006FF900가 됩니다.

반응형

댓글