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?

Foi útil?

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 ++ ).

C ++ 11 (e subsequentes) Versão

#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

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.

  1. #include <stdint.h>
  2. 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");
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top