ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Pointer] 정수형을 문자형으로
    개발/C·C++ 2021. 5. 28. 22:28

    정수형 포인터 변수를 초기화할 때 정수형 변수의 주소값을 넣는 이유는, 메모리에서 데이터를 읽는 간격이 자료형의 크기로 결정되기 때문입니다. int 포인터 변수 p++p 연산을 하면 4바이트를 이동하고, short 포인터 변수 s++s 연산을 하면 2바이트를 이동합니다. 컴파일 단계에서, 메모리 크기 단위를 포인터의 타입으로 파악할 수 있습니다. 때문에 포인터의 타입을 다른 타입으로 변환하다고 해서, 역참조하고 있는 내용이 바뀌는 건 아닙니다.

     

    아래 예제를 봅시다. s1에는 260, s2에는 2를 할당했습니다. 구초제 포인터의 타입 캐스팅을 통해 Family1의 객체인 f1의 주소값을 Family2 포인터 변수에 넣었습니다. f1short 변수들은 f2char 배열에 할당됩니다. 260인 s1는, ch 배열의 0, 1 인덱스에 각각 4, 1의 형태로 들어갔습니다. 2바이트 short 정수를 1바이트 char 자료형으로 표현한 것입니다. 풀어 보면 (256^0) * 4 + 256(256^1) * 1입니다. s2의 2는 2, 0으로 표현됩니다. 1(256^0) * 2 + 256(256^1) * 0인 것이지요. 여기에선 short와 char 사이에서 직접 포인터 타입을 변환하지 않았지만, 이를 멤버 변수로 가진 구조체 포인터의 형을 변환, 대입하여 멤버 변수의 타입이 달라도 메모리에 문제 없이 데이터가 들어간다는 것을 보여줍니다.

     

    두 번째 예제는 포인터 변수의 타입이 메모리를 어떤 간격으로 읽는지를 나타냅니다. int형 배열에서는 a[3]과 a[13]이 10이라는 간격을 가지지만, 이를 char 포인터로 변환하면 40 간격이 됩니다.

    #include <iostream>
    using namespace std;
    
    struct Family1
    {
        unsigned short s1;
        unsigned short s2;
    };
    
    struct Family2
    {
        unsigned char ch[6];
    };
    int main()
    {
        Family1 f1;
        f1.s1 = 260;
        f1.s2 = 2;
    
        Family2* f2 = reinterpret_cast<Family2*>(&f1);
        cout << static_cast<unsigned short>(f2->ch[0])          // 4
             << static_cast<unsigned short>(f2->ch[1]) << endl; // 1
        cout << static_cast<unsigned short>(f2->ch[2])          // 2
             << static_cast<unsigned short>(f2->ch[3]) << endl; // 0
    
        cout << endl;
    
        int a[20] = { 0 };
        int* p = &a[3];
        int* q = &a[13];
        ptrdiff_t diff1 = q - p; // This is 10
        cout << diff1 << endl;    
    
        char* x = (char*)p;
        char* y = (char*)q;
        ptrdiff_t diff2 = y - x; // 40. This is 10 times sizeof(int)
        cout << diff2 << endl;
    }

    윈도우에서 리스닝 소켓을 생성하고 바인드를 하기 전에 포트, 아이피 주소 등을 설정해주는데, 여기에도 위와 비슷한 과정이 있습니다.

    SOCKADDR_IN addr_in;
    memset(&addr_in, 0, sizeof SOCKADDR_IN);
    addr_in.sin_family = AF_INET;
    addr_in.sin_port = htons(server_config_->port_);
    addr_in.sin_addr.s_addr = htonl(INADDR_ANY);
    
    if (bind(listen_socket_, (sockaddr*)&addr_in, sizeof addr_in) == SOCKET_ERROR)
    {
        // do something
    }        

    bind 함수의 두 번째 인수는 sockaddr*이지만, SOCKADDR_IN 객체를 만들어 주소값을 sockaddr*로 캐스팅해서 넘깁니다. 포트랑 아이피를 각각 USHORT, ULONG으로 넣는데, 이를 개발자가 sockaddr에 있는 char형 배열에 일일이 넣는 것은 다소 번거롭습니다. 작업하기 편하게 SOCKADDR_IN 객체에 데이터를 넣어서 포인터 형 변환을 해서 전달하면 됩니다. 그러면 bind 함수가 내부적으로 자료형의 크기만큼 참조하겠죠.

    typedef struct sockaddr_in {
    
    #if(_WIN32_WINNT < 0x0600)
        short   sin_family;
    #else //(_WIN32_WINNT < 0x0600)
        ADDRESS_FAMILY sin_family;
    #endif //(_WIN32_WINNT < 0x0600)
    
        USHORT sin_port;
        IN_ADDR sin_addr;
        CHAR sin_zero[8];
    } SOCKADDR_IN, *PSOCKADDR_IN;
    /////////////////////////////////////////////////////////////////////////////
    typedef struct sockaddr {
    
    #if (_WIN32_WINNT < 0x0600)
        u_short sa_family;
    #else
        ADDRESS_FAMILY sa_family;           // Address family.
    #endif //(_WIN32_WINNT < 0x0600)
    
        CHAR sa_data[14];                   // Up to 14 bytes of direct address.
    } SOCKADDR, *PSOCKADDR, FAR *LPSOCKADDR;

    >>참고 사이트

    https://stackoverflow.com/questions/39854610/casting-int-pointer-to-char-pointer

    '개발 > C·C++' 카테고리의 다른 글

    IOCP(2)  (0) 2021.06.04
    [Design Pattern] Observer  (0) 2021.05.30
    IOCP(1)  (0) 2021.05.25
    [Rvalue reference] Rvalue Reference(3)  (0) 2021.05.21
    [Rvalue reference] Move Semantics(2)  (0) 2021.05.12

    댓글

Designed by Tistory.