sizeof одиночный член структуры в C

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?

kevinarpe
источник

Ответы:

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
Вау, я не знал, что sizeof ((type *) 0) -> member) работает. Меня сейчас нет на моей машине разработчика, но работает ли это для всех компиляторов? Спасибо за это, Джоуи.
Гангадхар,
4
@Gangadhar: Да, это работает для всех компиляторов. Операнд sizeofне оценивается, поэтому нет проблемы с разыменованием нулевого указателя (потому что он фактически не разыменован).
Джеймс МакНеллис,
4
чудесно? его простой C89, см. реализацию "offsetof" в <stddef.h> или ту же реализацию eetimes.com/design/other/4024941/…
user411313
1
@XavierGeoffrey: Здесь sizeof определяет тип элемента ((type *) 0) -> и возвращает размер этого типа. ((type *) 0) - это просто нулевой указатель типа структуры. ptr-> member - это (во время компиляции) выражение, тип которого совпадает с типом члена. Код в пределах sizeof никогда не запускается (если бы это было так, программа бы отказалась!). Проверяется только тип значения в пределах sizeof.
Джои Адамс
1
На странице Wikipedia для offset_of это указано как неопределенное поведение в соответствии со стандартом C, поэтому я бы не стал делать ставку на его работу со всеми компиляторами.
Грэм
30

Я сейчас не на своей машине для разработки, но думаю, вы можете сделать одно из следующих действий:

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

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


Изменить : мне нравится макрос member_size, который Джои предложил использовать эту технику, я думаю, что использовал бы это.

Брэндон Хорсли
источник
12
Вторая форма очень хороша (и концептуально чиста, поскольку не включает нулевых указателей), но вы должны упомянуть, что это невозможно в компиляторах до C99.
R .. GitHub НЕ ПОМОГАЕТ ICE
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?
Сент-Антарио
Насколько мне известно, в стандартном 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 ;
Дэйв Манкофф
источник
Согласен с Ньян. Остальные решения работают, но ничего другого не дают. Если вы хотите быть более ясным, назовите его PARENT_TEXT_LEN или как-нибудь так же описательно. Затем вы можете использовать его в условных выражениях во всем коде, чтобы предотвратить ошибки длины буфера, и он будет выполнять простые целочисленные сравнения.
Дэйв Манкофф
1
Я думаю, что преимущество решения sizeof в том, что вам не нужен доступ к родительской структуре, если она определена в библиотеке или где-то еще.
@evilclown: но вы делаете: "char text [member_size (Parent, text)];" Вам нужно сослаться на «Родителя».
Дэйв Манкофф
3
я не думаю, что вы меня поняли. предположим, что родительская структура drand48_data(определена в stdlib.h), и вам нужен sizeof __x. вы не можете #define X_LEN 3и изменить stdlib.h, использование member_sizeлучше, когда у вас нет доступа к источнику родительской структуры, что кажется разумной ситуацией.
5

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

#define TEXT_MAX_SIZE 255

и используйте его как в родительском, так и в дочернем.

codaddict
источник
да, но это не всегда вариант: скажем, в Linux /usr/include/bits/dirent.hразмер d_nameполя (struct direct/ 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 (Type :: member), похоже, тоже работает:

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

struct Child
{
    char flag;
    char text[sizeof(Parent::text)];
    int used;
};
Кориш
источник
3
Речь идет о C, а не о C ++.
Marc
0

@ joey-adams, спасибо! Я искал то же самое, но для массива без символов, и он отлично работает даже таким образом:

#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])
Роман Ковтух
источник