0

I have a function foo(void* pBuf). I need to pass it a 64 bit address but I can't seem to get the right typecast when I'm passing by value.

Example: foo(address). Where- uint64_t address=0x00000000DEADBEEF

EDIT: Compiling using an ARM compiler.

uint64_t foo(void *pBuf){
    uint64_t retAddr = (uint64_t) pBuf;
    retAddr += 0x100000;
    return retAddr;
}

I'm on a 32-bit ARM and sizeof(void *) is 4

Clarification: Why I needed a 64-bit address on a 32-bit ARM? Because my memory map uses 36-bit addressing.

sdmello
  • 431
  • 2
  • 6
  • 14
  • address is a local variable which contains the address I need. – sdmello Dec 18 '15 at 02:40
  • You will need to give more details about what sort of system you are on and what the `foo` function does with its argument – M.M Dec 18 '15 at 02:44
  • What's the problem in your latest code example ? The only issue I can guess is that some compilers will issue warnings by casting directly from `void*` to `uint64_t`. In such a case, prefer a double cast : `(uint64_t)(size_t)` – Cyan Dec 18 '15 at 02:55
  • The problem is in compiling the call to foo. Not the function itself. – sdmello Dec 18 '15 at 03:07
  • What do you expect to happen if the `address` did not start with `00000000` ? (bearing in mind that you are trying to fit the address into a 32-bit `void *`) – M.M Dec 18 '15 at 04:34
  • Then the address would be truncated and behave badly. My intention is that the input to foo is always 32-bit(in a 64 bit variable) and the returned value will be 64-bit. – sdmello Dec 18 '15 at 04:39
  • In that case, `foo( (void *)(uint32_t)address );` would be correct. If your compiler rejects that then something is wrong with your compiler, or you made a typo – M.M Dec 18 '15 at 05:40
  • Yeah, this works. Your question led me to the solution. Thanks a lot! – sdmello Dec 18 '15 at 05:42

4 Answers4

1

Call it this way:

uint64_t address = 0xDEADBEEF;
foo((void*)address);

That is, you cast the address to a void-pointer to be compatible with the function signature.

John Zwinck
  • 239,568
  • 38
  • 324
  • 436
1

Sorry to necro this question, but none of these answers seem reasonable to me. This is a fairly straightforward type conversion problem. It seems as though people were caught up on 64-bit addressing on a 32-bit system, when this could easily be for a peripheral or some other address space besides the system itself.

In the OP's case, a cast directly to uint64_t would cause undefined behavior because of the additional four bytes that do not exist in void *. In the case of the M4 calling convention, p would typically be passed in a single register, likely r0. There are no additional upper bytes for uint64_t to alias, so your compiler is rightly issuing a warning for this.

Under the GCC 7.3 arm-none-eabi port, void * can be safely cast to size_t (aka unsigned int) because they both have size and alignment of 4. Once that is done, you can safely promote unsigned int to uint64_t (aka unsigned long long int) by assignment. The promotion is better defined behavior than a cast.

uint64_t foo(void *p){
    uint64_t a = (size_t) p;
    a += 0x100000;
    return a;
} 
0

You should not use a 64-bits type for an address, as it is undefined behavior for 32-bits (or any non-64 bits) systems.

Rather, prefer using uintptr_t, which is standard C. See this question for more details or this page for references.

Then a solution could be :

uintptr_t address = 0xDEADBEEF;   /* will trigger a warning if the constant is > max possible memory size */
foo((void*)address);

Note : if uintptr_t is not available on your system, size_t is usually a good second choice.

Part 2 :

Looks like, in your rephrased question, you want to convert an address into a 64-bits integer.

In which case, a direct cast from ptr to integer is likely to trigger a compiler warning, due to potential differences in wideness.

Prefer a double cast : uint64_t value = (uint64_t)(size_t) ptr;

Community
  • 1
  • 1
Cyan
  • 13,248
  • 8
  • 43
  • 78
0

I can think of two ways to get this right. Got a solution to my problem by calling foo the first way

  1. foo((void*)(uint32_t)address)

This works only because my input to foo is always a 32-bit value. The returned value can be 64-bit.

  1. Of course, a proper fix would be to change foo itself, if I could modify it. I could just pass foo(&address). Inside foo, retAddr = *pBuf.

Thanks for all the suggestions!

sdmello
  • 431
  • 2
  • 6
  • 14