How to access same memory address as two different data types (uint64_t/uint8_t[8]) in C?

Viewed 338

I would like to access a single memory location with two different datatypes in the C programming language.

This is how I want it to be done:

I make a pointer and allocate 64 bits of memory for it. Then I want to access that memory by using either uint64_t or uint8_t[8].

Using unsigned long long int and unsigned char would not be correct because sizeof(unsigned char)==sizeof(uint8_t) is not always true.

I have a feeling that loops and copying memory is not really needed and I think that both

uint64_t abc = { 0xdeadbeefcafe1337 }

and

uint8_t[8] xyz = { 0xde, 0xad, 0xbe, 0xef, 0xca, 0xfe, 0x13, 0x37 }

look the same in memory.

Edit: But why?

I want to make it easier to do simple addition on int and I would also like to access that int value in a simple array-like fashion, one-byte time.

3 Answers

you can use unions for that

typedef union 
{
    uint64_t u64;
    uint32_t u32[2];
    uint16_t u16[4];
    uint8_t  u8[8];
}u64;

void foo(void)
{
    u64 u;

    u.u64 =  0xdeadbeefcafe1337ULL;
    for(size_t i = 0; i < sizeof(u.u64); i++)
    {
        printf("byte %02d - 0x%hhX\n", i, u.u8[i]);
    }
}

void foo(void)
{
    u64 u;

    u.u64 =  0xdeadbeefcafe1337ULL;
    for(size_t i = 0; i < sizeof(u.u64); i++)
    {
        printf("byte %02d - 0x%hhX\n", i, u.u8[i]);
    }
}

void bar(void)
{
    u64 *u = malloc(sizeof(*u));

    u -> u64 =  0x1337cafebeefdeadULL;
    for(size_t i = 0; i < sizeof(*u); i++)
    {
        printf("byte %02d - 0x%hhX\n", i, u -> u8[i]);
    }
    free(u);
}

int main(void)
{
    foo();
    printf("-----------------------\n");
    bar();
}

https://godbolt.org/z/hkoqFN

You can always access an object of any type as an array of characters (char, unsigned char, or signed char) and uint8_t is in 99.999% (100%?) cases just an unsigned char.

I.e., you can simply do:

uint64_t abc = { 0xdeadbeefcafe1337 };
uint8_t *pabc = (uint8_t*)&abc[0];

and use the pointer to inspect or modify abc.

Note that strict aliasing wouldn't allow you to do that with:

uint64_t abc = { 0xdeadbeefcafe1337 };
uint32_t *pabc = (uint32_t*)&abc[0]; 
//^just this is ok but derefing this *pabc would violate strict aliasing

There you'd need a union or memcpy, which would practically get optimized just the same as a direct dereference but the dereference is prohibited to help the compiler with alias analysis which helps with better codegen.

Going in the reverse direction, i.e., accessing (even a properly aligned and properly sized) declared uint8_t array as an uint64_t is also not allowed and it has nothing to do with how the memory would look like and everything to do with alias analysis.

ok start from scratch, take a look at this code

#include <stdio.h>
#include <stdint.h>
int main()
{
    uint64_t abc = 0xdeadbeefcafe1337;
    uint8_t xyz[8] = { 0xde, 0xad, 0xbe, 0xef, 0xca, 0xfe, 0x13, 0x37 };

    printf("%x\n",xyz[0]);
    printf("%x\n",((uint8_t*)&abc)[0]); //is the same of *(uint8_t*)&abc
    return 0;
}

and after that take a read https://en.wikipedia.org/wiki/Endianness. Beware do not confuse how the data are stored in cpu register, it is another story.

After that you will get your answer.

Cheers

Related