0

I am developing a system (virtual machine? not sure how to call this) in which every data structure is an array (or struct) with three integer fields at the beginning (which must be at the beginning right after another without padding bytes) and n generic pointers. I had a lot of thought on this but it seems hard to find a clear solution.

Below is my first attempt.

void **a = malloc(SOME_SIZE);
a[0] = (void *)1;
a[1] = (void *)0;
a[2] = (void *)1;
a[3] = malloc(SOME_SIZE); a[4] = malloc(SOME_SIZE); //and so on...

I wasn't sure here whether the void * casts to the integers are safe. After a little study, I changed the code as follows.

void **a = malloc(SOME_SIZE);
*(uintptr_t *)a = 1;
*((uintptr_t *)a + 1) = 0;
*((uintptr_t *)a + 2) = 1;
a[3] = malloc(SOME_SIZE); a[4] = malloc(SOME_SIZE); //and so on...

But then I found that on some platforms sizeof(void *) may not equal sizeof(uintptr_t). So I decided to change this to a struct.

typedef struct data {
    size_t counts[3]; //padding bytes are okay after this line
    void *members[]; //flexible array member
} *data;

data a = malloc(sizeof(*a) + SOME_SIZE);
a->counts[0] = 1;
a->counts[1] = 0;
a->counts[2] = 1;
a->members[0] = malloc(SOME_SIZE); a->members[1] = malloc(SOME_SIZE); //and so on...

Here I found a problem that there is no generic pointer in C that is 100% portable, but well I cannot find a solution for that, so let me just ignore some wierd platforms with some different pointer sizes.

So given that a void * can store a pointer to any object, is my last solution satisfying my purpose? I am not much familier with flexible array members, so I may have made a mistake. Or there still might be some points I missed.

Any help appreciated.

  • 7
    `uintptr_t` is fine for its intended purpose: You can convert any void pointer *into* it and back and get the same pointer. Nobody said you can convert arbitrary values of type `uintptr_t` to `void *` and get something meaningful. – Kerrek SB Mar 30 '15 at 10:42
  • Keep in mind that `void **a` is not safe. See [C FAQ 4.9](http://c-faq.com/ptrs/genericpp.html): *There is no generic pointer-to-pointer type in C. `void *` acts as a generic pointer only because conversions (if necessary) are applied automatically when other pointer types are assigned to and from `void *`s;* – Sinan Ünür Mar 30 '15 at 12:36
  • Why is `void** a` unsafe? I would not expect that such a pointer could point to anything other than a `void*`, and that one could safely do `a[0] = &someInt;` and then do `*((int*)*a)=123;` and `x=*((int*)*a);` even though one could not safely do `int **b; ... a=b;`. Of course, conversions of arbitrary `int` values to pointers are no good. – supercat Mar 30 '15 at 18:18

2 Answers2

0

You could use a single array of void*, casting them to uintptr_t to obtain their integer value.

uintptr_t is an unsigned integer type capable of storing a pointer. See What is uintptr_t data type .

Keep in mind that this is a very ugly, unreadable and dangerous trick.

Community
  • 1
  • 1
lodo
  • 2,314
  • 19
  • 31
0

Assuming you'll have some means of knowing which things are integers and which are pointers, you could legitimately use a union which combines and integer and a pointer. On systems where the integer type in question does not have padding bits or trap representations, storing a pointer and then reading back an integer should always yield a value (as opposed to causing Undefined Behavior) but the value of the integer in question should be considered meaningless. Even if the integer type happens to be uintptr_t, I don't think there's any guarantee that type punning would be a legitimate means of converting between the pointer type and the integer type.

Storing an integer value and then reading the pointer would certainly be Undefined Behavior of the integer value was not one that might be read by reading a legitimate pointer. If one has a uintptr_t that was obtained by type-punning a legitimate pointer, using type punning to convert that value back to a pointer would probably work, but I don't know if it's specified. I would not expect that casting such an integer to the pointer type would work, nor that one could use type punning on a uintptr_t that was obtained via cast.

supercat
  • 77,689
  • 9
  • 166
  • 211