Search code examples
cpointersstructvoid-pointersflexible-array-member

What is the most portable way to store both integers and generic pointers in C?


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.


Solution

  • 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.