[c++] 포인터를 정수로 변환

기존 코드를 64 비트 시스템에 적용하려고합니다. 주요 문제는 한 함수에서 이전 코더가 함수 자체에서 적합한 유형으로 변환되는 void * 인수를 사용한다는 것입니다. 간단한 예 :

void function(MESSAGE_ID id, void* param)
{
    if(id == FOO) {
        int real_param = (int)param;
        // ...
    }
}

물론 64 비트 컴퓨터에서는 오류가 발생합니다.

error: cast from 'void*' to 'int' loses precision

32 비트 컴퓨터에서 가능한 한 깔끔하게 작동하도록이 문제를 수정하고 싶습니다. 어떤 생각?



답변

사용 intptr_t하고 uintptr_t.

이식 가능한 방식으로 정의되도록하려면 다음과 같은 코드를 사용할 수 있습니다.

#if defined(__BORLANDC__)
    typedef unsigned char uint8_t;
    typedef __int64 int64_t;
    typedef unsigned long uintptr_t;
#elif defined(_MSC_VER)
    typedef unsigned char uint8_t;
    typedef __int64 int64_t;
#else
    #include <stdint.h>
#endif

.h 파일에 넣고 필요한 곳에 포함하면됩니다.

또는 여기stdint.h 에서 Microsoft 버전의 파일을 다운로드 하거나 여기 에서 휴대용 파일을 사용할 수 있습니다 .


답변

이것이 현대적인 C ++ 방식이라고 말하고 싶습니다.

#include <cstdint>
void *p;
auto i = reinterpret_cast<std::uintptr_t>(p);

수정 :

정수에 대한 올바른 유형

따라서 포인터를 정수로 저장하는 올바른 방법은 uintptr_t또는 intptr_t유형 을 사용하는 것 입니다. ( C99에 대한 cppreference 정수 유형 참조 ).

이러한 유형은 <stdint.h>C99 및 stdC ++ 11 의 네임 스페이스 에 정의되어 있습니다 <cstdint>( C ++의 정수 유형 참조 ).

C ++ 11 (이상) 버전

#include <cstdint>
std::uintptr_t i;

C ++ 03 버전

extern "C" {
#include <stdint.h>
}

uintptr_t i;

C99 버전

#include <stdint.h>
uintptr_t i;

올바른 캐스팅 연산자

C에서는 캐스트가 하나 뿐이며 C ++에서 C 캐스트를 사용하는 것은 눈살을 찌푸립니다 (따라서 C ++에서는 사용하지 마십시오). C ++에는 다른 캐스트가 있습니다. reinterpret_cast이 변환에 대한 올바른 캐스트입니다 ( 여기 참조 ).

C ++ 11 버전

auto i = reinterpret_cast<std::uintptr_t>(p);

C ++ 03 버전

uintptr_t i = reinterpret_cast<uintptr_t>(p);

C 버전

uintptr_t i = (uintptr_t)p; // C Version

관련 질문


답변

아키텍처와 일치하려면 ‘size_t’및 ‘ptrdiff_t’가 필요합니다. 따라서 ‘int’를 사용하는 것보다 64 비트 시스템에서 64 비트 유형이어야하는 ‘size_t’를 사용할 수 있어야한다고 생각합니다.

이 토론 unsigned int 대 size_t 는 좀 더 자세히 설명합니다.


답변

uintptr_t정수 유형으로 사용하십시오 .


답변

여러 답변에서 지적 uintptr_t하고#include <stdint.h> ‘는’솔루션으로. 즉, 답변의 일부이지만 전체 답변은 아닙니다. 또한 FOO의 메시지 ID로 함수가 호출되는 위치를 확인해야합니다.

이 코드와 컴파일을 고려하십시오.

$ cat kk.c
#include <stdio.h>
static void function(int n, void *p)
{
    unsigned long z = *(unsigned long *)p;
    printf("%d - %lu\n", n, z);
}

int main(void)
{
    function(1, 2);
    return(0);
}
$ rmk kk
        gcc -m64 -g -O -std=c99 -pedantic -Wall -Wshadow -Wpointer-arith \
            -Wcast-qual -Wstrict-prototypes -Wmissing-prototypes \
            -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE kk.c -o kk
kk.c: In function 'main':
kk.c:10: warning: passing argument 2 of 'func' makes pointer from integer without a cast
$

호출 위치 (in main())에 문제가 있음을 알 수 있습니다. 캐스트없이 정수를 포인터로 변환합니다. function()값이 전달되는 방식을 확인하려면 모든 용도를 분석해야 합니다. function()호출이 작성되면 내 코드 가 작동합니다.

unsigned long i = 0x2341;
function(1, &i);

당신의 것들은 아마도 다르게 쓰여질 것이므로, 표시된대로 값을 사용하는 것이 합리적인지 확인하기 위해 함수가 호출되는 지점을 검토해야합니다. 잊지 마세요. 잠재적 인 버그를 발견 할 수 있습니다.

또한이 값의 형식을하려는 경우, void *매개 변수 (변환 등)에주의 깊게 살펴 <inttypes.h>헤더 (대신 stdint.hinttypes.h의 서비스를 제공하는 stdint.h특별한이다,하지만 C99 표준은 말한다 그는 헤더는 [t] <inttypes.h>헤더를 포함 <stdint.h>하고 호스팅 된 구현에서 제공하는 추가 기능으로 확장하고 형식 문자열에 PRIxxx 매크로를 사용합니다.

또한 내 의견은 C ++가 아닌 C에 엄격하게 적용되지만 코드는 C와 C ++간에 이식 가능한 C ++의 하위 집합에 있습니다. 내 의견이 적용될 가능성은 공평합니다.


답변

  1. #include <stdint.h>
  2. uintptr_t포함 된 표준 헤더 파일에 정의 된 표준 유형을 사용하십시오 .


답변

SQLite 의 소스 코드를 연구하는 동안이 질문을 보았습니다 .

에서 sqliteInt.h , 정수와 포인터 사이 매크로 변환을 정의 된 코드의 단락이있다. 저자는 먼저 컴파일러에 의존적 인 문제가되어야한다고 지적한 다음 대부분의 인기있는 컴파일러를 설명하는 솔루션을 구현했습니다.

#if defined(__PTRDIFF_TYPE__)  /* This case should work for GCC */
# define SQLITE_INT_TO_PTR(X)  ((void*)(__PTRDIFF_TYPE__)(X))
# define SQLITE_PTR_TO_INT(X)  ((int)(__PTRDIFF_TYPE__)(X))
#elif !defined(__GNUC__)       /* Works for compilers other than LLVM */
# define SQLITE_INT_TO_PTR(X)  ((void*)&((char*)0)[X])
# define SQLITE_PTR_TO_INT(X)  ((int)(((char*)X)-(char*)0))
#elif defined(HAVE_STDINT_H)   /* Use this case if we have ANSI headers */
# define SQLITE_INT_TO_PTR(X)  ((void*)(intptr_t)(X))
# define SQLITE_PTR_TO_INT(X)  ((int)(intptr_t)(X))
#else                          /* Generates a warning - but it always works     */
# define SQLITE_INT_TO_PTR(X)  ((void*)(X))
# define SQLITE_PTR_TO_INT(X)  ((int)(X))
#endif

자세한 내용은 다음과 같은 의견을 인용합니다.

/*
** The following macros are used to cast pointers to integers and
** integers to pointers.  The way you do this varies from one compiler
** to the next, so we have developed the following set of #if statements
** to generate appropriate macros for a wide range of compilers.
**
** The correct "ANSI" way to do this is to use the intptr_t type.
** Unfortunately, that typedef is not available on all compilers, or
** if it is available, it requires an #include of specific headers
** that vary from one machine to the next.
**
** Ticket #3860:  The llvm-gcc-4.2 compiler from Apple chokes on
** the ((void*)&((char*)0)[X]) construct.  But MSVC chokes on ((void*)(X)).
** So we have to define the macros in different ways depending on the
** compiler.
*/

크레딧은 커미터에게갑니다.