ukuran anggota struct tunggal di C

109

Saya mencoba mendeklarasikan struct yang bergantung pada struct lain. Saya ingin menggunakan sizeofuntuk menjadi aman / bertele-tele.

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

Sekarang saya ingin mendeklarasikan struct child_tyang memiliki ukuran yang sama dengan parent_t.text.

Bagaimana saya bisa melakukan ini? (Pseudo-code di bawah.)

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

Saya mencoba beberapa cara berbeda dengan parent_tdan struct _parent, tetapi kompiler saya tidak mau menerimanya.

Sebagai trik, ini sepertinya berhasil:

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

Apakah mungkin untuk mendeklarasikan child_ttanpa menggunakan dummy?

kevinarpe
sumber

Jawaban:

202

Meskipun menentukan ukuran buffer dengan a #defineadalah salah satu cara idiomatik untuk melakukannya, cara lain adalah menggunakan makro seperti ini:

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

dan gunakan seperti ini:

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

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

Saya sebenarnya sedikit terkejut bahwa sizeof((type *)0)->member)itu bahkan diizinkan sebagai ekspresi konstan. Barang keren.

Joey Adams
sumber
2
Wah, saya tidak tahu sizeof ((type *) 0) -> member) karya. Saat ini tidak ada di mesin dev saya, tetapi apakah ini berfungsi untuk semua kompiler? Terima kasih untuk itu Joey.
Gangadhar
4
@Gangadhar: Ya, ini berfungsi untuk semua kompiler. Operan dari sizeoftidak dievaluasi, jadi tidak ada masalah dengan dereferensi penunjuk null (karena sebenarnya tidak didereferensi).
James McNellis
4
hebat? C89 yang biasa, lihat implementasi "offsetof" di <stddef.h> atau implementasi yang sama eetimes.com/design/other/4024941/…
user411313
1
@XavierGeoffrey: Di sini, sizeof menyimpulkan tipe ((type *) 0) -> anggota, dan mengembalikan ukuran dari tipe itu. ((type *) 0) hanyalah pointer nol dari tipe struct. ptr-> member adalah (pada waktu kompilasi) sebuah ekspresi yang tipenya adalah milik anggota. Kode dalam sizeof tidak pernah berjalan (jika berhasil, program akan segfault!). Hanya jenis nilai dalam sizeof yang dilihat.
Joey Adams
1
The Wikipedia halaman untuk offset_of catatan ini sebagai perilaku undefined sesuai dengan standar C, jadi saya tidak akan bertaruh bekerja dengan semua kompiler.
Graeme
30

Saat ini saya tidak menggunakan mesin pengembangan, tetapi menurut saya Anda dapat melakukan salah satu dari yang berikut:

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

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


Edit : Saya suka makro member_size yang disarankan Joey menggunakan teknik ini, saya pikir saya akan menggunakannya.

Brandon Horsley
sumber
12
Bentuk kedua sangat bagus (dan secara konseptual bersih karena tidak melibatkan pointer nol), tetapi Anda harus menyebutkan bahwa itu tidak mungkin dilakukan pada kompiler pra-C99.
R .. GitHub STOP HELPING ICE
11

Anda bebas menggunakan FIELD_SIZEOF(t, f)kernel Linux. Ini hanya didefinisikan sebagai berikut:

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

Jenis makro ini disebutkan dalam jawaban lain. Tapi lebih portabel menggunakan makro yang sudah ditentukan.

Dmitry
sumber
4
FIELD_SIZEOF adalah makro yang digunakan di kernel Linux modern, ditemukan di linux / kernel.h
Colin Ian King
@ColinIanKing Jadi ini adalah cara idiomatik di C secara umum untuk mendapatkan ukuran anggota struct? Bukankah makro seperti itu sudah termasuk dalam Standar di C modern?
St Antario
Sepengetahuan saya tidak ada makro yang setara dalam standar C.
Colin Ian King
Baru-baru ini dihapus dari linux / kernel.h.
Andriy Makukha
10

Gunakan arahan preprocessor, yaitu #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
sumber
Setuju dengan Nyan. Solusi lain berhasil, tetapi tidak mencapai sesuatu yang berbeda. Jika Anda ingin lebih eksplisit, sebut saja PARENT_TEXT_LEN atau sesuatu yang sama deskriptifnya. Anda kemudian dapat juga menggunakannya dalam kondisi di seluruh kode Anda untuk mencegah kesalahan panjang buffer dan itu akan melakukan perbandingan bilangan bulat sederhana.
dave mankoff
1
Saya rasa keuntungan dari solusi sizeof adalah bahwa Anda tidak memerlukan akses ke struct induk, jika didefinisikan di perpustakaan atau di tempat lain.
@evilclown: tetapi Anda melakukannya: "char text [member_size (Parent, text)];" Anda perlu mereferensikan "Induk".
dave mankoff
3
saya tidak berpikir Anda mengerti saya. misalkan struct induk adalah drand48_data(didefinisikan dalam stdlib.h) dan Anda menginginkan sizeof __x. Anda tidak dapat #define X_LEN 3dan mengubah stdlib.h, menggunakan member_sizelebih baik jika Anda tidak memiliki akses ke sumber struct induk, yang tampaknya merupakan situasi yang masuk akal.
5

Anda dapat menggunakan arahan preprocessor untuk ukuran sebagai:

#define TEXT_MAX_SIZE 255

dan menggunakannya untuk orang tua dan anak.

codaddict
sumber
ya, tetapi itu tidak selalu merupakan pilihan: katakanlah, di linux /usr/include/bits/dirent.hukuran d_namebidang (struct direct/ dirent) tidak didefinisikan DIRSIZlagi tetapi di-hardcode; jadi sizeof()tampaknya satu-satunya cara yang bersih untuk menjaga ketergantungan
ジ ョ ー ジ
5

struct.h apakah sudah ditentukan,

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

jadi kamu bisa,

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

Namun, saat melihat di header, tampaknya ini adalah hal BSD dan bukan standar ANSI atau POSIX. Saya mencobanya di mesin Linux dan tidak berhasil; kegunaan terbatas.

Neil
sumber
4

Kemungkinan lain adalah menentukan tipe. Fakta bahwa Anda ingin memastikan ukuran yang sama untuk dua bidang adalah indikator bahwa Anda memiliki semantik yang sama untuk mereka, saya kira.

typedef char description[255];

dan kemudian memiliki bidang

description text;

di kedua tipe Anda.

Jens Gustedt
sumber
4

solusi c ++:

sizeof (Type :: member) tampaknya berfungsi dengan baik:

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

struct Child
{
    char flag;
    char text[sizeof(Parent::text)];
    int used;
};
korish
sumber
3
Pertanyaannya adalah tentang C, bukan C ++.
Marc
0

@ joey-adams, terima kasih! Saya mencari hal yang sama, tetapi untuk non char array dan itu berfungsi dengan baik bahkan dengan cara ini:

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

Ini mengembalikan 20 seperti yang diharapkan.

Dan, @ brandon-horsley, ini juga berfungsi dengan baik:

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