розмір одного члена структури в С


109

Я намагаюся оголосити структуру, яка залежить від іншої структури. Я хочу використовувати, sizeofщоб бути безпечним / педантичним.

typedef struct _parent
{
  float calc ;
  char text[255] ;
  int used ;
} parent_t ;

Тепер я хочу оголосити структуру, child_tяка має той самий розмір, що і parent_t.text.

Як я можу це зробити? (Псевдо-код нижче.)

typedef struct _child
{
  char flag ;
  char text[sizeof(parent_t.text)] ;
  int used ;
} child_t ;

Я спробував декілька різних способів за допомогою parent_tі struct _parent, але мій компілятор не прийме.

Як хитрість, здається, це працює:

parent_t* dummy ;
typedef struct _child
{
  char flag ;
  char text[sizeof(dummy->text)] ;
  int used ;
} child_t ;

Чи можна декларувати child_tбез використання dummy?

Відповіді:


202

Хоча визначення розміру буфера за допомогою a #define- це один ідіоматичний спосіб зробити це, іншим було б використовувати такий макрос:

#define member_size(type, member) sizeof(((type *)0)->member)

і використовувати його так:

typedef struct
{
    float calc;
    char text[255];
    int used;
} Parent;

typedef struct
{
    char flag;
    char text[member_size(Parent, text)];
    int used;
} Child;

Я насправді трохи здивований, що sizeof((type *)0)->member)це навіть дозволено як постійний вираз. Класні речі.


2
Нічого собі, я не знав, що розмір ((тип *) 0) -> член) працює. Я зараз не на своїй машині розробки, але чи працює це для всіх компіляторів? Дякую за це, Джой.
Гангадхар

4
@Gangadhar: Так, це працює для всіх компіляторів. Операнд sizeofне оцінюється, тому немає проблеми з перенаправленням нульового вказівника (оскільки він насправді не відзначений).
Джеймс Макнелл

4
чудовий? його звичайний C89, див. реалізацію "offsetof" в <stddef.h> або тій же реалізації easures.com/design/other/4024941/…
user411313

1
@XavierGeoffrey: Тут розмір визначає тип ((тип *) 0) -> члена і повертає розмір цього типу. ((тип *) 0) - це лише нульовий покажчик типу структура. ptr-> member - це (під час компіляції) вираз, тип якого - тип члена. Код у розміріofof ніколи не запускається (якби це зробила, програма буде сегментарно!). Переглядається лише тип значення в межах sizeof.
Джої Адамс

1
Сторінка Вікіпедії для offset_of відзначає це як невизначене поведінку відповідно до стандарту С, тому я не ставлю на це роботу з усіма компіляторами.
Graeme

30

Наразі я не на своїй машині розвитку, але, думаю, ви можете зробити одне з наступного:

sizeof(((parent_t *)0)->text)

sizeof(((parent_t){0}).text)


Редагувати : Мені подобається, що макрос member_size Джої запропонував використовувати цю техніку, я думаю, я би використовував це.


12
Друга форма є дуже приємною (і концептуально чистою, оскільки вона не містить нульових покажчиків), але слід зазначити, що це неможливо в компіляторах до C99.
R .. GitHub СТОП ДОПОМОГА ВІД

11

Ви можете безкоштовно використовувати FIELD_SIZEOF(t, f)в ядрі Linux. Він просто визначений так:

#define FIELD_SIZEOF(t, f) (sizeof(((t*)0)->f))

Цей тип макросів згадується в інших відповідях. Але більш портативно використовувати вже визначений макрос.


4
FIELD_SIZEOF - макрос, який використовується у сучасних ядрах Linux, знайдений у linux / kernel.h
Колін Іан Кінг

@ColinIanKing Отже, це взагалі ідіоматичний спосіб на C отримати розмір члена структури? Чи такий макрос ще не включений до стандарту в сучасному C?
Св. Антаріо

Наскільки мені відомо, еквівалентний макрос у стандарті С.
Колін Іан Кінг

Нещодавно він був видалений з linux / kernel.h.
Андрій Макуха

10

Використовуйте директиву препроцесора, тобто #define:

#define TEXT_LEN 255

typedef struct _parent
{
  float calc ;
  char text[TEXT_LEN] ;
  int used ;
} parent_t ;

typedef struct _child
{
  char flag ;
  char text[TEXT_LEN] ;
  int used ;
} child_t ;

Погодьтеся з Nyan. Інші рішення працюють, але не виконують нічого іншого. Якщо ви хочете бути більш чіткими, називайте це PARENT_TEXT_LEN або чимось однаково описовим. Потім ви можете також використовувати його в умовних умовах у всьому коді, щоб запобігти помилкам довжини буфера, і він буде робити прості цілісні порівняння.
Дейв Манкофф

1
Я думаю, що перевагою рішення sizeof є те, що вам не потрібен доступ до батьківської структури, якщо вона визначена в бібліотеці чи деінде.

@evilclown: але ви робите: "char char [member_size (Батько, текст)];" Вам потрібно посилатися на "Батько".
Дейв Манкофф

3
я не думаю, що ти мене зрозумів. припустимо, батьківська структура є drand48_data(визначена в stdlib.h), і ви хочете розмір __x. ви не можете #define X_LEN 3і змінити stdlib.h, використовуючи member_sizeSuperior, коли у вас немає доступу до джерела батьківської структури, що здається розумною ситуацією.

5

Ви можете використовувати директиву препроцесора для розміру як:

#define TEXT_MAX_SIZE 255

і використовувати його як для батьків, так і для дитини.


так, але це не завжди варіант: скажімо, в linux /usr/include/bits/dirent.hрозмір d_nameполя (structdirect / dirent) більше не визначається як DIRSIZжорсткий; так sizeof()здається, єдиний чистий спосіб утримати залежність
ジ ョ ー ジ

5

struct.h їх уже визначено,

#define fldsiz(name, field) \
    (sizeof(((struct name *)0)->field))

щоб ви могли,

#include <stdlib.h> /* EXIT_SUCCESS */
#include <stdio.h>  /* printf */
#include <struct.h> /* fldsiz */

struct Penguin {
    char name[128];
    struct Penguin *child[16];
};
static const int name_size  = fldsiz(Penguin, name) / sizeof(char);
static const int child_size = fldsiz(Penguin, child) / sizeof(struct Penguin *);

int main(void) {
    printf("Penguin.name is %d chars and Penguin.child is %d Penguin *.\n",
           name_size, child_size);
    return EXIT_SUCCESS;
}

але, дивлячись у заголовку, виявляється, що це BSD, а не стандарт ANSI чи POSIX. Я спробував це на машині Linux, і це не спрацювало; обмежена корисність.


4

Іншою можливістю було б визначити тип. Те, що ви хочете забезпечити однаковий розмір для двох полів - це показник того, що у вас однакова семантика для них, я думаю.

typedef char description[255];

а потім мати поле

description text;

в обох типах.


4

c ++ рішення:

sizeof (Тип :: член), здається, працює також:

struct Parent
{
    float calc;
    char text[255];
    int used;
};

struct Child
{
    char flag;
    char text[sizeof(Parent::text)];
    int used;
};

3
Питання стосується C, а не C ++.
Марк

0

@ joey-adams, дякую! Я шукав те саме, але для масиву non char, і він працює чудово навіть таким чином:

#define member_dim(type, member) sizeof(((type*)0)->member) / \
                                 sizeof(((type*)0)->member[0])

struct parent {
        int array[20];
};

struct child {
        int array[member_dim(struct parent, array)];
};

int main ( void ) {
        return member_dim(struct child, array);
}

Він повертає 20, як очікувалося.

І, @ brandon-horsley, це також добре працює:

#define member_dim(type, member) sizeof(((type){0}).member) / \
                                 sizeof(((type){0}).member[0])
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.