Подтвердить что ты не робот

Является ли листинг массива однородной структурой, переносимой в C?

Рассмотрим следующую однородную структуру:

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

Я считаю, что это однородно, потому что все типы данных являются указателями.

С учетом этой структуры следующий код будет действительным и переносимым на 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;
}

Я понимаю, что structs часто добавляет дополнение между компонентами, чтобы сохранить согласованность структуры, однако, поскольку типы указателей одинаковы, безопасно ли предположить, что никакие дополнения не будут добавлены? Я не обязательно спрашиваю, есть ли 100% -ная гарантия (я понимаю, что это полностью конкретная реализация, и что компилятор может добавлять дополнения для неясных причин), больше я прошу указать, какие дополнения могут быть добавлены в однородную структуру, если есть какие-либо причины.

4b9b3361

Ответ 1

Нет, он не переносится. Размер указателей может фактически различаться, что означает, что в вашей структуре может быть дополнение.

На большинстве современных платформ это не будет проблемой, но в стандарте нет ничего, что говорит, что все указатели должны быть равны по размеру. Только эти указатели неявно конвертируются с и до void *.

Хорошим примером платформы, где размеры указателя отличаются, является DOS (который все еще активно используется, например, во встроенных системах) и другие 16-разрядные сегментированные системы.

Ответ 2

Код нарушает правила псевдонимов. Типы void * и struct myStruct несовместимы, и в этом случае исключения не применяются. Разновидность указателя struct в выражении printf вызывает поведение undefined:

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

Это верно, даже если структура имеет тот же размер, что и три указателя void, не имеет отступов и имеет те же требования к выравниванию.

Ответ 3

Ничто не мешает компилятору использовать различное выравнивание для структур и массивов. Например, в SPARC с GCC можно выровнять структуры с 4 или 8 байтами с помощью переключателей -mfaster-structs/-mnofaster-structs, в то время как массивы указателей выровнены по размеру их элемента (опять же, 4 или 8 байтов, в зависимости от тип указателей, которые вы используете). В случае несоответствия, ваш личный

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

приведет к недопустимому указателю и приведет к UB.

Ответ 4

(...), потому что типы указателей одинаковы, безопасно ли предположить, что никакие дополнения не будут добавлены?

Нет, совсем нет. Заполнение (упаковка) является полностью несвязанной проблемой для однородности структуры или нет. Он работает просто "до тех пор, пока член короче текущей упаковки, он дополняется, чтобы компенсировать". Например, однородный struct сингулярного char почти всегда будет сильно дополнен.

Вы можете сделать #pragma pack(16), и ваши члены структуры будут разнесены на 16 байт. Или вы можете установить упаковку в 1 байт, а затем смешать void* с одиночным char и до сих пор не иметь отступов (но вместо этого указывается головная боль указателя).