tamaño de un solo miembro de estructura en C

109

Estoy tratando de declarar una estructura que depende de otra estructura. Quiero usar sizeofpara ser seguro / pedante.

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

Ahora quiero declarar una estructura child_tque tenga el mismo tamaño que parent_t.text.

¿Cómo puedo hacer esto? (Pseudocódigo a continuación).

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

Intenté algunas formas diferentes con parent_ty struct _parent, pero mi compilador no acepta.

Como truco, esto parece funcionar:

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

¿Es posible declarar child_tsin el uso de dummy?

kevinarpe
fuente

Respuestas:

202

Aunque definir el tamaño del búfer con a #definees una forma idiomática de hacerlo, otra sería usar una macro como esta:

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

y utilícelo así:

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

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

De hecho, estoy un poco sorprendido de que sizeof((type *)0)->member)incluso se permita como una expresión constante. Cosas interesantes.

Joey Adams
fuente
2
Wow, no sabía que sizeof ((type *) 0) -> member) funciona. No estoy en mi máquina de desarrollo ahora, pero ¿funciona esto para todos los compiladores? Gracias por eso Joey.
Gangadhar
4
@Gangadhar: Sí, esto funciona para todos los compiladores. El operando de sizeofno se evalúa, por lo que no hay problema con desreferenciar el puntero nulo (porque en realidad no está desreferenciado).
James McNellis
4
¿maravilloso? su C89 simple, vea la implementación de "offsetof" en <stddef.h> o la misma implementación eetimes.com/design/other/4024941/…
user411313
1
@XavierGeoffrey: Aquí, sizeof infiere el tipo de ((tipo *) 0) -> miembro y devuelve el tamaño de ese tipo. ((tipo *) 0) es solo un puntero nulo del tipo de estructura. ptr-> member es (en tiempo de compilación) una expresión cuyo tipo es el del miembro. El código dentro de sizeof nunca se ejecuta (si lo hiciera, ¡el programa fallaría!) Solo se analiza el tipo de valor dentro del tamaño de.
Joey Adams
1
La página de Wikipedia para offset_of señala esto como un comportamiento indefinido de acuerdo con el estándar C, por lo que no apostaría a que funcione con todos los compiladores.
Graeme
30

No estoy en mi máquina de desarrollo en este momento, pero creo que puede hacer una de las siguientes cosas:

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

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


Editar : Me gusta la macro member_size que Joey sugirió usar esta técnica, creo que la usaría.

Brandon Horsley
fuente
12
La segunda forma es muy agradable (y conceptualmente limpia en el sentido de que no implica punteros nulos), pero debe mencionar que no es posible en compiladores anteriores a C99.
R .. GitHub DEJA DE AYUDAR A ICE
11

Puede utilizarlo FIELD_SIZEOF(t, f)en el kernel de Linux. Simplemente se define de la siguiente manera:

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

Este tipo de macro se menciona en otras respuestas. Pero es más portátil usar una macro ya definida.

Dmitry
fuente
4
FIELD_SIZEOF es la macro utilizada en los kernels modernos de Linux, que se encuentra en linux / kernel.h
Colin Ian King
@ColinIanKing Entonces, ¿esta es una forma idiomática en C en general para obtener el tamaño del miembro de estructura? ¿No se incluye todavía una macro de este tipo en un estándar en C moderno?
St.Antario
Que yo sepa, no hay una macro equivalente en el estándar C.
Colin Ian King
Recientemente se eliminó de linux / kernel.h.
Andriy Makukha
10

Utilice una directiva de preprocesador, es decir, #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
fuente
De acuerdo con Nyan. Las otras soluciones funcionan, pero no logran nada diferente. Si quieres ser más explícito, llámalo PARENT_TEXT_LEN o algo igualmente descriptivo. Luego, también puede usarlo en condicionales a lo largo de su código para evitar errores de longitud del búfer y hará comparaciones simples de enteros.
Dave Mankoff
1
Creo que la ventaja de la solución sizeof es que no necesita acceso a la estructura principal, si está definida en una biblioteca o en otro lugar.
@evilclown: pero lo haces: "char text [member_size (Parent, text)];" Debe hacer referencia al "Padre".
Dave Mankoff
3
No creo que me hayas entendido. suponga que la estructura principal está drand48_data(definida en stdlib.h) y desea el tamaño de __x. no puede #define X_LEN 3y cambiar stdlib.h, usar member_sizees superior cuando no tiene acceso a la fuente de la estructura principal, lo que parece una situación razonable.
5

Puede utilizar una directiva de preprocesador para el tamaño como:

#define TEXT_MAX_SIZE 255

y utilícelo tanto en padres como en niños.

codaddict
fuente
sí, pero no siempre es una opción: digamos, en linux /usr/include/bits/dirent.hel tamaño del d_namecampo (struct direct/ dirent) ya no está definido como DIRSIZsino codificado; así que sizeof()parece ser la única forma limpia de mantener la dependencia
ジ ョ ー ジ
5

struct.h los tiene ya definidos,

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

para que pudieras

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

pero, al mirar en el encabezado, parece que esto es una cosa BSD y no un estándar ANSI o POSIX. Lo probé en una máquina Linux y no funcionó; utilidad limitada.

Neil
fuente
4

Otra posibilidad sería definir un tipo. El hecho de que desee garantizar el mismo tamaño para los dos campos es un indicador de que tiene la misma semántica para ellos, creo.

typedef char description[255];

y luego tener un campo

description text;

en ambos tipos.

Jens Gustedt
fuente
4

solución c ++:

sizeof (Type :: member) parece estar funcionando también:

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

struct Child
{
    char flag;
    char text[sizeof(Parent::text)];
    int used;
};
korish
fuente
3
La pregunta es sobre C, no sobre C ++.
Marc
0

@ joey-adams, ¡gracias! Estaba buscando lo mismo, pero para una matriz sin caracteres y funciona perfectamente bien incluso de esta manera:

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

Devuelve 20 como se esperaba.

Y, @ brandon-horsley, esto también funciona bien:

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