A conversão de um ponteiro em um número inteiro
Pergunta
Eu estou tentando adaptar um código existente para uma máquina de 64 bits. O principal problema é que, de uma função, o codificador utiliza um argumento anterior void * que é convertido no tipo adequado na própria função. Um pequeno exemplo:
void function(MESSAGE_ID id, void* param)
{
if(id == FOO) {
int real_param = (int)param;
// ...
}
}
Claro que, em uma máquina de 64 bits, eu recebo o erro:
error: cast from 'void*' to 'int' loses precision
Gostaria de corrigir isso para que ele ainda funciona em uma máquina de 32 bits e tão limpa quanto possível. Alguma idéia?
Solução
Use intptr_t
e uintptr_t
.
Para garantir que ele é definido de uma forma portátil, você pode usar um código como este:
#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
Apenas lugar que em algum arquivo .h e incluem sempre que você precisar.
Como alternativa, você pode baixar a versão da Microsoft do arquivo stdint.h
de aqui ou usar um portátil de aqui .
Outras dicas
Eu diria que esta é a maneira moderna C ++.
#include <cstdint>
void *p;
auto i = reinterpret_cast<std::uintptr_t>(p);
Editar :
O tipo correto para o Integer
para o caminho certo para armazenar um ponteiro como um inteiro é usar os tipos uintptr_t
ou intptr_t
. (Ver também em cppreference tipos inteiros para C99 ).
Estes tipos são definidos no <stdint.h>
para C99 e no std
namespace para C ++ 11 em <cstdint>
(veja inteiro tipos para C ++ ).
#include <cstdint>
std::uintptr_t i;
C ++ 03 Versão
extern "C" {
#include <stdint.h>
}
uintptr_t i;
C99 Versão
#include <stdint.h>
uintptr_t i;
O operador de conversão correta ??h2>
Em C existe apenas um elenco e usando o elenco C em C ++ é desaprovada (por isso não usá-lo em C ++). Em C ++ há diferentes moldes. reinterpret_cast
é o elenco correta para esta conversão (Veja também aqui ).
C ++ 11 Versão
auto i = reinterpret_cast<std::uintptr_t>(p);
C ++ 03 Versão
uintptr_t i = reinterpret_cast<uintptr_t>(p);
C Versão
uintptr_t i = (uintptr_t)p; // C Version
Questões relacionadas
'size_t' e 'ptrdiff_t' são necessárias para corresponder à sua arquitetura (seja ele qual for). Portanto, eu acho que em vez de usar 'int', você deve ser capaz de usar 'size_t', que em um sistema de 64 bits deve ser um tipo de 64 bits.
Esta discussão unsigned int vs size_t entra em um pouco mais detalhadamente.
Use uintptr_t
como seu tipo inteiro.
Várias respostas têm apontado para uintptr_t
e #include <stdint.h>
como 'a' solução. Isto é, eu sugiro, parte da resposta, mas não a resposta completa. Você também precisa olhar para onde a função é chamada com o ID da mensagem de FOO.
Considere este código e de compilação:
$ 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
$
Você vai observar que há um problema no local de chamada (em main()
) - conversão de um inteiro para um ponteiro sem um elenco. Você está indo a necessidade de analisar o seu function()
em todos os seus usos para ver como os valores são passados ??para ele. O código dentro da minha function()
funcionaria se as chamadas foram escritos:
unsigned long i = 0x2341;
function(1, &i);
Desde a sua estão provavelmente escrito de forma diferente, você precisa rever os pontos onde a função é chamada para garantir que ele faz sentido usar o valor como mostrado. Não se esqueça, você pode ser encontrar um bug latente.
Além disso, se você estiver indo para formatar o valor do parâmetro void *
(como convertido), olhar cuidadosamente para o cabeçalho <inttypes.h>
(em vez de stdint.h
- inttypes.h
fornece os serviços de stdint.h
, o que é incomum, mas o padrão C99 diz < em> [o] cabeçalho <inttypes.h>
inclui o <stdint.h>
cabeçalho e estende-lo com
facilidades adicionais fornecidos pelas implementações hospedadas ) e usar as macros PRIxxx em suas cadeias de formato.
Além disso, meus comentários são estritamente aplicável a C em vez de C ++, mas o seu código está no subconjunto de C ++ que é portátil entre C e C ++. As chances são razoáveis ??a boas que os meus comentários se aplicam.
-
#include <stdint.h>
- Use
uintptr_t
tipo padrão definido no arquivo de cabeçalho padrão incluído.
Eu acho que o "significado" de void *, neste caso, é um identificador genérico. Não é um ponteiro para um valor, é o próprio valor. (Isto só acontece de ser como void * é usado por programadores C e C ++.)
Se ele está segurando um valor inteiro, é melhor que seja dentro do alcance inteiro!
Aqui é fácil de renderização para número inteiro:
int x = (char*)p - (char*)0;
Ele só deve dar um aviso.
me deparei com esta pergunta ao estudar o código fonte do SQLite .
Na sqliteInt.h , há é um número de código definido um convertido macro entre inteiro e ponteiro. O autor fez uma boa primeira declaração apontando que deveria ser um problema dependente compilador e, em seguida, implementou a solução para a conta para a maioria dos compiladores populares lá fora.
#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
E aqui está uma citação do comentário para mais detalhes:
/*
** 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.
*/
O crédito vai para os committers.
A melhor coisa a fazer é evitar a conversão de tipo de ponteiro para tipos não-ponteiro. No entanto, este não é claramente possível no seu caso.
Como todos disse, o uintptr_t é o que você deve usar.
Este link tem boas informações sobre a conversão de código de 64 bits.
Há também uma boa discussão sobre isso na comp. std.c
Desde uintptr_t
é não garantido para estar lá em C ++ / C ++ 11 , se esta é uma conversão de uma maneira você pode considerar uintmax_t
, sempre definido na <cstdint>
.
auto real_param = reinterpret_cast<uintmax_t>(param);
Para jogar pelo seguro, pode-se acrescentar em qualquer lugar do código de uma afirmação:
static_assert(sizeof (uintmax_t) >= sizeof (void *) ,
"No suitable integer type for conversion from pointer type");