Größe eines einzelnen Strukturelements in C.

109

Ich versuche eine Struktur zu deklarieren, die von einer anderen Struktur abhängig ist. Ich möchte verwenden sizeof, um sicher / pedantisch zu sein.

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

Jetzt möchte ich eine Struktur deklarieren child_t, die dieselbe Größe hat wie parent_t.text.

Wie kann ich das machen? (Pseudocode unten.)

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

Ich habe ein paar verschiedene Möglichkeiten mit parent_tund ausprobiert struct _parent, aber mein Compiler wird dies nicht akzeptieren.

Als Trick scheint dies zu funktionieren:

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

Ist es möglich, child_tohne die Verwendung von zu deklarieren dummy?

Kevinarpe
quelle

Antworten:

202

Obwohl das Definieren der Puffergröße mit a #defineeine idiomatische Methode ist, besteht eine andere darin, ein Makro wie das folgende zu verwenden:

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

und benutze es so:

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

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

Ich bin eigentlich ein bisschen überrascht, dass sizeof((type *)0)->member)das sogar als ständiger Ausdruck erlaubt ist. Cooles Zeug.

Joey Adams
quelle
2
Wow, ich wusste nicht, dass sizeof ((Typ *) 0) -> member) funktioniert. Bin jetzt nicht auf meiner Entwicklungsmaschine, aber funktioniert das für alle Compiler? Danke dafür, Joey.
Gangadhar
4
@ Gangadhar: Ja, das funktioniert für alle Compiler. Der Operand von sizeofwird nicht ausgewertet, daher gibt es kein Problem bei der Dereferenzierung des Nullzeigers (da er nicht tatsächlich dereferenziert ist).
James McNellis
4
wunderbar? seine einfache C89, siehe Implementierung von "offsetof" in <stddef.h> oder die gleiche Implementierung eetimes.com/design/other/4024941/…
user411313
1
@XavierGeoffrey: Hier leitet sizeof den Typ ((Typ *) 0) -> member ab und gibt die Größe dieses Typs zurück. ((Typ *) 0) ist nur ein Nullzeiger des Strukturtyps. ptr-> member ist (zur Kompilierungszeit) ein Ausdruck, dessen Typ der des Mitglieds ist. Der Code innerhalb der Größe von wird nie ausgeführt (wenn dies der Fall wäre, würde das Programm fehlerhaft sein!). Es wird nur die Art des Wertes innerhalb der Größe von betrachtet.
Joey Adams
1
Die Wikipedia-Seite für offset_of vermerkt dies als undefiniertes Verhalten gemäß dem C-Standard, daher würde ich nicht darauf wetten, dass es mit allen Compilern funktioniert.
Graeme
30

Ich bin gerade nicht auf meiner Entwicklungsmaschine, aber ich denke, Sie können eine der folgenden Aktionen ausführen:

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

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


Bearbeiten : Ich mag das member_size-Makro, das Joey mit dieser Technik vorgeschlagen hat. Ich denke, ich würde das verwenden.

Brandon Horsley
quelle
12
Die zweite Form ist sehr schön (und konzeptionell sauber, da sie keine Nullzeiger enthält), aber Sie sollten erwähnen, dass dies in Compilern vor C99 nicht möglich ist.
R .. GitHub STOP HELPING ICE
11

Sie können FIELD_SIZEOF(t, f)den Linux-Kernel verwenden. Es ist nur wie folgt definiert:

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

Diese Art von Makro wird in anderen Antworten erwähnt. Es ist jedoch portabler, ein bereits definiertes Makro zu verwenden.

Dmitry
quelle
4
FIELD_SIZEOF ist das Makro, das in modernen Linux-Kerneln verwendet wird und in linux / kernel.h gefunden wird
Colin Ian King
@ColinIanKing Das ist also eine idiomatische Methode in C im Allgemeinen, um die Strukturelementgröße zu ermitteln? Ist ein solches Makro noch nicht in einem Standard im modernen C enthalten?
St.Antario
Meines Wissens gibt es kein gleichwertiges Makro in Standard C.
Colin Ian King
Es wurde vor kurzem entfernt von linux / kernel.h.
Andriy Makukha
10

Verwenden Sie eine Präprozessor-Direktive, dh #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
quelle
Stimmen Sie mit Nyan überein. Die anderen Lösungen funktionieren, erreichen aber nichts anderes. Wenn Sie expliziter sein möchten, nennen Sie es PARENT_TEXT_LEN oder etwas ähnlich Beschreibendes. Sie können es dann auch in Bedingungen im gesamten Code verwenden, um Pufferlängenfehler zu vermeiden, und es werden einfache Ganzzahlvergleiche durchgeführt.
Dave Mankoff
1
Ich denke, der Vorteil der sizeof-Lösung ist, dass Sie keinen Zugriff auf die übergeordnete Struktur benötigen, wenn diese in einer Bibliothek oder anderswo definiert ist.
@evilclown: aber du machst: "char text [member_size (Parent, text)];" Sie müssen auf das "Elternteil" verweisen.
Dave Mankoff
3
Ich glaube nicht, dass du mich verstanden hast. Angenommen, die übergeordnete Struktur ist drand48_data(definiert in stdlib.h) und Sie möchten die Größe von __x. Sie können #define X_LEN 3die Datei stdlib.h nicht ändern, die Verwendung member_sizeist überlegen, wenn Sie keinen Zugriff auf die Quelle der übergeordneten Struktur haben, was als vernünftige Situation erscheint.
5

Sie können eine Präprozessor-Direktive für die Größe verwenden als:

#define TEXT_MAX_SIZE 255

und verwenden Sie es sowohl bei Eltern als auch bei Kindern.

Codaddict
quelle
Ja, aber es ist nicht immer eine Option: Sagen wir, unter Linux ist /usr/include/bits/dirent.hdie Größe des d_nameFeldes (struct direct/ dirent) nicht mehr definiert, DIRSIZsondern fest codiert. so sizeof()scheint der einzige saubere Weg , um die Abhängigkeit zu halten
ジョージ
5

struct.h hat sie schon definiert,

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

so könntest du,

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

Beim Betrachten des Headers scheint es sich jedoch um eine BSD-Sache zu handeln und nicht um einen ANSI- oder POSIX-Standard. Ich habe es auf einem Linux-Computer versucht und es hat nicht funktioniert. begrenzte Nützlichkeit.

Neil
quelle
4

Eine andere Möglichkeit wäre, einen Typ zu definieren. Die Tatsache, dass Sie für beide Felder die gleiche Größe sicherstellen möchten, ist ein Indikator dafür, dass Sie für sie die gleiche Semantik haben, denke ich.

typedef char description[255];

und dann ein Feld haben

description text;

in beiden Ihrer Typen.

Jens Gustedt
quelle
4

C ++ - Lösung:

sizeof (Type :: member) scheint ebenfalls zu funktionieren:

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

struct Child
{
    char flag;
    char text[sizeof(Parent::text)];
    int used;
};
koreanisch
quelle
3
Die Frage bezieht sich auf C, nicht auf C ++.
Marc
0

@ Joey-Adams, danke! Ich habe das Gleiche gesucht, aber nach einem Array ohne Zeichen, und es funktioniert auch auf diese Weise einwandfrei:

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

Es wird wie erwartet 20 zurückgegeben.

Und @ brandon-horsley, das funktioniert auch gut:

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