user4829059
user4829059

Reputation: 13

C pointer address manipulation

There is a basic problem which I've had to use the below logic to fix. I want to know how to make this work.

void main(void)
{
     unsigned char data[100];
     unsigned int i;
     uint32_t data_addr;
     uint32_t *p;

     for(i=0;i<100;i++)
         data[i] = i;

     data_addr = (uint32_t) &data[16];
     p = (uint32_t *) data_addr;
     // p = &data[16];
     printf("\nAddress = %#x %#x \n", *p, (data + 16));
     printf("\n");
     return (0);
}

I know for a fact p = (uint32_t *) data_addr; does not work; but I have a situation to make this code work with the same statement.

I would like to know how to make it work without using p = &data[16];.

Upvotes: 1

Views: 1094

Answers (3)

Loic
Loic

Reputation: 650

I am not sure what you want to achieve, but I believe there are some inaccuracies in your code which may be the source of your problem:

data_addr = (uint32_t) &data[16] is not a good idea, because uint32_t may be too small for an address (actually it makes my machine segfault at later execution, because the truncated value is casted back to an invalid address).

p = (uint32_t *) data_addr can be safely replaced with p = (uint32_t *) (data+16), since both sides represent addresses (no truncation).

Use printf("%p", p) for printing an address, because in the printf("%#x", data + 16) you use, %x expects and unsigned int, which may not have the same size as an address.

Use printf("%#x", (unsigned int) ...) for printing an uint32_t, because %x expects an unsigned int, which may not have the same size as an uint32_t.

Here is a corrected version of your program which does not causes a segfault on my machine:

#include <stdint.h>
#include <stdio.h>

int main()
{
     unsigned char data[100];
     unsigned int i;
     uint32_t *p;

     for (i = 0; i != 100; i++)
         data[i] = i;

     p = (uint32_t *)  (data + 16);
     printf("Value = %#x Address %p\n", (unsigned int) *p, data + 16);
     return 0;
}

I hope it helps.

Upvotes: 2

John Kugelman
John Kugelman

Reputation: 361585

Your code invokes undefined behavior. I presume you know that and are ignoring that technicality. But to be clear, converting pointers to ints and back is usually not a good idea, and casting pointers from one type to another (so-called type punning) is very much not a good idea.

That said, this code will not work on a 64-bit system with 64-bit pointers. You can't store a 64-bit address in a 32-bit integer. If your operating system is 64-bit—which is most likely the case these days—then you'll need uint64_t instead of uint32_t. Or, better yet, use intptr_t, an integer type that is large enough to hold a pointer.

 #include <stdint.h>

 data_addr = (intptr_t) &data[16];
 p = (uint32_t *) data_addr;

And again, this roundabout conversion from unsigned char * to uint32_t * is not safe. You do not know the alignment of data. If it is not 32-bit aligned then &data[16] could be an improperly aligned address for a 32-bit int and could crash some systems. x86 architectures are forgiving of mis-aligned data accesses; other architectures are not.

Type punning also introduces aliasing problems. Compilers are allowed to assume pointers to unrelated types point to unrelated objects, and thereby make optimizations that will break when those pointers actually refer to the same object.

See: Type punning isn't funny: Using pointers to recast in C is bad.

(Also, you should use %p to print pointers, not %x.)

Upvotes: 3

Federico
Federico

Reputation: 1090

If I understand you correctly, you want something on these lines:

p = *(uint32_t **) &data_addr;

(as seen in "Carmack's hack") basically you take the address of data_addr, cast it to pointer to pointer of uint32_t, and then de-reference it, obtaining a pointer to uint32_t, the datatype of p.

The caveats mentioned by John Kugelman in his answer obviously apply.

Upvotes: 0

Related Questions