문제

기존 코드를 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 파일에 배치하고 필요한 곳에 포함하십시오.

또는 Microsoft 버전의 stdint.h 파일 여기 또는 휴대용을 사용하십시오 여기.

다른 팁

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

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

편집하다:

정수에 대한 올바른 유형

따라서 포인터를 정수로 저장하는 올바른 방법은 uintptr_t 또는 intptr_t 유형.(cppreference에서도 참조하세요. C99의 정수 유형).

이러한 유형은 다음에 정의되어 있습니다. <stdint.h> C99 및 네임스페이스의 경우 std C++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'를 사용할 수 있어야한다고 생각합니다.

이 토론 서명되지 않은 int vs size_t 좀 더 자세히 설명합니다.

사용 uintptr_t 정수 유형으로.

몇 가지 답변이 지적되었습니다 uintptr_t 그리고 #include <stdint.h> 'The'솔루션으로. 즉, 나는 답의 일부를 제안하지만 전체 대답은 아닙니다. 또한 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
$

호출 위치에 문제가 있음을 알게 될 것입니다 ( 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 포함 된 표준 헤더 파일에 정의 된 표준 유형.

이 경우 공허*의 "의미"는 일반적인 손잡이라고 생각합니다. 그것은 값에 대한 포인터가 아니라 값 자체입니다. (이것은 단지 C 및 C ++ 프로그래머가 무효가 사용하는 방식입니다.)

정수 값을 보유하고 있다면 정수 범위 내에있는 것이 더 나았습니다!

다음은 정수에 대한 쉬운 렌더링입니다.

int x = (char*)p - (char*)0;

경고 만 제공해야합니다.

나는 소스 코드를 연구하는 동안이 질문을 발견했다. 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.
*/

신용은 커밋터에게갑니다.

가장 좋은 방법은 포인터 유형에서 비 포인터 유형으로 변환하는 것을 피하는 것입니다. 그러나 이것은 귀하의 경우에 분명하지 않습니다.

모두가 말했듯이, uintptr_t는 당신이 사용해야하는 것입니다.

이것 링크 64 비트 코드로 변환하는 것에 대한 정보가 적습니다.

이것에 대한 좋은 토론도 있습니다 comp.std.c

부터 uintptr_t ~이다 C ++/C ++ 11에 보장되지 않습니다., 이것이 편도 변환이라면 고려할 수 있습니다. uintmax_t, 항상 정의됩니다 <cstdint>.

auto real_param = reinterpret_cast<uintmax_t>(param);

안전하게 플레이하려면 코드의 어느 곳에서나 어설 션을 추가 할 수 있습니다.

static_assert(sizeof (uintmax_t) >= sizeof (void *) ,
              "No suitable integer type for conversion from pointer type");
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top