Чего мне не хватает в этом образце K & R? - PullRequest
3 голосов
/ 22 марта 2011

Ниже приведен отрывок из примера кода на языке программирования C от K & R (раздел 8.7):

typedef long Align;

union header {
    struct {
        union header *ptr;
        unsigned size;
    } s;
    Align x;
};

typedef union header Header;

А вот пояснительная выдержка:

Для упрощениявыравнивание, все блоки кратны размеру заголовка, и заголовок выровнен правильно.Это достигается за счет объединения, которое содержит желаемую структуру заголовка и экземпляр самого ограничительного типа, который мы произвольно сделали long.

Итак, насколько я понимаю, идея состоит в том, чтобы обеспечитьчто экземпляр header занимает количество байтов, кратное sizeof(Align).Цель имеет для меня смысл.

Но рассмотрим случай, когда long равен 64 битам, int равен 32 битам, а указатель равен 64 битам.В этом случае header.s будет 64 + 32 = 96 бит.Таким образом, sizeof(header) будет 96 бит, что не кратно 64, как предполагалось.

В таком случае, я полагаю, было бы необходимо определить Align как что-то еще (возможно, double).Но я не совсем уверен, полностью ли я понимаю, что будет диктовать этот выбор для конкретной архитектуры.Это просто "самый большой" тип?А что, если long - самый большой тип - может ли он не работать так, как я описал выше?

Спасибо

Ответы [ 5 ]

3 голосов
/ 23 марта 2011

Размер объединения по крайней мере такой же, как размер его самого большого члена.Таким образом, даже если sizeof(s) равно 12, а sizeof(long) равно 8, как в вашем примере, у вас все равно будет sizeof(header) >= 12.Аналогично, выравнивание объединения является наибольшим выравниванием любого из его элементов, поэтому наличие элемента long гарантирует, что выравнивание header будет, по крайней мере, выравниванием long, что в данном случае будет 8.

Если вы скомпилируете этот код, вы почти наверняка обнаружите, что sizeof(header) равно 16, а не 12. Причина в том, что если у вас есть массив header объектов, компилятор должен убедиться, чтовыравнивание любого из членов является правильным, и массивы сохраняются в памяти непрерывно.Чтобы достичь этого, размер должен быть кратным выравниванию.Компилятор делает это, добавляя дополнительные 4 байта заполнения к концу объекта header.

3 голосов
/ 23 марта 2011

Компилятор отрегулирует размер header, чтобы ptr, size и x могли эффективно извлекаться из элементов в массиве header[]. На большинстве архитектур наличие x означает, что sizeof(header) будет увеличено до кратного sizeof(long).

1 голос
/ 23 марта 2011

Прошло много времени с тех пор, как я работал со структурами.Но я использовал их немного.Основной принцип состоял в том, чтобы расположить элементы в порядке убывания размера выравнивания.

В то время, когда это было написано, long будет выравниваться по наибольшей границе адресации.Я верю, что это все еще так, но я давно не следил за проблемами с оборудованием.Некоторое оборудование было разработано таким образом, что адреса long не были эффективно обработаны, если их адрес не соответствовал размеру long.

Я считаю, что цель состоит в том, чтобы заголовок долго выравнивался на следующей эффективной границе адресации.Это зависит от оборудования.Если long-адреса эффективно адресуемы на 32-битных границах, то структура выровняется соответствующим образом.Если long-адреса эффективно адресуются только на 64-битных границах, то структура будет использовать 128-битные для правильного выравнивания.

0 голосов
/ 23 марта 2011

Чтобы быть правильным, sizeof (Align) должен быть больше или равен sizeof (s) .Но сегодняшнему компилятору действительно все равно, и он сделает всю работу за вас.

#include <limits.h>
#include <stdio.h>

typedef long Align;

typedef union header {
    struct {
        union header *ptr;  /* 64 bits */
        unsigned size;    /* 32 bits */
    } s;                    /* subtotal 96 */
    Align x;                /* 64 bits -> should be greater than 96 */
} Header;                  /* total 128 bits */

typedef struct hair {
    union header *ptr;  /* 64 bits */
    unsigned size;      /* 32 bits */
} Hair;              /* subtotal 96 */

int main(void)
{
    Header u;
    Hair h;

    printf("char %ld bits, %ld bytes\n", sizeof(char)*CHAR_BIT,(sizeof(char)*CHAR_BIT)/8);
    printf("unsigned %ld bits, %ld bytes\n", sizeof(unsigned)*CHAR_BIT,(sizeof(unsigned)*CHAR_BIT)/8);
    printf("int %ld bits, %ld bytes\n", sizeof(int) * CHAR_BIT, (sizeof(int) * CHAR_BIT)/8);
    printf("long int %ld bits, %ld bytes\n", sizeof(long) * CHAR_BIT, (sizeof(long) * CHAR_BIT)/8);
    printf("long long int %ld bits, %ld bytes\n", sizeof(long long) * CHAR_BIT, (sizeof(long long) * CHAR_BIT)/8);
    printf("float %ld bits, %ld bytes\n", sizeof(float) * CHAR_BIT, (sizeof(float) * CHAR_BIT)/8);
    printf("double %ld bits, %ld bytes\n", sizeof(double) * CHAR_BIT, (sizeof(double) * CHAR_BIT)/8);
    printf("long double %ld bits, %ld bytes\n\n", sizeof(long double) * CHAR_BIT, (sizeof(long double) * CHAR_BIT)/8);
    printf("Header u %ld bits, %ld bytes\n", sizeof(u) * CHAR_BIT, (sizeof(u) * CHAR_BIT)/8);
    printf("Header *ptr %ld bits, %ld bytes\n", sizeof(u.s.ptr) * CHAR_BIT, (sizeof(u.s.ptr) * CHAR_BIT)/8);
    printf("Header size %ld bits, %ld bytes\n", sizeof(u.s.size) * CHAR_BIT, (sizeof(u.s.size) * CHAR_BIT)/8);
    printf("Header x %ld bits, %ld bytes\n\n", sizeof(u.x) * CHAR_BIT, (sizeof(u.x) * CHAR_BIT)/8);
    printf("Hair h %ld bits, %ld bytes\n", sizeof(h) * CHAR_BIT, (sizeof(h) * CHAR_BIT)/8);
    printf("Hair *ptr %ld bits, %ld bytes\n", sizeof(h.ptr) * CHAR_BIT, (sizeof(h.ptr) * CHAR_BIT)/8);
    printf("Hair size %ld bits, %ld bytes\n", sizeof(h.size) * CHAR_BIT, (sizeof(h.size) * CHAR_BIT)/8);
    return 0;
}

Вывод:

$ ./union-limits 
char 8 bits, 1 bytes
unsigned 32 bits, 4 bytes
int 32 bits, 4 bytes
long int 64 bits, 8 bytes
long long int 64 bits, 8 bytes
float 32 bits, 4 bytes
double 64 bits, 8 bytes
long double 128 bits, 16 bytes

Header u 128 bits, 16 bytes
Header *ptr 64 bits, 8 bytes
Header size 32 bits, 4 bytes
Header x 64 bits, 8 bytes

Hair h 128 bits, 16 bytes
Hair *ptr 64 bits, 8 bytes
Hair size 32 bits, 4 bytes

Как видите, Hair такого же размера, как Header,Но для компиляторов, которые не выполняют эту работу, правильно использовать long double в качестве Align.

Берегите себя, Beco.

0 голосов
/ 22 марта 2011

Прошло много времени (слишком долго) с тех пор, как я прочитал книгу K & R, но я верю, что вы правы. Вам нужно будет ввести определение Align для чего-то, по крайней мере, размер указателя на рассматриваемой платформе + sizeof без знака.

Я предполагаю, что когда это было написано (и последний раз обновлено), 32-битная ОС безраздельно властвовала, и ваш сценарий избежал авторов и редакторов. Конечно, они намного умнее / мудрее, и я и я можем быть не в курсе.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...