简体   繁体   中英

Is casting an array to a homogeneous struct portable in C?

Consider the following homogeneous struct:

struct myStruct {
    void* a;
    char* b;
    int* c;
};

It is homogeneous, I believe, because all of the datatypes are pointers.

Given this struct would the following code be valid and portable across C99?

int main()
{
    void* x = NULL;
    char* y = "hello";
    int* z = malloc(sizeof(int) * 10);
    z[2] = 10;

    void** myArray = malloc(sizeof(void*) * 3);
    myArray[0] = x;
    myArray[1] = y;
    myArray[2] = z;

    struct myStruct* s = (struct myStruct*)myArray;

    printf("%p %s %d\n", s->a, s->b, s->c[2]);

    return 0;
}

I understand that structs will often add padding between components in order to keep the size of the struct consistent, however, because the types of the pointers are all the same, is it a safe assumption that no padding will be added? I'm not necessarily asking if there is a 100% guarantee (I understand this is completely implementation specific, and that a compiler could add padding for obscure reasons), more I am asking for what reasons padding might be added to a homogeneous struct, if any reasons at all.

No it's not portable. The size of pointers may actually differ which means there may be padding in your structure.

On most modern platforms this won't be a problem, but there's nothing in the standard that says all pointers have to be equal in size. Only that pointers are implicitly convertible from and to void * .

A good example of a platform where pointer sizes differs is DOS (which is still actively used, for example on embedded systems) and other 16-bit segmented systems.

The code violates aliasing rules. The types void* and struct myStruct are not compatible, and no exceptions apply in this case. The dereference of the struct pointer in the printf statement causes undefined behavior:

printf("%p %s %d\n", s->a, s->b, s->c[2]);

This is true even if the structure has the same size as three void pointers, has no padding, and has the same alignment requirements.

Nothing prevents the compiler to use different alignment for structures and arrays. For example on SPARC with GCC it's possible to align structures to either 4 or 8 byte boundary using -mfaster-structs / -mnofaster-structs switches, while arrays of pointers are aligned to the size of their element (again, 4 or 8 bytes, depending on the kind of pointers you use). In case of a mismatch, your cast

struct myStruct* s = (struct myStruct*)myArray;

will produce an invalid pointer and result in UB.

(...) because the types of the pointers are all the same, is it a safe assumption that no padding will be added?

No, not at all. Padding (packing) is completely unrelated issue to a struct being homogeneous or not. It works simply "as long as a member is shorter than current packing, it's padded to compensate". Eg, homogeneous struct of singular char s will almost always be heavily padded.

You can do #pragma pack(16) and your struct members will be spaced 16 bytes apart. Or you can set the packing to 1 byte and then mix void* s with single char s and still have no padding (but pointer alignment headache instead).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM