135

I am trying to declare a struct that is dependent upon another struct. I want to use sizeof to be safe/pedantic.

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

Now I want to declare a struct child_t that has the same size as parent_t.text.

How can I do this? (Pseudo-code below.)

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

I tried a few different ways with parent_t and struct _parent, but my compiler will not accept.

As a trick, this seems to work:

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

Is it possible to declare child_t without the use of dummy?

jww
  • 97,681
  • 90
  • 411
  • 885
kevinarpe
  • 20,319
  • 26
  • 127
  • 154

9 Answers9

248

Although defining the buffer size with a #define is one idiomatic way to do it, another would be to use a macro like this:

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

and use it like this:

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

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

I'm actually a bit surprised that sizeof(((type *)0)->member) is even allowed as a constant expression. Cool stuff.

Andy
  • 44,610
  • 13
  • 70
  • 69
Joey Adams
  • 41,996
  • 18
  • 86
  • 115
  • 3
    Wow, I didn't know sizeof((type *)0)->member) works. Am not on my dev machine now, but does this work for all the compilers? Thanks for that Joey. – Gangadhar Aug 24 '10 at 05:03
  • 5
    @Gangadhar: Yes, this works for all compilers. The operand of `sizeof` is not evaluated, so there is no issue with dereferencing the null pointer (because it isn't actually dereferenced). – James McNellis Aug 24 '10 at 05:50
  • 4
    wonderful? its plain C89, see implementation of "offsetof" in or the same implementation http://www.eetimes.com/design/other/4024941/Learn-a-new-trick-with-the-offsetof--macro – user411313 Aug 24 '10 at 07:35
  • Hi can anyone explain this sizeof(((type *)0)->member. I am not able to make sense of it :( – jxgn Sep 29 '14 at 06:38
  • 1
    @XavierGeoffrey: Here, sizeof infers the type of ((type *)0)->member, and returns the size of that type. ((type *)0) is just a null pointer of the struct type. ptr->member is (at compile time) an expression whose type is that of the member. The code within the sizeof never runs (if it did, the program would segfault!). Only the type of value within the sizeof is looked at. – Joey Adams Sep 29 '14 at 21:13
  • 3
    The [Wikipedia page for offset_of](https://en.wikipedia.org/wiki/Offsetof) notes this as undefined behaviour according to the C standard, so I wouldn't bet on it working with all compilers. – Graeme Aug 03 '15 at 15:49
43

I am not on my development machine right now, but I think you can do one of the following:

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

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


Edit: I like the member_size macro Joey suggested using this technique, I think I would use that.
Brandon Horsley
  • 7,956
  • 1
  • 29
  • 28
16

You are free to use FIELD_SIZEOF(t, f) in the Linux kernel. It's just defined as following:

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

This type of macro is mentioned in other answers. But it's more portable to use an already-defined macro.

Craig McQueen
  • 41,871
  • 30
  • 130
  • 181
Dmitry
  • 276
  • 2
  • 4
11

Use a preprocessor directive, i.e. #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 ;
dave mankoff
  • 17,379
  • 7
  • 50
  • 64
  • Agree with Nyan. The other solutions work, but don't accomplish anything different. If you want to be more explicit, call it PARENT_TEXT_LEN or something equally descriptive. You can then also use it in conditionals throughout your code to prevent buffer length errors and it will be doing simple integer comparisons. – dave mankoff Aug 24 '10 at 12:40
  • 1
    i think the advantage of the sizeof solution is that you don't need access to the parent struct, if it is defined in a library or somewhere else. –  Aug 24 '10 at 12:59
  • @evilclown: but you do: "char text[member_size(Parent, text)];" You need to reference the "Parent". – dave mankoff Aug 24 '10 at 14:02
  • 3
    i don't think you understood me. suppose the parent struct is `drand48_data` (defined in stdlib.h) and you want the sizeof `__x`. you cannot `#define X_LEN 3` and change the stdlib.h, using `member_size` is superior when you don't have access to the source of the parent struct, which seems like a reasonable situation. –  Aug 25 '10 at 01:42
  • This solution is not applicable if the parent_t structure comes from some library, where you are not allowed (or don't want) to change the declarations of the structure members. – OpalApps Feb 08 '21 at 14:44
5

You can use a preprocessor directive for size as:

#define TEXT_MAX_SIZE 255

and use it in both parent and child.

codaddict
  • 445,704
  • 82
  • 492
  • 529
  • 1
    yep, but it's not always an option: say, in linux `/usr/include/bits/dirent.h` the size of the `d_name` field (struct `direct` / `dirent`) is not defined as `DIRSIZ` any more but hard-coded; so `sizeof()` seems to be the only clean way to keep the dependency – ジョージ Jan 21 '12 at 17:08
5

c++ solution:

sizeof(Type::member) seems to be working as well:

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

struct Child
{
    char flag;
    char text[sizeof(Parent::text)];
    int used;
};
S.R
  • 2,411
  • 1
  • 22
  • 33
korish
  • 533
  • 5
  • 8
5

struct.h has them already defined,

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

so you could,

#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;
}

but, on looking in the header, it appears that this is a BSD thing and not ANSI or POSIX standard. I tried it on a Linux machine and it didn't work; limited usefulness.

Neil
  • 1,767
  • 2
  • 16
  • 22
  • 1
    TIL, and I’m a BSD person… that being said, `` has been removed from OpenBSD at least (“unused and should not be used”). – mirabilos Jul 13 '22 at 21:26
4

Another possibility would be to define a type. The fact that you want to ensure the same size for the two fields is an indicator that you have the same semantics for them, I think.

typedef char description[255];

and then have a field

description text;

in both of your types.

Jens Gustedt
  • 76,821
  • 6
  • 102
  • 177
0

@joey-adams, thank you! I was searching the same thing, but for non char array and it works perfectly fine even this way:

#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);
}

It returns 20 as expected.

And, @brandon-horsley, this works good too:

#define member_dim(type, member) sizeof(((type){0}).member) / \
                                 sizeof(((type){0}).member[0])
Roman Kovtuh
  • 561
  • 8
  • 14