문제
기존 코드를 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 파일에 배치하고 필요한 곳에 포함하십시오.
다른 팁
나는 이것이 현대 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.h
— inttypes.h
서비스를 제공합니다 stdint.h
, 이는 드문 일이지만 C99 표준은 말합니다. T] 헤더 <inttypes.h>
헤더가 포함되어 있습니다 <stdint.h>
호스팅 된 구현에서 제공하는 추가 시설로 확장합니다.) 그리고 형식 문자열에서 prixxx 매크로를 사용하십시오.
또한 내 의견은 C ++보다는 C에 엄격하게 적용 할 수 있지만 코드는 C ++의 하위 집합에 있으며 C와 C ++ 사이에 휴대 할 수 있습니다. 내 의견이 적용될 가능성은 공정합니다.
#include <stdint.h>
- 사용
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");