sizeof único membro da estrutura em C

109

Estou tentando declarar uma estrutura que depende de outra estrutura. Eu quero usar sizeofpara ser seguro / pedante.

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

Agora quero declarar uma estrutura child_t que tem o mesmo tamanho que parent_t.text.

Como posso fazer isso? (Pseudocódigo abaixo.)

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

Eu tentei algumas maneiras diferentes com parent_t e struct _parent, mas meu compilador não aceita.

Como um truque, isso parece funcionar:

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

É possível declarar child_tsem o uso de dummy?

Kevinarpe
fonte

Respostas:

202

Embora definir o tamanho do buffer com um #defineseja uma maneira idiomática de fazer isso, outra seria usar uma macro como esta:

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

e use-o assim:

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

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

Na verdade, estou um pouco surpreso que isso sizeof((type *)0)->member)seja permitido como uma expressão constante. Coisas legais.

Joey Adams
fonte
2
Nossa, eu não sabia que sizeof ((tipo *) 0) -> membro) funciona. Não estou na minha máquina de desenvolvimento agora, mas isso funciona para todos os compiladores? Obrigado por isso Joey.
Gangadhar
4
@Gangadhar: Sim, isso funciona para todos os compiladores. O operando de sizeofnão é avaliado, portanto, não há problema em desreferenciar o ponteiro nulo (porque ele não é realmente desreferenciado).
James McNellis
4
Maravilhoso? seu C89 simples, consulte a implementação de "offsetof" em <stddef.h> ou a mesma implementação eetimes.com/design/other/4024941/…
user411313
1
@XavierGeoffrey: Aqui, sizeof infere o tipo de ((tipo *) 0) -> membro, e retorna o tamanho desse tipo. ((tipo *) 0) é apenas um ponteiro nulo do tipo de estrutura. ptr-> member é (em tempo de compilação) uma expressão cujo tipo é o do membro. O código dentro de sizeof nunca é executado (se fosse, o programa causaria falha em segfault!). Apenas o tipo de valor em sizeof é examinado.
Joey Adams
1
A página da Wikipedia para offset_of observa isso como um comportamento indefinido de acordo com o padrão C, então eu não apostaria que funcionaria com todos os compiladores.
Graeme
30

Não estou na minha máquina de desenvolvimento agora, mas acho que você pode fazer o seguinte:

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

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


Edit : Eu gosto da macro member_size que Joey sugeriu usar esta técnica, acho que a usaria.

Brandon Horsley
fonte
12
A segunda forma é muito boa (e conceitualmente limpa, pois não envolve ponteiros nulos), mas você deve mencionar que não é possível em compiladores pré-C99.
R .. GitHub PARAR DE AJUDAR O ICE
11

Você está livre para usar FIELD_SIZEOF(t, f)o kernel do Linux. É apenas definido da seguinte forma:

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

Este tipo de macro é mencionado em outras respostas. Mas é mais portátil usar uma macro já definida.

Dmitry
fonte
4
FIELD_SIZEOF é a macro usada nos kernels Linux modernos, encontrada em linux / kernel.h
Colin Ian King
@ColinIanKing Então, essa é uma maneira idiomática em C em geral de obter o tamanho do membro da estrutura? Essa macro ainda não está incluída em um padrão no C moderno?
St.Antario
Que eu saiba, não há macro equivalente no padrão C.
Colin Ian King
Ele foi recentemente removido de linux / kernel.h.
Andriy Makukha
10

Use uma diretiva de pré-processador, ou seja, #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
fonte
Concordo com Nyan. As outras soluções funcionam, mas não realizam nada diferente. Se você quiser ser mais explícito, chame-o de PARENT_TEXT_LEN ou algo igualmente descritivo. Você também pode usá-lo em condicionais em todo o código para evitar erros de comprimento de buffer e ele fará comparações de inteiros simples.
dave mankoff
1
Acho que a vantagem da solução sizeof é que você não precisa acessar a estrutura pai, se ela estiver definida em uma biblioteca ou em outro lugar.
@evilclown: mas você: "char text [member_size (Parent, text)];" Você precisa fazer referência ao "Pai".
dave mankoff
3
eu não acho que você me entendeu. suponha que a estrutura pai seja drand48_data(definida em stdlib.h) e você queira o sizeof __x. você não pode #define X_LEN 3e alterar o stdlib.h, o uso member_sizeé superior quando você não tem acesso à fonte da estrutura pai, o que parece uma situação razoável.
5

Você pode usar uma diretiva de pré-processador para o tamanho como:

#define TEXT_MAX_SIZE 255

e usá-lo em pais e filhos.

codadicto
fonte
sim, mas nem sempre é uma opção: digamos, no linux /usr/include/bits/dirent.ho tamanho do d_namecampo (struct direct/ dirent) não é mais definido como, DIRSIZmas embutido em código; então sizeof()parece ser a única maneira limpa de manter a dependência
ジ ョ ー ジ
5

struct.h já os tem definidos,

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

então você poderia,

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

mas, olhando no cabeçalho, parece que isso é uma coisa BSD e não o padrão ANSI ou POSIX. Tentei em uma máquina Linux e não funcionou; utilidade limitada.

Neil
fonte
4

Outra possibilidade seria definir um tipo. O fato de você querer garantir o mesmo tamanho para os dois campos é um indicador de que você tem a mesma semântica para eles, eu acho.

typedef char description[255];

e então tem um campo

description text;

em ambos os seus tipos.

Jens Gustedt
fonte
4

solução c ++:

sizeof (Type :: member) parece estar funcionando também:

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

struct Child
{
    char flag;
    char text[sizeof(Parent::text)];
    int used;
};
coreano
fonte
3
A questão é sobre C, não C ++.
Marc
0

@joey-adams, obrigado! Eu estava pesquisando a mesma coisa, mas para array não char e funciona perfeitamente assim:

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

Ele retorna 20 conforme o esperado.

E, @ brandon-horsley, isso também funciona bem:

#define member_dim(type, member) sizeof(((type){0}).member) / \
                                 sizeof(((type){0}).member[0])
Roman Kovtuh
fonte