Estou tentando adaptar um código existente a uma máquina de 64 bits. O principal problema é que em uma função, o codificador anterior usa um argumento void * que é convertido em um 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, em uma máquina de 64 bits, recebo o erro:
error: cast from 'void*' to 'int' loses precision
Eu gostaria de corrigir isso para que ele ainda funcione em uma máquina de 32 bits e da forma mais limpa possível. Qualquer ideia ?
size_t
não funcionamento é a memória segmentada i386. Embora seja uma máquina de 32 bits,sizeof
retorna2
parasize_t
. A resposta de Alex abaixo parece correta. A resposta de Alexuintptr_t
funciona em quase todos os lugares e agora é padrão. Ele fornece um tratamento C ++ 11 e até mesmo fornece proteções de cabeçalho do C ++ 03.Respostas:
Use
intptr_t
euintptr_t
.Para garantir que seja definido de 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
Basta colocá-lo em algum arquivo .h e incluí-lo onde for necessário.
Como alternativa, você pode baixar a versão do
stdint.h
arquivo da Microsoft aqui ou usar uma versão portátil aqui .fonte
<cstdint>
ou baixar o apropriadocstdint
se você baixar umstdint.h
.cstdint
do padrão C ++, assim como todos os nomes de tipo definidos lá. Na verdade, é apropriado para as tags especificadas. ... Não concordo em definir os tipos manualmente, mas pode ser necessário ao trabalhar com compiladores que não o fazem.Eu diria que esta é a maneira moderna do C ++.
#include <cstdint> void *p; auto i = reinterpret_cast<std::uintptr_t>(p);
EDITAR :
O tipo correto para o inteiro
portanto, a maneira correta de armazenar um ponteiro como um inteiro é usar os tipos
uintptr_t
ouintptr_t
. (Veja também em cppreference inteiros tipos para C99 ).esses tipos são definidos em
<stdint.h>
para C99 e no namespacestd
para C ++ 11 em<cstdint>
(consulte tipos inteiros para C ++ ).Versão C ++ 11 (e posteriores)
#include <cstdint> std::uintptr_t i;
Versão C ++ 03
extern "C" { #include <stdint.h> } uintptr_t i;
Versão C99
#include <stdint.h> uintptr_t i;
O operador de fundição correto
Em C, há apenas um elenco e usar o elenco de C em C ++ é desaprovado (portanto, não o use em C ++). Em C ++, existem diferentes castes.
reinterpret_cast
é o elenco correto para esta conversão (veja também aqui ).Versão C ++ 11
auto i = reinterpret_cast<std::uintptr_t>(p);
Versão C ++ 03
uintptr_t i = reinterpret_cast<uintptr_t>(p);
Versão C
uintptr_t i = (uintptr_t)p; // C Version
Perguntas relacionadas
fonte
uintptr_t
vez desize_t
, por que isso exigereinterpret_cast
? Parece umastatic_cast
static_cast
pode converter o tipo ou se for um ponteiro pode fazer ajustes de ponteiro se o tipo precisar.reinterpret_cast
é realmente apenas mudar o tipo de padrão de memória subjacente (sem mutações). para esclarecer:static_cast
se comporta de forma idêntica aqui.'size_t' e 'ptrdiff_t' são necessários para corresponder à sua arquitetura (seja ela qual for). Portanto, acho que em vez de usar 'int', você deve ser capaz de usar 'size_t', que em um sistema de 64 bits deve ser do tipo 64 bits.
Esta discussão unsigned int vs size_t entra em um pouco mais de detalhes.
fonte
size_t
é que deve conter o resultado de qualquer umsizeof()
. Isso não significa necessariamente 64 bits em x64. ver tambémsize_t
pode armazenar com segurança o valor de um ponteiro não membro. Consulte en.cppreference.com/w/cpp/types/size_t .Use
uintptr_t
como seu tipo inteiro.fonte
Várias respostas apontaram
uintptr_t
e#include <stdint.h>
como 'a' solução. Essa é, eu sugiro, parte da resposta, mas não toda a resposta. Você também precisa verificar onde a função é chamada com o ID de mensagem FOO.Considere este código e 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ê observará que há um problema no local de chamada (in
main()
) - converter um inteiro em um ponteiro sem uma conversão. Você precisará analisá-lofunction()
em todos os seus usos para ver como os valores são passados para ele. O código dentro do meufunction()
funcionaria se as chamadas fossem escritas:unsigned long i = 0x2341; function(1, &i);
Como os seus provavelmente estão escritos de forma diferente, você precisa revisar os pontos onde a função é chamada para garantir que faz sentido usar o valor conforme mostrado. Não se esqueça, você pode estar encontrando um bug latente.
Além disso, se você for formatar o valor do
void *
parâmetro (conforme convertido), observe cuidadosamente o<inttypes.h>
cabeçalho (em vez destdint.h
-inttypes.h
fornece os serviços destdint.h
, o que é incomum, mas o padrão C99 diz [t] que o cabeçalho<inttypes.h>
inclui o cabeçalho<stdint.h>
e estende-o com recursos adicionais fornecidos por implementações hospedadas ) e usa as macros PRIxxx em suas strings de formato.Além disso, meus comentários são estritamente aplicáveis a C em vez de C ++, mas seu código está no subconjunto de C ++ que é portável entre C e C ++. As chances são boas de que meus comentários se apliquem.
fonte
#include <stdint.h>
uintptr_t
o tipo padrão definido no arquivo de cabeçalho padrão incluído.fonte
Me deparei com essa pergunta enquanto estudava o código-fonte do SQLite .
No sqliteInt.h , há um parágrafo de código definido uma macro convertida entre inteiro e ponteiro. O autor fez uma declaração muito boa, primeiro apontando que deveria ser um problema dependente do compilador e então implementou a solução para contabilizar a maioria dos compiladores populares que existem.
#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 comprometedores.
fonte
A melhor coisa a fazer é evitar a conversão de tipos de ponteiro em tipos não-ponteiro. No entanto, isso claramente não é possível no seu caso.
Como todos disseram, o uintptr_t é o que você deve usar.
Este link contém boas informações sobre a conversão para código de 64 bits.
Também há uma boa discussão sobre isso em comp.std.c
fonte
Acho que o "significado" de void * neste caso é um identificador genérico. Não é um ponteiro para um valor, é o próprio valor. (Acontece que é assim que void * é usado por programadores C e C ++.)
Se estiver contendo um valor inteiro, é melhor que esteja dentro do intervalo inteiro!
Aqui está uma renderização fácil para inteiro:
int x = (char*)p - (char*)0;
Deve dar apenas um aviso.
fonte
Desde
uintptr_t
é não garantido para estar lá em C ++ / C ++ 11 , se esta é uma conversão de uma maneira que você pode consideraruintmax_t
, sempre definido no<cstdint>
.auto real_param = reinterpret_cast<uintmax_t>(param);
Para jogar pelo seguro, pode-se adicionar uma declaração em qualquer lugar do código:
static_assert(sizeof (uintmax_t) >= sizeof (void *) , "No suitable integer type for conversion from pointer type");
fonte