Mengapa kita harus mengetik sebuah struct begitu sering dalam C?

407

Saya telah melihat banyak program yang terdiri dari struktur seperti di bawah ini

typedef struct 
{
    int i;
    char k;
} elem;

elem user;

Mengapa begitu sering dibutuhkan? Adakah alasan khusus atau bidang yang dapat diterapkan?

Keraguan Manoj
sumber
14
Jawaban yang lebih menyeluruh dan tepat: stackoverflow.com/questions/612328/…
AbiusX
Ini memiliki kekurangan saya pikir Anda tidak dapat membuat daftar tautan dengan struct anonim karena garis struct * ptrdi dalam struct akan menyebabkan kesalahan
Raghib Ahsan
9
'Jawaban yang lebih menyeluruh dan tepat' adalah Perbedaan antara struct dan typedef struct dalam C ++ , dan ada perbedaan signifikan antara C dan C ++ di area ini yang membuat jawaban itu tidak sepenuhnya sesuai untuk pertanyaan tentang C.
Jonathan Leffler
Pertanyaan ini memiliki duplikat typedef struct vs definisi struct yang juga memiliki jawaban bintang.
Jonathan Leffler
2
OTOH, kernel.org/doc/html/v4.10/process/coding-style.html memberi tahu kita bahwa kita seharusnya tidak melakukan typedef tersebut.
glglgl

Jawaban:

455

Seperti kata Greg Hewgill, typedef berarti Anda tidak perlu lagi menulis di structsemua tempat. Itu tidak hanya menyimpan penekanan tombol, itu juga dapat membuat kode lebih bersih karena memberikan sedikit lebih banyak abstraksi.

Hal-hal seperti

typedef struct {
  int x, y;
} Point;

Point point_new(int x, int y)
{
  Point a;
  a.x = x;
  a.y = y;
  return a;
}

menjadi lebih bersih ketika Anda tidak perlu melihat kata kunci "struct" di semua tempat, itu terlihat lebih seolah-olah ada jenis yang disebut "Point" dalam bahasa Anda. Yang, setelah itu typedef, adalah kasus saya kira.

Perhatikan juga bahwa ketika contoh Anda (dan milik saya) mengabaikan penamaan struct itu sendiri, sebenarnya penamaan itu juga berguna ketika Anda ingin memberikan jenis buram. Maka Anda akan memiliki kode seperti ini di header, misalnya:

typedef struct Point Point;

Point * point_new(int x, int y);

dan kemudian memberikan structdefinisi dalam file implementasi:

struct Point
{
  int x, y;
};

Point * point_new(int x, int y)
{
  Point *p;
  if((p = malloc(sizeof *p)) != NULL)
  {
    p->x = x;
    p->y = y;
  }
  return p;
}

Dalam kasus yang terakhir ini, Anda tidak dapat mengembalikan Poin dengan nilai, karena definisinya disembunyikan dari pengguna file header. Ini adalah teknik yang digunakan secara luas di GTK + , misalnya.

UPDATE Perhatikan bahwa ada juga proyek-proyek C yang sangat terkenal di mana penggunaan typedefuntuk menyembunyikan structini dianggap sebagai ide yang buruk, kernel Linux mungkin adalah proyek yang paling terkenal. Lihat Bab 5 dokumen The Linux Kernel CodingStyle untuk kata-kata marah Linus. :) Maksud saya adalah bahwa "harus" dalam pertanyaan itu mungkin tidak diatur, setelah semua.

beristirahat
sumber
58
Anda tidak boleh menggunakan pengidentifikasi dengan garis bawah yang diikuti oleh huruf besar, mereka dicadangkan (lihat bagian 7.1.3 paragraf 1). Meskipun tidak terlalu menjadi masalah, ini adalah perilaku yang secara teknis tidak terdefinisi saat Anda menggunakannya (7.1.3 paragraf 2).
dreamlax
16
@dreamlax: Jika tidak jelas bagi orang lain, itu hanya memulai pengidentifikasi dengan garis bawah dan huruf besar yang tidak boleh Anda lakukan; Anda bebas menggunakannya di tengah pengenal.
brianmearns
12
Sangat menarik bahwa contoh yang diberikan di sini (di mana typedef mencegah penggunaan "struct" "all over the place") sebenarnya lebih panjang dari kode yang sama tanpa typedef, karena itu menghemat tepat satu penggunaan kata "struct". Smidgen abstraksi yang diperoleh jarang dibandingkan menguntungkan dengan kebingungan tambahan.
William Pursell
7
@Rerito fyi, halaman 166 dari draft C99 , Semua pengidentifikasi yang dimulai dengan garis bawah dan huruf besar atau garis bawah lainnya selalu dicadangkan untuk penggunaan apa pun. Dan Semua pengidentifikasi yang dimulai dengan garis bawah selalu dicadangkan untuk digunakan sebagai pengidentifikasi dengan cakupan file dalam ruang biasa dan nama tag.
e2-e4
10
Cukup menarik, pedoman pengkodean kernel linux mengatakan kita harus lebih konservatif dalam menggunakan typedefs (bagian 5): kernel.org/doc/Documentation/CodingStyle
gsingh2011
206

Sungguh menakjubkan berapa banyak orang yang melakukan kesalahan ini. TOLONG jangan mengetik struct di C, itu tidak perlu mencemari namespace global yang biasanya sudah sangat tercemar di program C besar.

Juga, struct yang diketik tanpa nama tag adalah penyebab utama pemaksaan hubungan pemesanan yang tidak perlu di antara file header.

Mempertimbangkan:

#ifndef FOO_H
#define FOO_H 1

#define FOO_DEF (0xDEADBABE)

struct bar; /* forward declaration, defined in bar.h*/

struct foo {
  struct bar *bar;
};

#endif

Dengan definisi seperti itu, tidak menggunakan typedef, adalah mungkin bagi unit compiland untuk memasukkan foo.h untuk mendapatkan FOO_DEFdefinisi tersebut. Jika tidak mencoba untuk melakukan dereferensi anggota 'bar' dari foostruct maka tidak perlu menyertakan file "bar.h".

Juga, karena ruang nama berbeda antara nama tag dan nama anggota, dimungkinkan untuk menulis kode yang sangat mudah dibaca seperti:

struct foo *foo;

printf("foo->bar = %p", foo->bar);

Karena ruang nama terpisah, tidak ada konflik dalam penamaan variabel yang bertepatan dengan nama tag struct mereka.

Jika saya harus menjaga kode Anda, saya akan menghapus struct typedef'd Anda.

Jerry Hicks
sumber
37
Apa yang lebih menakjubkan adalah bahwa 13 bulan setelah jawaban ini diberikan, saya adalah orang pertama yang mengangkatnya! typedef'ing structs adalah salah satu penyalahgunaan terbesar dari C, dan tidak memiliki tempat dalam kode yang ditulis dengan baik. typedef berguna untuk men-de-obfuscating tipe pointer fungsi yang berbelit-belit dan benar-benar tidak berguna.
William Pursell
28
Peter van der Linden juga membuat kasus terhadap typedefing structs dalam bukunya yang mencerahkan "Expert C Programming - Deep C Secrets". Intinya adalah: Anda INGIN tahu bahwa sesuatu adalah struct atau persatuan, bukan HIDE itu.
Jens
34
Gaya pengkodean kernel Linux secara eksplisit melarang struct typedefing. Bab 5: Typedefs: "Adalah kesalahan untuk menggunakan typedef untuk struktur dan pointer." kernel.org/doc/Documentation/CodingStyle
jasso
63
Apa manfaatnya, tepatnya, mengetik "struct" berulang-ulang memberikan? Dan berbicara tentang polusi, mengapa Anda ingin memiliki struct dan fungsi / variabel / typedef dengan nama yang sama di namespace global (kecuali itu typedef untuk fungsi yang sama)? Pola aman untuk digunakan typedef struct X { ... } X. Dengan begitu Anda bisa menggunakan formulir singkat Xuntuk membahas struct di mana pun definisi tersedia, tetapi masih dapat meneruskan dan mendeklarasikan struct Xketika diinginkan.
Pavel Minaev
7
Saya pribadi sangat jarang jika pernah menggunakan typedef, saya tidak akan mengatakan orang lain tidak boleh menggunakannya, itu hanya bukan gaya saya. Saya suka melihat struct sebelum tipe variabel jadi saya langsung tahu itu sebuah struct. Argumen yang lebih mudah diketik agak timpang, memiliki variabel yang merupakan huruf tunggal juga lebih mudah untuk diketik, juga dengan pelengkapan otomatis betapa sulitnya mengetik struct saat ini di mana saja.
Nathan Day
138

Dari artikel lama oleh Dan Saks ( http://www.ddj.com/cpp/184403396?pgno=3 ):


Aturan bahasa C untuk penamaan struct agak eksentrik, tetapi cukup berbahaya. Namun, ketika diperluas ke kelas-kelas di C ++, aturan-aturan yang sama membuka sedikit celah bagi bug untuk menjelajah.

Di C, namanya muncul di

struct s
    {
    ...
    };

adalah sebuah tag. Nama tag bukan nama jenis. Mengingat definisi di atas, deklarasi seperti

s x;    /* error in C */
s *p;   /* error in C */

ada kesalahan dalam C. Anda harus menuliskannya sebagai

struct s x;     /* OK */
struct s *p;    /* OK */

Nama-nama serikat pekerja dan enumerasi juga merupakan tag dan bukan tipe.

Di C, tag berbeda dari semua nama lain (untuk fungsi, tipe, variabel, dan konstanta enumerasi). Kompiler C memelihara tag dalam tabel simbol yang secara konseptual jika tidak secara fisik terpisah dari tabel yang menampung semua nama lain. Dengan demikian, dimungkinkan bagi program C untuk memiliki tag dan nama lain dengan ejaan yang sama dalam cakupan yang sama. Sebagai contoh,

struct s s;

adalah deklarasi yang valid yang menyatakan variabel s dari tipe struct s. Ini mungkin bukan praktik yang baik, tetapi kompiler C harus menerimanya. Saya belum pernah melihat alasan mengapa C dirancang dengan cara ini. Saya selalu berpikir itu adalah kesalahan, tapi itu dia.

Banyak programmer (termasuk Anda yang benar-benar) lebih suka menganggap nama struct sebagai nama tipe, sehingga mereka mendefinisikan alias untuk tag menggunakan typedef. Misalnya, mendefinisikan

struct s
    {
    ...
    };
typedef struct s S;

memungkinkan Anda menggunakan S sebagai ganti struct, seperti pada

S x;
S *p;

Suatu program tidak dapat menggunakan S sebagai nama tipe dan variabel (atau konstanta fungsi atau enumerasi):

S S;    // error

Ini bagus.

Nama tag dalam definisi struct, gabungan, atau enum adalah opsional. Banyak programmer melipat definisi struct ke dalam typedef dan membuang tag sama sekali, seperti pada:

typedef struct
    {
    ...
    } S;

Artikel yang ditautkan juga memiliki diskusi tentang bagaimana perilaku C ++ yang tidak memerlukan typedefdapat menyebabkan masalah persembunyian nama halus. Untuk mencegah masalah ini, itu ide yang bagus untuk typedefkelas dan struct Anda di C ++, juga, meskipun pada pandangan pertama tampaknya tidak perlu. Di C ++, dengan typedefnama bersembunyi menjadi kesalahan yang kompiler memberitahu Anda tentang daripada sumber tersembunyi dari potensi masalah.

Michael Burr
sumber
2
Salah satu contoh di mana nama tag sama dengan nama non-tag dalam program (POSIX atau Unix) dengan int stat(const char *restrict path, struct stat *restrict buf)fungsi. Di sana Anda memiliki fungsi statdi ruang nama biasa dan struct statdi ruang nama tag.
Jonathan Leffler
2
pernyataan Anda, SS; // error .... IS SALAH Ini bekerja dengan baik. Maksud saya pernyataan Anda bahwa "kita tidak dapat memiliki nama yang sama untuk tag typedef dan var" SALAH ...
tolong
63

Menggunakan typedefmenghindari harus menulis structsetiap kali Anda mendeklarasikan variabel jenis itu:

struct elem
{
 int i;
 char k;
};
elem user; // compile error!
struct elem user; // this is correct
Greg Hewgill
sumber
2
ok kita tidak mengalami masalah di C ++. Jadi mengapa tidak ada yang menghapus kesalahan itu dari kompiler C dan membuatnya sama seperti di C ++. Ok C ++ memiliki beberapa area aplikasi yang berbeda dan juga memiliki fitur-fitur canggih. asli C?
Manoj Doubts
4
Manoj, nama tag ("struct foo") diperlukan ketika Anda perlu mendefinisikan struct yang mereferensikan dirinya. misalnya penunjuk "berikutnya" dalam daftar yang ditautkan. Lebih penting lagi, kompiler mengimplementasikan standar, dan itulah yang dikatakan standar untuk dilakukan.
Michael Carman
41
Ini bukan kesalahan dalam kompiler C, itu bagian dari desain. Mereka mengubah itu untuk C ++, yang saya pikir membuat segalanya lebih mudah, tetapi itu tidak berarti perilaku C salah.
Herms
5
sayangnya banyak 'programmer' mendefinisikan struct kemudian mengetiknya dengan beberapa nama 'tidak terkait' (seperti struct myStruct ...; typedef struct myStruct susan *; Dalam hampir semua kasus typedef mengarah ke apa-apa selain mengacaukan kode, menyembunyikan definisi aktual dari variabel / parameter, dan salah mengarahkan semua orang, termasuk penulis asli kode.
user3629249
1
@ user3629249 Saya setuju dengan Anda bahwa gaya pemrograman yang disebutkan itu mengerikan, tetapi ini bukan alasan untuk merendahkan typedefstruktur secara umum. Anda juga bisa melakukannya typedef struct foo foo;. Tentu saja structkata kunci tidak diperlukan lebih dari itu yang dapat menjadi petunjuk yang berguna bahwa jenis alias yang kita lihat adalah alias untuk struktur tetapi itu tidak buruk secara umum. Pertimbangkan juga kasus di mana pengenal jenis alias yang dihasilkan dari typedefmenunjukkan bahwa itu adalah alias untuk struktur, fe: typedef struct foo foo_struct;.
RobertS mendukung Monica Cellio
38

Satu alasan bagus lainnya untuk selalu mengetik enum dan struct hasil dari masalah ini:

enum EnumDef
{
  FIRST_ITEM,
  SECOND_ITEM
};

struct StructDef
{
  enum EnuumDef MyEnum;
  unsigned int MyVar;
} MyStruct;

Perhatikan kesalahan ketik di EnumDef di struct (Enu u mDef)? Ini mengkompilasi tanpa kesalahan (atau peringatan) dan (tergantung pada interpretasi literal dari Standar C) yang benar. Masalahnya adalah bahwa saya baru saja membuat definisi enumerasi (kosong) baru dalam struct saya. Saya tidak (sebagaimana dimaksud) menggunakan definisi EnumDef sebelumnya.

Dengan typdef, kesalahan ketik yang serupa akan menghasilkan kesalahan kompiler untuk menggunakan jenis yang tidak dikenal:

typedef 
{
  FIRST_ITEM,
  SECOND_ITEM
} EnumDef;

typedef struct
{
  EnuumDef MyEnum; /* compiler error (unknown type) */
  unsigned int MyVar;
} StructDef;
StrructDef MyStruct; /* compiler error (unknown type) */

Saya akan menganjurkan SELALU mengetik struct dan enumerasi.

Bukan hanya untuk menyimpan beberapa pengetikan (tidak ada permainan kata-kata yang dimaksudkan;)), tetapi karena lebih aman.

cschol
sumber
1
Lebih buruk lagi, kesalahan ketik Anda mungkin bertepatan dengan tag yang berbeda. Dalam kasus struct, ini dapat mengakibatkan seluruh program mengkompilasi dengan benar dan memiliki perilaku runtime yang tidak terdefinisi.
MM
3
definisi ini: 'typedef {FIRST_ITEM, SECOND_ITEM} EnumDef;' tidak mendefinisikan enum. Saya telah menulis ratusan program besar dan memiliki nasib buruk untuk melakukan pemeliharaan pada program yang ditulis orang lain. Dari pengalaman sulit, menggunakan typedef pada struct hanya menyebabkan masalah. Mudah-mudahan programmer tidak cacat sehingga mereka memiliki masalah mengetik definisi penuh ketika mereka mendeklarasikan instance struct. C bukan Basic, jadi mengetikkan beberapa karakter lagi tidak merugikan pengoperasian program.
user3629249
2
Bagi mereka yang merasa tidak suka mengetik lebih dari jumlah minimum karakter absolut, mungkin saya sarankan bergabung dengan beberapa kelompok yang mencoba menulis aplikasi dengan jumlah penekanan tombol minimum. Hanya saja, jangan gunakan 'keahlian' baru mereka di lingkungan kerja, terutama lingkungan kerja yang dengan ketat melakukan tinjauan sejawat
user3629249
Catatan, biasanya tidak disarankan untuk menggunakan enumtipe, karena banyak kompiler memancarkan peringatan aneh ketika menggunakannya 'salah'. Misalnya, menginisialisasi suatu enumke 0 mungkin memberikan peringatan 'integer constant not in enum'. Pernyataan ke depan enumjuga tidak diizinkan. Orang harus menggunakan int(atau unsigned int) sebagai gantinya.
yyny
5
Contoh itu tidak dikompilasi, saya juga tidak mengharapkannya. Mengkompilasi Debug / test.o test.c: 10: 17: error: bidang memiliki tipe 'enum EnuumDef' yang tidak lengkap 'enum EnuumDef MyEnum; ^ test.c: 10: 8: catatan: deklarasi maju 'enum EnuumDef' enum EnuumDef MyEnum; ^ 1 kesalahan dihasilkan. gnuc, dengan std = c99.
natersoz
30

Gaya pengkodean kernel Linux Bab 5 memberikan pro dan kontra (sebagian besar kontra) penggunaan typedef.

Tolong jangan gunakan hal-hal seperti "vps_t".

Merupakan kesalahan untuk menggunakan typedef untuk struktur dan pointer. Ketika Anda melihat a

vps_t a;

di sumbernya, apa artinya?

Sebaliknya, jika dikatakan

struct virtual_container *a;

Anda benar-benar dapat mengetahui apa itu "a".

Banyak orang berpikir bahwa mengetik "membantu keterbacaan". Tidak begitu. Mereka hanya berguna untuk:

(A) objek benar-benar buram (di mana typedef secara aktif digunakan untuk menyembunyikan apa objek itu).

Contoh: "pte_t" dll. Objek buram yang hanya dapat Anda akses menggunakan fungsi accessor yang tepat.

CATATAN! Ketidakjelasan dan "fungsi pengakses" tidak baik dalam diri mereka. Alasan kami memilikinya untuk hal-hal seperti pte_t dll. Adalah benar-benar nol informasi yang dapat diakses di sana.

(B) Hapus jenis integer, di mana abstraksi membantu menghindari kebingungan apakah itu "int" atau "panjang".

u8 / u16 / u32 adalah typedef yang sangat bagus, meskipun mereka masuk dalam kategori (d) lebih baik daripada di sini.

CATATAN! Sekali lagi - perlu ada alasan untuk ini. Jika ada sesuatu yang "tidak ditandai lama", maka tidak ada alasan untuk melakukannya

typedef unsigned long myflags_t;

tetapi jika ada alasan yang jelas mengapa itu dalam keadaan tertentu mungkin merupakan "unsigned int" dan di bawah konfigurasi lain mungkin "unsigned long", maka tentu saja silakan dan gunakan typedef.

(C) ketika Anda menggunakan jarang untuk benar-benar membuat tipe baru untuk pemeriksaan tipe.

(d) Tipe baru yang identik dengan tipe C99 standar, dalam keadaan tertentu tertentu.

Meskipun hanya membutuhkan waktu singkat bagi mata dan otak untuk menjadi terbiasa dengan tipe standar seperti 'uint32_t', toh sebagian orang tetap menolak penggunaannya.

Oleh karena itu, tipe 'u8 / u16 / u32 / u64' khusus Linux dan setara yang ditandatangani yang identik dengan tipe standar diizinkan - meskipun mereka tidak wajib dalam kode baru Anda sendiri.

Saat mengedit kode yang ada yang sudah menggunakan satu atau beberapa tipe lainnya, Anda harus menyesuaikan dengan pilihan yang ada dalam kode itu.

(e) Jenis aman untuk digunakan di ruang pengguna.

Dalam struktur tertentu yang terlihat oleh userspace, kami tidak dapat membutuhkan tipe C99 dan tidak dapat menggunakan formulir 'u32' di atas. Dengan demikian, kami menggunakan __u32 dan tipe serupa di semua struktur yang dibagi dengan userspace.

Mungkin ada kasus-kasus lain juga, tetapi aturan dasarnya harus TIDAK PERNAH menggunakan typedef kecuali Anda dapat dengan jelas mencocokkan salah satu aturan itu.

Secara umum, sebuah pointer, atau struct yang memiliki elemen yang dapat diakses secara langsung tidak boleh berupa typedef.

Yu Hao
sumber
4
'Ketidakjelasan dan "fungsi pengakses" tidak baik dalam diri mereka sendiri. Adakah yang bisa menjelaskan mengapa? Saya akan berpikir menyembunyikan informasi dan enkapsulasi akan menjadi ide yang sangat bagus.
Yawar
5
@ Yawar Saya baru saja membaca dokumen ini dan memiliki pemikiran yang persis sama. Tentu, C tidak berorientasi objek, tetapi abstraksi masih merupakan sesuatu.
Baldrickk
12

Ternyata ada pro dan kontra. Sumber informasi yang bermanfaat adalah buku mani "Pemrograman C Pakar" ( Bab 3 ). Secara singkat, di C Anda memiliki beberapa ruang nama: tag, tipe, nama anggota, dan pengidentifikasi . typedefmemperkenalkan alias untuk suatu jenis dan menempatkannya di tag namespace. Yaitu,

typedef struct Tag{
...members...
}Type;

mendefinisikan dua hal. Satu Tag di namespace tag dan satu Ketik dalam tipe namespace. Jadi Anda bisa melakukan keduanya Type myTypedan struct Tag myTagType. Deklarasi suka struct Type myTypeatau Tag myTagTypeilegal. Selain itu, dalam deklarasi seperti ini:

typedef Type *Type_ptr;

kita mendefinisikan pointer ke Tipe kita. Jadi jika kita menyatakan:

Type_ptr var1, var2;
struct Tag *myTagType1, myTagType2;

kemudian var1, var2dan myTagType1pointer ke Ketik tetapi myTagType2tidak.

Dalam buku yang disebutkan di atas, disebutkan bahwa struct typedefing tidak terlalu berguna karena hanya menyelamatkan programmer dari menulis kata struct. Namun, saya keberatan, seperti banyak programmer C lainnya. Meskipun kadang-kadang ternyata mengaburkan beberapa nama (itu sebabnya tidak disarankan dalam basis kode besar seperti kernel) ketika Anda ingin menerapkan polimorfisme dalam C, banyak membantu mencari di sini untuk rincian . Contoh:

typedef struct MyWriter_t{
    MyPipe super;
    MyQueue relative;
    uint32_t flags;
...
}MyWriter;

Anda dapat melakukan:

void my_writer_func(MyPipe *s)
{
    MyWriter *self = (MyWriter *) s;
    uint32_t myFlags = self->flags;
...
}

Jadi, Anda dapat mengakses anggota luar ( flags) dengan struct bagian dalam ( MyPipe) melalui casting. Bagi saya itu kurang membingungkan untuk melemparkan seluruh jenis daripada melakukan (struct MyWriter_ *) s;setiap kali Anda ingin melakukan fungsi seperti itu. Dalam kasus ini referensi singkat adalah masalah besar terutama jika Anda menggunakan teknik dalam kode Anda.

Akhirnya, aspek terakhir dengan typedeftipe ed adalah ketidakmampuan untuk memperpanjangnya, berbeda dengan makro. Jika misalnya, Anda memiliki:

#define X char[10] or
typedef char Y[10]

Anda kemudian dapat mendeklarasikan

unsigned X x; but not
unsigned Y y;

Kami tidak terlalu peduli untuk ini karena struct karena tidak berlaku untuk specifier penyimpanan ( volatiledan const).

pengguna1533288
sumber
1
MyPipe *s; MyWriter *self = (MyWriter *) s;dan Anda baru saja melanggar alias ketat.
Jonathon Reinhart
@JonathonReinhart Akan sangat ilustratif untuk menyebutkan bagaimana hal ini dapat dihindari, misalnya bagaimana GTK + yang sangat senang bekerja di sekitarnya: bugzilla.gnome.org/show_bug.cgi?id=140722 / mail.gnome.org/archives/gtk -devel-list / 2004-April / msg00196.html
underscore_d
"typedef memperkenalkan alias untuk suatu tipe dan menempatkannya di tag namespace. Yaitu" typedef struct Tag{ ...members... }Type; "mendefinisikan dua hal" tidak cukup masuk akal. jika typedef mendefinisikan tag maka 'Ketik' di sini juga harus menjadi tag. Yang benar adalah definisi mendefinisikan 2 tag dan 1 jenis (atau 2 jenis dan 1 tag tidak yakin.): struct Tag, TagDan Type. struct Tagjelas merupakan suatu tipe. Tagadalah sebuah tag. tetapi kebingungannya adalah apakah Typesebuah tag atau tipe
Qwertyzw
12

Saya tidak berpikir deklarasi maju bahkan mungkin dengan typedef. Penggunaan struct, enum, dan union memungkinkan untuk meneruskan deklarasi ketika dependensi (tahu tentang) adalah dua arah.

Style: Penggunaan typedef di C ++ cukup masuk akal. Hampir bisa diperlukan ketika berhadapan dengan templat yang memerlukan banyak dan / atau parameter variabel. Typedef membantu menjaga penamaan tetap lurus.

Tidak demikian halnya dalam bahasa pemrograman C. Penggunaan typedef paling sering tidak memiliki tujuan selain mengaburkan penggunaan struktur data. Karena hanya {struct (6), enum (4), union (5)} jumlah penekanan tombol yang digunakan untuk mendeklarasikan tipe data, hampir tidak ada gunanya untuk alias alias struct. Apakah tipe data itu gabungan atau struktur? Menggunakan deklarasi non-typdefed langsung membuat Anda langsung tahu apa itu.

Perhatikan bagaimana Linux ditulis dengan menghindari ketat dari typedef omong kosong alias ini membawa. Hasilnya adalah gaya minimalis dan bersih.

natersoz
sumber
10
Bersih tidak akan berulang di structmana - mana ... Typedef membuat tipe baru. Apa yang kamu gunakan? Jenis. Kami tidak peduli apakah itu struct, union, atau enum, itu sebabnya kami mengetiknya.
GManNickG
13
Tidak, kami tidak peduli apakah itu struct atau union, versus enum atau beberapa jenis atom. Anda tidak bisa memaksa struct ke integer atau ke pointer (atau ke jenis lain, dalam hal ini), yang hanya Anda terkadang harus menyimpan beberapa konteks. Memiliki kata kunci 'struct' atau 'union' di sekitar meningkatkan lokalitas penalaran. Tidak ada yang mengatakan Anda perlu tahu apa yang ada di dalam struct.
Bernd Jendrissek
1
@BerndJendrissek: Structs dan unions berbeda dari tipe lain, tetapi haruskah kode klien peduli tentang mana dari dua hal (struct atau union) yang kira-kira seperti FILEitu?
supercat
4
@supercat FILE adalah penggunaan typedef yang baik. Saya pikir typedef terlalu sering digunakan , bukan karena kesalahan bahasa. IMHO menggunakan typedef untuk semuanya adalah bau kode "terlalu banyak generalisasi". Perhatikan bahwa Anda mendeklarasikan variabel sebagai FILE * foo, tidak pernah sebagai FILE foo. Bagi saya, ini penting.
Bernd Jendrissek
2
@supercat: "Jika variabel pengidentifikasi file bertipe FILE daripada FILE * ..." Tapi itulah ambiguitas yang diaktifkan oleh typedef! Kami hanya terbiasa membuka FILE * jadi kami tidak khawatir tentang hal itu, tetapi setiap kali Anda menambahkan typedef Anda memperkenalkan sedikit overhead kognitif: apakah API ini ingin foo_t args atau foo_t *? Membawa 'struct' secara eksplisit meningkatkan lokalitas penalaran, jika dengan biaya beberapa karakter per definisi fungsi.
Bernd Jendrissek
4

Mari kita mulai dengan dasar-dasar dan terus maju.

Berikut adalah contoh definisi Struktur:

struct point
  {
    int x, y;
  };

Di sini namanya pointopsional.

Struktur dapat dideklarasikan selama definisi atau setelahnya.

Mendeklarasikan selama definisi

struct point
  {
    int x, y;
  } first_point, second_point;

Mendeklarasikan setelah definisi

struct point
  {
    int x, y;
  };
struct point first_point, second_point;

Sekarang, perhatikan baik-baik kasus terakhir di atas; Anda perlu menulis struct pointuntuk mendeklarasikan Struktur jenis itu jika Anda memutuskan untuk membuat jenis itu di kemudian hari dalam kode Anda.

Masukkan typedef. Jika Anda bermaksud membuat Struktur baru (Struktur adalah tipe data khusus) di lain waktu di program Anda menggunakan cetak biru yang sama, menggunakan typedefselama definisi mungkin merupakan ide yang bagus karena Anda dapat menyimpan beberapa pengetikan bergerak maju.

typedef struct point
  {
    int x, y;
  } Points;

Points first_point, second_point;

Sebuah kata hati-hati saat memberi nama jenis kustom Anda

Tidak ada yang mencegah Anda menggunakan akhiran _t di akhir nama jenis kustom Anda tetapi standar POSIX cadangan penggunaan akhiran _t untuk menunjukkan nama jenis perpustakaan standar.

Seolah-olah
sumber
3

nama yang Anda (opsional) berikan kepada struct disebut nama tag dan, seperti yang telah dicatat, bukan tipe itu sendiri. Untuk sampai ke tipe memerlukan awalan struct.

Selain GTK +, saya tidak yakin tagname digunakan seperti biasanya seperti typedef untuk tipe struct, jadi di C ++ yang dikenali dan Anda dapat menghilangkan kata kunci struct dan menggunakan tagname sebagai nama tipe juga:


    struct MyStruct
    {
      int i;
    };

    // The following is legal in C++:
    MyStruct obj;
    obj.i = 7;

philsquared
sumber
1

typedef tidak akan menyediakan seperangkat struktur data yang saling tergantung. Ini tidak dapat Anda lakukan dengan typdef:

struct bar;
struct foo;

struct foo {
    struct bar *b;
};

struct bar {
    struct foo *f;
};

Tentu saja Anda selalu dapat menambahkan:

typedef struct foo foo_t;
typedef struct bar bar_t;

Apa sebenarnya gunanya itu?

natersoz
sumber
1

A> typdef membantu dalam arti dan dokumentasi suatu program dengan memungkinkan pembuatan sinonim yang lebih bermakna untuk tipe data . Selain itu, mereka membantu membuat parameter program terhadap masalah portabilitas (K&R, pg147, C prog lang).

B> suatu struktur mendefinisikan suatu tipe . Structs memungkinkan pengelompokan yang mudah dari kumpulan vars untuk kenyamanan penanganan (K&R, pg127, C prog lang.) Sebagai satu unit

C> mengetikkan struct dijelaskan dalam A di atas.

D> Bagi saya, struct adalah tipe kustom atau wadah atau koleksi atau ruang nama atau tipe kompleks, sedangkan typdef hanyalah sarana untuk membuat lebih banyak nama panggilan.

JamesAD-0
sumber
0

Diperlukan mengetikkan C99. Itu sudah usang, tetapi banyak alat (ala HackRank) menggunakan c99 sebagai implementasi C murni. Dan typedef diperlukan di sana.

Saya tidak mengatakan mereka harus berubah (mungkin memiliki dua opsi C) jika persyaratannya berubah, kita yang sedang belajar untuk wawancara di situs adalah SOL.

Matthew Corey Brown
sumber
2
" typedefDiperlukan C99 ." Apa maksudmu?
Julien Lopez
Pertanyaannya adalah tentang C, bukan C++. Di Ctypedef 'diperlukan' (dan kemungkinan besar akan selalu demikian). 'Diperlukan' seperti pada, Anda tidak akan dapat mendeklarasikan variabel Point varName;dan memiliki tipe yang sama dengan struct Point;tanpa typedef struct Point Point;.
yyny
0

Dalam bahasa pemrograman 'C' kata kunci 'typedef' digunakan untuk mendeklarasikan nama baru untuk beberapa objek (struct, array, function..enum type). Sebagai contoh, saya akan menggunakan 'struct-s'. Dalam 'C' kita sering menyatakan 'struct' di luar fungsi 'utama'. Sebagai contoh:

struct complex{ int real_part, img_part }COMPLEX;

main(){

 struct KOMPLEKS number; // number type is now a struct type
 number.real_part = 3;
 number.img_part = -1;
 printf("Number: %d.%d i \n",number.real_part, number.img_part);

}

Setiap kali saya memutuskan untuk menggunakan tipe struct saya akan membutuhkan kata kunci ini 'struct' something '' name '.' Typedef 'hanya akan mengubah nama tipe itu dan saya dapat menggunakan nama baru itu di program saya setiap kali saya mau. Jadi kode kita adalah:

typedef struct complex{int real_part, img_part; }COMPLEX;
//now COMPLEX is the new name for this structure and if I want to use it without
// a keyword like in the first example 'struct complex number'.

main(){

COMPLEX number; // number is now the same type as in the first example
number.real_part = 1;
number.img)part = 5;
printf("%d %d \n", number.real_part, number.img_part);

}

Jika Anda memiliki beberapa objek lokal (struct, array, bernilai) yang akan digunakan di seluruh program Anda, Anda bisa memberinya nama menggunakan 'typedef'.

RichardGeerify
sumber
-2

Sama sekali, dalam bahasa C, struct / union / enum adalah instruksi makro yang diproses oleh preprocessor bahasa C (jangan salah dengan preprocessor yang memperlakukan "#include" dan lainnya)

jadi:

struct a
{
   int i;
};

struct b
{
   struct a;
   int i;
   int j;
};

struct b dikeluarkan sebagai sesuatu seperti ini:

struct b
{
    struct a
    {
        int i;
    };
    int i;
    int j;
}

jadi, pada waktu kompilasi, ia berevolusi di stack sebagai sesuatu seperti: b: int ai int i int j

itu juga mengapa sulit untuk memiliki struct selfreferent, putaran preprocessor C dalam loop deklarasi yang tidak dapat diakhiri.

typedef adalah specifier tipe, yang berarti hanya proses compiler C dan itu bisa dilakukan seperti yang dia inginkan untuk mengoptimalkan implementasi kode assembler. Itu juga tidak mengeluarkan anggota tipe par bodoh seperti préprocessor lakukan dengan struct tetapi menggunakan algoritma konstruksi referensi yang lebih kompleks, jadi konstruksi seperti:

typedef struct a A; //anticipated declaration for member declaration

typedef struct a //Implemented declaration
{
    A* b; // member declaration
}A;

diizinkan dan berfungsi penuh. Implementasi ini juga memberikan akses ke konversi tipe kompilator dan menghapus beberapa efek penyadap ketika utas eksekusi meninggalkan bidang aplikasi fungsi inisialisasi.

Ini berarti bahwa dalam C typedefs lebih dekat sebagai kelas C ++ daripada struct kesepian.

doccpu
sumber
3
Sama sekali tidak sulit untuk memiliki struktur referensial sendiri. struct foo {struct foo * berikutnya; hal int; }
Bernd Jendrissek
4
...apa? Mengatakan preprocessor untuk menggambarkan resolusi structsdan typedefs sudah cukup buruk, tetapi sisa tulisan Anda sangat membingungkan sehingga saya merasa kesulitan untuk mendapatkan pesan apa pun darinya. Satu hal yang dapat saya katakan adalah bahwa ide Anda bahwa non- typedefd structtidak dapat dideklarasikan ke depan atau digunakan sebagai anggota yang tidak jelas (pointer) benar-benar salah. Dalam contoh pertama Anda, struct bdapat dengan sepele berisi struct a *, tidak typedefdiperlukan. Pernyataan bahwa structitu hanyalah bagian bodoh dari ekspansi makro, dan itu typedefmemberi mereka kekuatan baru yang revolusioner, sangat keliru
underscore_d