Server/C++

포인터

Juzdalua 2024. 7. 21. 19:12

주소를 가르키는(저장하는) 포인터 변수

주소의 단위는 정수형 byte.

포인터 변수의 크기는 64bit os에서 8바이트다.

포인터 변수의 크기는 32bit os에서 4바이트다.


포인터 변수의 덧셈은 해당 포인터변수가 가르키는 자료형만큼의 byte를 더한 것과 같다.

포인터 변수의 증감단위는 가르키는 자료형의 크기와 같다.

#include <stdio.h>
#include "common.h"
#include "func.h"

using namespace std;

int g_random = 42;
int main()
{
    int* pInt = nullptr; // 해당 포인터는 아무것도 가르키지 않는다. 값은 0으로 초기화
    pInt += 1;
    printf("%d \n", pInt); // pInt = 4

    return 0;
}

pInt: 주소값

*pInt: 주소에 저장된 값

 

 

포인터 변수를 선언 시, (자료형*)이 의미하는 것은 해당 포인터에게 전달된 주소를 해석하는 단위이다.

#include <stdio.h>

using namespace std;

int main()
{
    int num = 1;
    int* pNum = &num; // 해당 주소에 접근시 int만큼의 크기에 접근하겠다.(4byte)
    printf("%d \n", num); // num = 1

    *pNum = 2;
    printf("%d \n", num); // num = 2

    return 0;
}

배열의 이름은 배역의 시작 주소이다.

int arr[10];
*(arr+1) = 2; // arr[1] = 2와 같다.
printf("%d \n", arr[1]);

상수 값 변경하기

#include <stdio.h>

using namespace std;

int main()
{
    const int num = 1;
    int* pNum = (int*)&num;
    *pNum = 2;
    printf("%d \n", num);

    return 0;
}

 

상수와 포인터

// 포인터 변수가 가르키는 값을 상수화
// 포인터가 가르키는 값을 변경할 수 없다.
const int *p = &a; // const int* p = &a;
// *p = 1 -> ERROR

// 포인터 변수를 상수화
// 포인터 변수를 바꿀 수 없다.
int *const p = &a; // int* const p = &a;
// p = &b -> ERROR
#include <stdio.h>

using namespace std;

int g_random = 42;
int main()
{
    int num = 1;
    int num2 = 2;

    // 포인터의 값 상수화 - 값 고정
    const int* pNum = &num; // int const* pNum = &num
    // *pNum = 2; // 불가능
    pNum = &num2;

    // 포인터 변수 상수화 - 주소값 고정
    int* const pNum2 = &num2;
    *pNum2 = 11;
    // pNum2 = &num; // 불가능

    // 읽기 전용 포인터
    const int* const pNum3 = nullptr;
    
    // 포인터가 아닌 변수 자체는 상수 조건과 무관하다
    num = 100;
    num2 = 200;

    return 0;
}

 

 

 

상수 포인터를 사용하는 이유)

기본적으로 존재하는 변수를 함수에 매개변수로 전달할 경우, 해당 변수의 복사본이 새로 생성되고 함수가 실행된다.

만약 데이터가 엄청 큰 변수라면 이런 행위는 메모리 낭비로 이어질 수 있다.

그래서 원본 데이터가 존재하는 변수의 저장된 주소, 포인터 변수를 매개변수로 넘겨줄 수 있다.

하지만 해당 함수에서 주소에 존재하는 변수 원본 값을 변경할 수 있기 때문에 여기서 문제가 발생한다. 

상수 포인터 변수를 사용하면 수정 불가능한 주소값만을 매개변수로 전달할 수 있다.

하지만 강제 형변환을 하게 되면 수정이 가능하다. (완벽한건 아님)

#include <stdio.h>

using namespace std;

void Test1(int a)
{
    a = 2; // 복사된 변수 값 변경
    printf("TEST1: %d \n", a);
}

void Test2(int* a)
{
    *a = 22; // 원본 데이터 변경

    printf("TEST2: %d \n", *a);
}

void Test3_readonly(const int* a){ // 상수 포인터 변수로 매개변수를 받으면 수정이 불가하다.
    // *a = 1000; // error

    int* test = (int *)a; // 강제 캐스팅하면 상수는 무효된다.
    *test = 1000;
}

int main()
{

    int num = 1;
    Test1(num);
    printf("MAIN1: %d \n", num); // 원본 데이터 변경되지 않음

    int num2 = 2;
    Test2(&num2);
    printf("MAIN2: %d \n", num2); // 원본 데이터 변경됨

    int test = 0;
    Test3_readonly(&test);
    printf("MAIN3: %d \n", test); // 원본 데이터 변경됨

    return 0;
}

void 포인터

자료형이 void로 선언된 포인터 변수는 원본의 자료형을 정하지 않는다.

따라서 모든 자료형의 주소를 받을 수 있다.

하지만 정해진 자료형이 없으므로 제약이 존재한다.

#include <stdio.h>

using namespace std;
int main()
{
    int num = 1;
    float fNum = 1.f;
    void *pNum = &num;
    void *pNum = &fNum;

    *pNum = 3; // 역참조 불가능
    pNum + 1;  // 주소연산 불가능

    return 0;
}

레퍼런스 변수

// 레퍼런스 변수 선언 -> 자료형 &변수명
int a = 1;
int &refA = a;         // == int *const p = &a
refA = 10;             // *p = 10
printf("a: %d \n", a); // a = 10