4

I need to convert "void*" to int, but compiler keeps giving me warning. Wonder if there is a way to change the code so that compiler will not complain. This occurs a lot in the code base, especially when passing an argument to starting a new thread.

$ g++ -fpermissive te1.cc
te1.cc: In function ‘void dummy(void*)’:
te1.cc:4:15: warning: cast from ‘void*’ to ‘int’ loses precision [-fpermissive]
  int x = (int)p;
               ^

Here is the simple code "te1.cc":

#include <stdio.h>

extern void someFunc(int);
void dummy(int type, void *p) {
    if (type == 0) {
        int x = (int)p;
        someFunc(x);
    } else if (type == 1) {
        printf("%s\n", (char*)p);
    }
}

int main(int argc, char *argv[]) {
    void *p = (void*)5;
    dummy(p);
    return 0;
}

UDPATE1

I understand that I will lose precision. It's intended sometimes. What I need is to have a way to remove the warning in places I know for sure it's safe. Sorry for not making it clear earlier.

UDPATE2

Updated the code snippet to be a little less non-trivial to illustrate the point. The parameter needs to pass different type of values. I need a way to cast without generating warning.

packetie
  • 4,839
  • 8
  • 37
  • 72
  • 4
    `static_cast(static_cast(p))`. Note though that the cast does lose information - the compiler isn't warning you just for laughs. Why do you feel the need to cast - why don't you instead `printf("p = %p\n", p);` ? – Igor Tandetnik Sep 04 '16 at 17:56
  • You seems to be casting 64bit pointer to an int. try `#include ... uintptr_t x = (uintptr_t)p`; – Serge Sep 04 '16 at 18:05
  • 1
    It complains because your pointer does not fit into an `int`. You should probably use `long long`, otherwise you'll lose half of the address. – zvone Sep 04 '16 at 18:08
  • Correction: `static_cast(reinterpret_cast(p))` – Igor Tandetnik Sep 04 '16 at 18:13
  • Why not `int p = 5; dummy(&p);` in `main()`, and then `void dummy(void *p) { int x = *(int *)p; printf("x = %d\n", x); }` for the function? There's only one cast needed — that's an improvement. You can use a reinterpret cast if you prefer that to the C style cast. – Jonathan Leffler Sep 04 '16 at 18:22
  • @IgorTandetnik, it's meant as a simple example, the point is that this function is so that caller can pass a pointer or an (4 byte) integer to callees. – packetie Sep 04 '16 at 19:58
  • The `printf` is simply an example. In reality, the callee will do something non-trivial with the passed in value. The passed in value should be intepreted as pointer in some cases or simply integer in some other cases. – packetie Sep 04 '16 at 20:02
  • So, did my suggestion not work for you? In what way does it fail to satisfy your requirements? – Igor Tandetnik Sep 04 '16 at 22:41
  • @IgorTandetnik, when I have `int x = static_cast(static_cast(p)); printf("x = %d\n",x);`, I got compile error `te1.cc: In function ‘void dummy(void*)’: te1.cc:6:51: error: invalid static_cast from type ‘void*’ to type ‘uintptr_t {aka long unsigned int}’ int x = static_cast(static_cast(p)); ^ ` Thanks for your suggestion though. – packetie Sep 04 '16 at 23:45
  • I did post a correction (4th comment). – Igor Tandetnik Sep 05 '16 at 01:14
  • all of your code looks like `C` to me. Are you sure you want to tag this as `C++`? – Jan Hohenheim Sep 05 '16 at 06:21
  • Thanks @IgorTandetnik, your correction did it. This C++ trick works great. – packetie Sep 05 '16 at 14:06
  • @IgorTandetnik, do you want to change the comment into an answer? – packetie Sep 05 '16 at 14:12

2 Answers2

7

I need to convert "void*" to int

no you don't.

I really do...

no, you need to represent a pointer as some kind of integer type which is guaranteed not to lose information.

#include <cstdio>
#include <cstdint>
#include <iostream>
#include <cstring>
#include <utility>
#include <cinttypes>

void dummy(void *p) {
    std::intptr_t x = reinterpret_cast<std::intptr_t>(p);
    printf("x = %" PRIiPTR "\n", x);
// ^^ see here: http://en.cppreference.com/w/cpp/types/integer
}

int main(int argc, char *argv[]) {
    void *p = (void*)5;
    dummy(p);
    return 0;
}

ok, what I really want to do is work with 32-bit values in a standards-compliant way.

This is what std::uint32_t is for:

#include <cstdint>
#include <iostream>

void dummy(std::uint32_t x) {
  std::cout << x << '\n';
}

int main(int argc, char *argv[]) {
    auto x = std::uint32_t(5);
    dummy(x);
    return 0;
}

std::uint32_t - guaranteed to be unsigned 32 bits

std::int32_t - guaranteed to be signed 32 bits

Richard Hodges
  • 68,278
  • 7
  • 90
  • 142
  • Thanks @richard-hodges for the answer. Sometimes I just need to pass in an 4-byte integer. How can I receive this integer in the callee without causing a warning? – packetie Sep 04 '16 at 20:04
  • 1
    @packetie std::uint32_t is a standardised unsigned 32 bit integer. It is not a pointer. Void* is a pointer, which on my laptop is 64 bits. – Richard Hodges Sep 04 '16 at 20:29
6

You are probably looking for something along the lines of

int x = static_cast<int>(reinterpret_cast<std::uintptr_t>(p));

This is not strictly guaranteed to work: perhaps surprisingly, the standard guarantees that a pointer converted to a large enough integer and back to a pointer results in the same value; but doesn't provide a similar guarantee for when an integer is converted to a pointer and back to the integer. All it says about the latter case is

[expr.reinterpret.cast]/4 A pointer can be explicitly converted to any integral type large enough to hold it. The mapping function is implementation-defined. [ Note: It is intended to be unsurprising to those who know the addressing structure of the underlying machine. —end note ]

Hopefully, you know the addressing structure of your machine, and won't be surprised.

Igor Tandetnik
  • 50,461
  • 4
  • 56
  • 85