무엇 uintptr_t
이며 어떤 용도로 사용할 수 있습니까?
무엇 uintptr_t
이며 어떤 용도로 사용할 수 있습니까?
답변:
uintptr_t
데이터 포인터를 저장할 수있는 부호없는 정수 유형입니다. 일반적으로 포인터와 크기가 같습니다.
선택적으로 C ++ 11 이상 표준에 정의되어 있습니다.
아키텍처의 포인터 유형을 보유 할 수있는 정수 유형을 원하는 일반적인 이유는 포인터에 정수 특정 연산을 수행하거나 포인터를 정수 "핸들"로 제공하여 포인터 유형을 모호하게하기 위해서입니다.
편집 : Steve Jessop은 당신에게 pedantic 유형에 대한 또 다른 대답에 매우 흥미로운 추가 세부 사항 (내가 훔치지 않을 것입니다)이 있음에 유의 하십시오 :)
size_t
만하면 가장 큰 개체의 일 크기를 유지하기에 충분하고, 포인터보다 작을 수 있습니다. 이것은 8086 (16 비트 size_t
, 32 비트 void*
) 과 같은 세그먼트 아키텍처에서 예상됩니다.
ptrdiff_t
. uintptr_t
그런 의미가 아닙니다.
unsigned int
일반적으로 충분히 크지 않습니다. 그러나 충분히 클 수 있습니다. 이 유형은 모든 "가정" 을 제거하기 위해 특별히 존재합니다 .
첫 번째 질문은 uintptr_t
C ++에 없었습니다. <stdint.h>
선택적 유형으로 C99에 있습니다. 많은 C ++ 03 컴파일러가 해당 파일을 제공합니다. 또한 C ++ 11에 <cstdint>
있으며 여기서 다시 선택 사항이며 정의를 위해 C99를 나타냅니다.
C99에서는 "void에 대한 유효한 포인터를이 유형으로 변환 한 다음 다시 void로 포인터를 변환하여 결과를 원래 포인터와 비교할 수있는 속성이있는 부호없는 정수 유형"으로 정의됩니다.
이것을 말하는 것을 의미합니다. 크기에 대해서는 아무 말도하지 않습니다.
uintptr_t
와 같은 크기 일 수 있습니다 void*
. 더 클 수도 있습니다. 비록 그러한 C ++ 구현이 어설프게 접근하지만, 더 작을 수도 있습니다. 예를 들어 void*
32 비트이지만 24 비트의 가상 주소 공간 만 사용되는 일부 가상 플랫폼에서는 uintptr_t
요구 사항을 충족 하는 24 비트 를 가질 수 있습니다 . 구현이 왜 그렇게하는지 모르겠지만 표준에서는 허용합니다.
void*
. 그러나 변환 된 포인터가 아닌 실제로 정수 핸들 인 것을 사용하도록 변경하려는 경우 가능한 미래 방향에 영향을 미칩니다.
typedef struct { int whyAmIDoingThis; } SeriouslyTooLong; SeriouslyTooLong whyAmNotDoneYet; whyAmINotDoneYet.whyAmIDoingThis = val; callback.dataPtr = &whyAmINotDoneYet;
. 대신 : callback.dataPtr = (void*)val
. 다른 한편으로, 당신은 물론 void*
그것을 다시 캐스팅해야합니다 int
.
정확하게 포인터 크기의 부호없는 정수 유형입니다. 예를 들어 모든 비트를 뒤집어서 (이유를 묻지 않는 등) 포인터를 사용하여 비정상적인 작업을 수행해야 할 때마다 uintptr_t
일반적인 정수로 캐스트 하고 조작 한 다음 다시 캐스트하십시오.
void*
포인터 값을 uintptr_t
다시 변환 void*
하면 원래 포인터와 동일한 값을 산출 한다는 것입니다. uintptr_t
일반적으로와 같은 크기 void*
이지만 보장되지는 않으며 변환 된 값의 비트가 특정 의미를 지니고 있다는 보장도 없습니다. 그리고 정보 손실없이 변환 된 포인터 대 함수 값을 보유 할 수 있다는 보장은 없습니다. 마지막으로, 존재한다는 보장은 없습니다.
"uiintptr_t 데이터 유형은 무엇입니까"부분에 대한 많은 좋은 답변이 이미 있습니다. "어떻게 사용할 수 있습니까?" 이 게시물의 일부입니다.
주로 포인터의 비트 단위 작업에 사용됩니다. C ++에서는 포인터에 대해 비트 단위 연산을 수행 할 수 없습니다. C의 포인터에서 비트 단위 연산을 수행 할 수없는 이유는 무엇입니까?를 참조하십시오 .
따라서 포인터에 대해 비트 단위 연산을 수행하려면 포인터를 unitpr_t 유형으로 캐스팅 한 다음 비트 단위 연산을 수행해야합니다.
다음은 XOR 연결 목록에 저장하기 위해 비트 전용 또는 2 개의 포인터로 작성하는 함수의 예입니다. 우리는 이중 연결 목록처럼 양방향으로 순회 할 수 있지만 각 노드에 2 개의 포인터를 저장하지 않아도됩니다. .
template <typename T>
T* xor_ptrs(T* t1, T* t2)
{
return reinterpret_cast<T*>(reinterpret_cast<uintptr_t>(t1)^reinterpret_cast<uintptr_t>(t2));
}
다른 Necromancer 배지를 얻을 위험을 감수하면서 uintptr_t (또는 intptr_t)에 매우 유용한 사용법을 추가하고 싶습니다. 테스트 가능한 임베디드 코드를 작성하고 있습니다. 나는 다양한 팔과 현재 tensilica 프로세서를 대상으로하는 임베디드 코드를 주로 작성합니다. 이들은 다양한 기본 버스 너비를 가지고 있으며 tensilica는 실제로 너비가 다른 별도의 코드 및 데이터 버스가있는 하버드 아키텍처입니다. 필자는 대부분의 코드에 대해 테스트 중심 개발 스타일을 사용합니다. 즉, 내가 작성한 모든 코드 단위에 대해 단위 테스트를 수행합니다. 실제 대상 하드웨어에 대한 단위 테스트는 번거로우므로 일반적으로 Ceedling 및 GCC를 사용하여 Windows 또는 Linux의 Intel 기반 PC에서 모든 것을 작성합니다. 즉, 많은 임베디드 코드에는 비트 트위들 링 및 주소 조작이 포함됩니다. 내 Intel 컴퓨터의 대부분은 64 비트입니다. 따라서 주소 조작 코드를 테스트하려면 수학을 수행 할 일반화 된 객체가 필요합니다. 따라서 uintptr_t는 대상 하드웨어에 배포하려고 시도하기 전에 기계 독립적으로 코드를 디버깅하는 방법을 제공합니다. 또 다른 문제는 일부 컴파일러 또는 일부 컴파일러의 메모리 모델, 함수 포인터 및 데이터 포인터의 너비가 다릅니다. 이러한 머신에서 컴파일러는 두 클래스 사이의 캐스트를 허용하지 않을 수도 있지만 uintptr_t는 둘 중 하나를 보유 할 수 있어야합니다.