포인터를 정수로 변환
기존 코드를 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 및 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'를 사용할 수 있어야한다고 생각합니다.
이 토론 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
$
You will observe that there is a problem at the calling location (in main()
) — converting an integer to a pointer without a cast. You are going to need to analyze your function()
in all its usages to see how values are passed to it. The code inside my function()
would work if the calls were written:
unsigned long i = 0x2341;
function(1, &i);
Since yours are probably written differently, you need to review the points where the function is called to ensure that it makes sense to use the value as shown. Don't forget, you may be finding a latent bug.
Also, if you are going to format the value of the void *
parameter (as converted), look carefully at the <inttypes.h>
header (instead of stdint.h
— inttypes.h
provides the services of stdint.h
, which is unusual, but the C99 standard says [t]he header <inttypes.h>
includes the header <stdint.h>
and extends it with additional facilities provided by hosted implementations) and use the PRIxxx macros in your format strings.
Also, my comments are strictly applicable to C rather than C++, but your code is in the subset of C++ that is portable between C and C++. The chances are fair to good that my comments apply.
#include <stdint.h>
- Use
uintptr_t
standard type defined in the included standard header file.
I think the "meaning" of void* in this case is a generic handle. It is not a pointer to a value, it is the value itself. (This just happens to be how void* is used by C and C++ programmers.)
If it is holding an integer value, it had better be within integer range!
Here is easy rendering to integer:
int x = (char*)p - (char*)0;
It should only give a warning.
I came across this question while studying the source code of SQLite.
In the sqliteInt.h, there is a paragraph of code defined a macro convert between integer and pointer. The author made a very good statement first pointing out it should be a compiler dependent problem and then implemented the solution to account for most of the popular compilers out there.
#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
And here is a quote of the comment for more details:
/*
** 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.
*/
Credit goes to the committers.
The best thing to do is to avoid converting from pointer type to non-pointer types. However, this is clearly not possible in your case.
As everyone said, the uintptr_t is what you should use.
This link has good info about converting to 64-bit code.
There is also a good discussion of this on comp.std.c
Since uintptr_t
is not guaranteed to be there in C++/C++11, if this is a one way conversion you can consider uintmax_t
, always defined in <cstdint>
.
auto real_param = reinterpret_cast<uintmax_t>(param);
To play safe, one could add anywhere in the code an assertion:
static_assert(sizeof (uintmax_t) >= sizeof (void *) ,
"No suitable integer type for conversion from pointer type");
참고URL : https://stackoverflow.com/questions/153065/converting-a-pointer-into-an-integer
'Nice programing' 카테고리의 다른 글
node gyp error TRACKER : 오류 TRK0005 : 찾기 실패 :“CL.exe”. (0) | 2020.10.15 |
---|---|
NuGet 패키지 관리자 : 'AutoMapper'에 이미 'Microsoft.CSharp'에 대한 종속성이 정의되어 있습니다. (0) | 2020.10.15 |
glxgears에 대한 수직 동기화 비활성화 (0) | 2020.10.15 |
Laravel Request :: all ()은 정적으로 호출하면 안됩니다. (0) | 2020.10.15 |
전체 서브넷에 대해 MySQL에 대한 원격 액세스 권한을 부여하는 방법은 무엇입니까? (0) | 2020.10.15 |