Neden bir yapıyı C'de bu kadar sık ​​yazalım?


406

Aşağıdaki gibi yapılardan oluşan birçok program gördüm

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

elem user;

Neden bu kadar sık ​​ihtiyaç duyuluyor? Belirli bir sebep veya uygulanabilir alan var mı?


14
Daha kapsamlı ve kesin cevap: stackoverflow.com/questions/612328/…
AbiusX

Dezavantajları var Bence anonim yapı ile bir bağlantı listesi oluşturamazsınız çünkü yapı struct * ptriçindeki çizgi hataya neden olur
Raghib Ahsan

9
'Daha ayrıntılı ve kesin cevap' C ++ 'daki yapı ve typedef yapısı arasındaki farktır ve bu alanda C ve C ++ arasında bu cevabı C ile ilgili bir soru için tamamen uygun olmayan önemli farklılıklar vardır.
Jonathan Leffler

Bu sorunun yinelenen bir typedef struct ve vst tanımı da vardır.
Jonathan Leffler

2
OTOH, kernel.org/doc/html/v4.10/process/coding-style.html bize bu tip tanımları yapmamamız gerektiğini söyler.
glglgl

Yanıtlar:


454

Greg Hewgill'in dediği gibi, typedef artık her yere yazmak zorunda olmadığınız anlamına geliyor struct. Bu sadece tuş vuruşlarını kaydetmekle kalmaz, aynı zamanda bir temizleyici daha soyutlama sağladığı için kodu daha temiz hale getirebilir.

Gibi şeyler

typedef struct {
  int x, y;
} Point;

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

"struct" anahtar kelimesini her yerde görmeniz gerekmediğinde daha temiz hale gelir, daha çok kendi dilinizde "Nokta" adı verilen bir tür varmış gibi görünür. Hangi, sonra, typedefsanırım.

Ayrıca, örneğiniz (ve benimki) struct kendisini adlandırmayı ihmal etse de , aslında adlandırmanın opak bir tür sağlamak istediğinizde de yararlı olduğunu unutmayın. Ardından, başlıkta böyle bir kodunuz olur, örneğin:

typedef struct Point Point;

Point * point_new(int x, int y);

ve ardından structuygulama dosyasındaki tanımı sağlayın :

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

Bu son durumda, tanımı başlık dosyasının kullanıcılarından gizlendiği için Noktadan Noktaya değer döndüremezsiniz. Bu, örneğin GTK + 'da yaygın olarak kullanılan bir tekniktir .

GÜNCELLEME Bu typedefgizlenmenin kullanımının structkötü bir fikir olduğu düşünülen C projeleri de vardır , Linux çekirdeği muhtemelen en iyi bilinen projedir. Linus'un öfkeli kelimeleri için Linux Çekirdek Kodlama Türü belgesinin 5. Bölümüne bakın . :) Demek istediğim, soruda "gerekir" belki de sonuçta taş değil.


58
Alt çizgi ve ardından büyük harf içeren tanımlayıcılar kullanmamalısınız, bunlar ayrılmıştır (bkz. Bölüm 7.1.3 paragraf 1). Çok fazla sorun olması muhtemel olmasa da, bunları kullandığınızda teknik olarak tanımlanmamış bir davranıştır (7.1.3 paragraf 2).
dreamlax

16
@dreamlax: Başkaları için net değilse, bu yalnızca bir alt çizgiyle ve yapmamanız gereken bir büyük harfle bir tanımlayıcı başlatır ; bunu bir tanımlayıcının ortasında kullanabilirsiniz.
brianmearns

12
Burada verilen örneğin (typedef'in "yapının" "her yerde" kullanılmasını önlediği) aslında typedef olmadan aynı koddan daha uzundur, çünkü "struct" kelimesinin tam bir kullanımından tasarruf sağlar. Elde edilen soyutlama smidgeni nadiren ek gizleme ile olumludur.
William Pursell

7
@Rerito fyi, sayfa 166 / C99 taslağı , Bir alt çizgi ve bir büyük harf veya başka bir alt çizgi ile başlayan tüm tanımlayıcılar her zaman kullanım için ayrılmıştır. Ve alt çizgiyle başlayan tüm tanımlayıcılar her zaman hem normal hem de etiket adı alanlarında dosya kapsamı olan tanımlayıcılar olarak kullanılmak üzere ayrılmıştır.
e2-e4

10
İlginç bir şekilde, linux çekirdek kodlama yönergeleri, typedefs kullanma konusunda çok daha muhafazakâr olmamız gerektiğini söylüyor (bölüm 5): kernel.org/doc/Documentation/CodingStyle
gsingh2011 29:13

206

Kaç kişinin bunu yanlış anlaması şaşırtıcı. LÜTFEN C'ye yapı tanımlamaz, gereksiz olarak büyük C programlarında zaten çok kirli olan küresel ad alanını gereksiz yere kirletir.

Ayrıca, etiket adı olmayan typedef'd yapıları, üstbilgi dosyaları arasında ilişki siparişi vermenin gereksiz nedenlerinden biridir.

Düşünmek:

#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

Böyle bir tanımla, typedefs kullanmadan, bir derleme biriminin tanımına foo.h eklemesi mümkündür FOO_DEF. Eğer yapının 'bar' üyesini kaldırma girişiminde bulunmazsa foo, "bar.h" dosyasını dahil etmeye gerek kalmaz.

Ayrıca, ad alanları etiket adları ile üye adları arasında farklı olduğundan, aşağıdakiler gibi çok okunabilir kodlar yazmak mümkündür:

struct foo *foo;

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

Ad alanları ayrı olduğundan, yapı etiketi adıyla çakışan değişkenlerin adlandırılmasında bir çakışma yoktur.

Kodunuzu korumak zorunda kalırsam, typedef'd yapılarınızı kaldıracağım.


37
Daha da şaşırtıcı olan, bu cevabın verilmesinden 13 ay sonra, onu ilk kez oylayan benim! typedef'ing structs, C'nin en büyük suiistimallerinden biridir ve iyi yazılmış kodda yeri yoktur. typedef, kıvrık işlev işaretçisi türlerini gizlemek için kullanışlıdır ve gerçekten başka yararlı bir amaca hizmet etmez.
William Pursell

28
Peter van der Linden ayrıca aydınlatıcı "Uzman C Programlama - Derin C Sırları" adlı kitabında tip tanımlayıcı yapılara karşı da dava açıyor. Asıl mesele: Bir şeyin bir gizleme ya da birlik olduğunu bilmek istiyorsun, GİZLE değil.
Jens

34
Linux çekirdek kodlama stili, tip tanımlama yapılarını açıkça yasaklar. Bölüm 5: Typedefs: " yapılar ve işaretçiler için typedef kullanmak bir hatadır ." kernel.org/doc/Documentation/CodingStyle
jasso

63
Tekrar tekrar "struct" yazmanın faydaları nelerdir? Kirlilikten bahsetmişken, neden küresel bir ad alanında aynı ada sahip bir yapıya ve bir fonksiyona / değişken / typedef'e sahip olmak istersiniz (aynı işlev için bir typedef olmadıkça)? Güvenli desen kullanmaktır typedef struct X { ... } X. Bu şekilde X, yapıyı tanımın mevcut olduğu her yerde ele almak için kısa formu kullanabilirsiniz, ancak yine struct Xde istendiğinde ileri bildirebilir ve kullanabilirsiniz .
Pavel Minaev

7
Çok nadiren kişisel olarak typedef kullanırsam, başkalarının kullanmaması gerektiğini söyleyemem, sadece benim tarzım değil. Ben değişken bir tür önce bir yapı görmek ister, bu yüzden hemen onun bir yapı biliyorum. Daha kolay argüman yazmak biraz geciktir, tek harfli bir değişkene sahip olmak da daha kolaydır, ayrıca otomatik tamamlamalarla günümüzde her yerde yapıyı yazmak ne kadar zordur.
Nathan Day

138

Dan Saks'ın eski bir makalesinden ( http://www.ddj.com/cpp/184403396?pgno=3 ):


Yapıları adlandırmak için C dili kuralları biraz eksantriktir, ancak oldukça zararsızdır. Bununla birlikte, C ++ sınıflarına genişletildiğinde, aynı kurallar hataların taranması için küçük çatlaklar açar.

C harfinde, ad

struct s
    {
    ...
    };

bir etikettir. Etiket adı bir tür adı değildir. Yukarıdaki tanım göz önüne alındığında,

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

C hatalarıdır. Bunları şu şekilde yazmalısınız:

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

Sendikaların ve numaralandırmaların adları da türlerden ziyade etiketlerdir.

C'de, etiketler diğer tüm adlardan farklıdır (işlevler, türler, değişkenler ve numaralandırma sabitleri için). C derleyicileri, diğer tüm adları içeren tablodan fiziksel olarak ayrı değilse kavramsal olarak bir sembol tablosundaki etiketleri korur. Böylece, bir C programının aynı yazılışta aynı yazılışı olan bir etikete ve başka bir ada sahip olması mümkündür. Örneğin,

struct s s;

s değişkeninin s değişkenini bildiren geçerli bir bildirimdir. İyi bir uygulama olmayabilir, ancak C derleyicileri bunu kabul etmelidir. C'nin neden bu şekilde tasarlandığına dair hiçbir gerekçe görmedim. Her zaman bunun bir hata olduğunu düşündüm, ama işte burada.

Birçok programcı (sizinki de dahil) yapı adlarını tür adı olarak düşünmeyi tercih eder, bu nedenle bir typedef kullanarak etiket için bir takma ad tanımlarlar. Örneğin,

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

S'yi, yapıların yerine,

S x;
S *p;

Bir program S'yi hem tür hem de değişken (veya işlev veya numaralandırma sabiti) adı olarak kullanamaz:

S S;    // error

Bu iyi.

Yapı, birleşim veya numaralandırma tanımındaki etiket adı isteğe bağlıdır. Birçok programcı yapı tanımını typedef'e katlar ve aşağıdaki gibi etiketle birlikte dağıtır:

typedef struct
    {
    ...
    } S;

Bağlantılı makalede ayrıca bir C ++ davranışının nasıl typedefince ad gizleme sorunlarına neden olabileceği hakkında bir tartışma vardır . Bu sorunları önlemek için typedef, ilk bakışta gereksiz gibi görünse de, C ++ 'daki sınıflarınız ve yapılarınız için iyi bir fikirdir . C ++ 'da, typedefgizleme adıyla derleyicinin size gizli bir potansiyel sorun kaynağı yerine anlattığı bir hata olur.


2
Etiket adının etiketsiz adla aynı olduğu durumlara bir örnek, (POSIX veya Unix) programında işlevlidir int stat(const char *restrict path, struct stat *restrict buf). Orada statsıradan isim alanında bir fonksiyonunuz var vestruct stat etiket adı alanında .
Jonathan Leffler

2
ifadeniz SS; // hata .... YANLIŞ İyi çalışıyor. Demek istediğim "typedef tag ve var için aynı isme sahip olamayız" YANLIŞ ... pls check
eRaisedToX

63

Kullanarak typedef yazmak zorunda önü alınır structbu türde bir değişken bildirmek her zaman:

struct elem
{
 int i;
 char k;
};
elem user; // compile error!
struct elem user; // this is correct

2
Tamam biz C ++ bu sorunu yaşıyoruz. Öyleyse neden kimse bu aksaklığı C'nin derleyicisinden kaldırmıyor ve C ++ ile aynı hale getirmiyor? Ok C ++ bazı farklı uygulama alanlarına sahip ve bu yüzden gelişmiş özelliklere sahip. Ancak bazılarını değiştirmeden C orijinal C?
Manoj Doubts

4
Manoj, etiket adı ("struct foo"), kendisini referans alan bir yapı tanımlamanız gerektiğinde gereklidir. bağlantılı bir listedeki "sonraki" işaretçisi. Daha da önemlisi, derleyici standardı uygular ve standardın yaptığı şey budur.
Michael Carman

41
C derleyicisindeki bir aksaklık değil, tasarımın bir parçası. C ++ için, işleri kolaylaştırdığını düşünüyorum, ama bu C'nin davranışının yanlış olduğu anlamına gelmiyor.
Herms

5
maalesef pek çok 'programcı' bir yapı tanımlar ve daha sonra bazı 'ilişkisiz' adlarla onu tanımlar (struct myStruct ... gibi; typedef struct myStruct susan *; Hemen hemen tüm durumlarda, bir typedef, kodun dağınıklığından başka bir şeye yol açmaz. bir değişken / parametre ve kodun orijinal yazarı da dahil olmak üzere herkesi yanlış yönlendirir
user3629249

1
@ user3629249 Bahsedilen programlama tarzının korkunç olduğunu kabul ediyorum, ancak bu typedefgenel olarak yapıları kötüleştirmek için bir neden değil . Sen de yapabilirsin typedef struct foo foo;. Tabii ki structanahtar kelimeden daha fazla gerekli değildir, bu da baktığımız tür takma adının bir yapı için takma ad olduğu, ancak genel olarak kötü olmadığı için yararlı bir ipucu olabilir. Elde edilen tipi takma tanımlayıcısı burada da bir olgu göz önünde typedefbir yapı, Fe için bir takma olduğunu gösterir: typedef struct foo foo_struct;.
RobertS,

38

Numaraları ve yapıları daima tanımlamak için bir başka iyi neden, bu sorundan kaynaklanır:

enum EnumDef
{
  FIRST_ITEM,
  SECOND_ITEM
};

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

Yapıdaki EnumDef'teki (Enu u mDef) yazım hatası fark ettiniz mi? Bu, hatasız (veya uyarı) olmadan derlenir ve (C Standardının gerçek yorumuna bağlı olarak) doğrudur. Sorun şu ki, benim yapımda yeni (boş) bir numaralandırma tanımı oluşturdum. Ben (tanımlandığı gibi) önceki tanım EnumDef kullanarak değilim.

Bir typdef ile benzer türdeki yazım hataları, bilinmeyen bir türü kullanmak için derleyici hatalarına neden olur:

typedef 
{
  FIRST_ITEM,
  SECOND_ITEM
} EnumDef;

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

DAİMA daktilo tanımlama yapılarını ve numaralandırmalarını savunurdum.

Sadece bazı yazımları kaydetmek için değil (cinas amaçlanan;)) değil, daha güvenli olduğu için.


1
Daha da kötüsü, yazım hatası farklı bir etiketle çakışabilir. Bir yapı söz konusu olduğunda bu, tüm programın doğru bir şekilde derlenmesine ve çalışma zamanı tanımlanmamış davranışa sahip olmasına neden olabilir.
MM

3
bu tanım: 'typedef {FIRST_ITEM, SECOND_ITEM} EnumDef;' bir enum tanımlamaz. Yüzlerce büyük program yazdım ve başkalarının yazdıkları programlarda bakım yapma şansını yakaladım. Deneyimin zorluğundan, bir yapı üzerinde typedef kullanmak sadece sorunlara yol açar. Umarım programcı o kadar özürlü değildir ki, bir yapı örneği bildirdiklerinde tam bir tanım yazarken sorun yaşarlar. C Temel değildir, bu nedenle daha fazla karakter yazmak programın çalışması için zararlı değildir.
user3629249

2
Mutlak minimum karakter sayısından daha fazla yazmaktan biraz rahatsızlık duyanlar için, minimum sayıda tuş vuruşuyla uygulama yazmaya çalışan bir gruba katılmanızı önerebilirim. Yeni 'becerilerini' bir çalışma ortamında, özellikle de akran değerlendirmelerini titizlikle yapan bir çalışma ortamında kullanmayın
user3629249

Not: enumÇoğu derleyici 'yanlış' kullanırken garip uyarılar verdiğinden , genellikle bir tür olarak kullanılması önerilmez . Örneğin, bir enum0'a başlatma 'numaralandırmada olmayan bir tamsayı sabiti' uyarısı verebilir. Ayrıca ileri bir bildirime enumde izin verilmez. Bunun yerine int(veya unsigned int) kullanılmalıdır.
yyny

5
Bu örnek derlemiyor, ne de beklemiyordum. Hata ayıklama / test.o test.c: 10: 17: hata: alan eksik 'enum EnuumDef' enum EnuumDef MyEnum; ^ test.c: 10: 8: not: 'enum EnuumDef' enum EnuumDef MyEnum'un ileri bildirimi; ^ 1 hatası oluştu. gnuc, std = c99 ile.
natersoz

30

Linux çekirdeği kodlama stili Bölüm 5 kullanmanın büyük artılarını ve eksilerini (çoğunlukla eksilerini) verir typedef.

Lütfen "vps_t" gibi şeyler kullanmayın.

Yapıları ve işaretçiler için typedef kullanmak bir hatadır . Bir gördüğünüzde

vps_t a;

Kaynakta ne anlama geliyor?

Aksine, diyorsa

struct virtual_container *a;

aslında "a" nın ne olduğunu söyleyebilirsiniz.

Birçok kişi typedef'lerin "okunabilirliğe yardımcı olduğunu" düşünüyor. Öyle değil. Sadece şunlar için yararlıdırlar:

(a) tamamen opak nesneler (burada typedef'in nesnenin ne olduğunu gizlemek için aktif olarak kullanıldığı ).

Örnek: "pte_t" vb. Yalnızca uygun erişimci işlevlerini kullanarak erişebileceğiniz opak nesneler.

NOT! Opaklık ve "erişimci fonksiyonları" kendi başlarına iyi değildir. Onları pte_t gibi şeyler için sahip olmamızın nedeni, orada kesinlikle sıfır portatif olarak erişilebilir bilgi olmasıdır.

(b) "int" ya da "long" olsun , soyutlamanın karışıklığı önlemeye yardımcı olduğu temiz tamsayı türleri .

u8 / u16 / u32, bu kategoriden (d) kategorisine daha iyi uysalar da, mükemmel şekilde ince tip tanımlardır.

NOT! Tekrar - bunun bir nedeni olmalı . Bir şey "imzasız uzun" ise, bunu yapmak için bir neden yok

typedef unsigned long myflags_t;

ancak belirli koşullar altında "imzasız int" ve diğer yapılandırmalar altında "imzasız uzun" olabileceğinin açık bir nedeni varsa, elbette devam edin ve bir typedef kullanın.

(c) tam anlamıyla yazım denetimi için yeni bir tür oluşturmak için seyrek kullandığınızda .

(d) Belirli istisnai durumlarda standart C99 tipleriyle aynı olan yeni tipler.

Gözlerin ve beynin 'uint32_t' gibi standart tiplere alışması kısa bir süre alsa da, bazı insanlar yine de kullanımlarına itiraz ediyorlar.

Bu nedenle, Linux'a özgü 'u8 / u16 / u32 / u64' türlerine ve standart türlerle aynı olan imzalı eşdeğerlerine izin verilir - kendi kodunuzda zorunlu olmamasına rağmen.

Halihazırda bir veya daha fazla tür grubu kullanan mevcut kodu düzenlerken, o koddaki mevcut seçeneklere uymanız gerekir.

(e) Kullanıcı alanında kullanım için güvenli tipler.

Kullanıcı alanı tarafından görülebilen bazı yapılarda, C99 tiplerine gereksinim duymayız ve yukarıdaki 'u32' formunu kullanamayız. Bu nedenle, kullanıcı alanıyla paylaşılan tüm yapılarda __u32 ve benzeri türleri kullanıyoruz.

Belki başka durumlar da vardır, ancak bu kurallardan birini açıkça eşleştiremediğiniz sürece, temelde HİÇBİR ZAMAN asla bir typedef kullanmamak olmalıdır.

Genel olarak, bir imleç veya makul olarak doğrudan erişilebilen öğelere sahip bir yapı hiçbir zaman bir typedef olmamalıdır .


4
'Opaklık ve "erişimci fonksiyonları" kendi başlarına iyi değildir. Birisi nedenini açıklayabilir mi? Bilginin gizlenmesi ve kapsüllenmesinin çok iyi bir fikir olacağını düşünürdüm.
Yawar

5
@Yawar Bu belgeyi yeni okudum ve aynı düşünceye sahiptim. Tabii, C nesne yönelimli değil, ama soyutlama hala bir şey.
Baldrickk

12

Artıları ve eksileri olduğu ortaya çıkıyor. Yararlı bir bilgi kaynağı "Uzman C Programlama" başlıklı kitaptır ( Bölüm 3 ). Kısaca, C'de birden çok ad alanınız vardır: etiketler, türler, üye adları ve tanımlayıcılar . typedefbir tür için bir takma ad ekler ve bunu etiket ad alanında bulur. Yani,

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

iki şeyi tanımlar. Etiket ad alanında bir Etiket ve tür ad alanında bir Etiket yazın. Yani her ikisini birden yapabilirsiniz Type myTypeve struct Tag myTagType. Yasa dışı struct Type myTypeveya benzeri beyanlar Tag myTagType. Ayrıca, böyle bir bildiride:

typedef Type *Type_ptr;

Tipimize bir işaretçi tanımlıyoruz. Yani eğer beyan edersek:

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

Daha sonra var1, var2ve myTagType1yazın ancak işaretçileri myTagType2değil.

Yukarıda adı geçen kitapta, yalnızca tanımlayıcı yapıların, programcıyı yalnızca struct kelimesini yazmaktan kurtardığı için çok yararlı olmadığı belirtilmektedir. Ancak, diğer birçok C programcısı gibi bir itirazım var. Her ne kadar bazen C'de polimorfizm uygulamak istediğinizde bazı isimleri gizlemek için (bu yüzden çekirdek gibi büyük kod tabanlarında tavsiye edilmez), ayrıntılar için burada çok fazla görünmeye yardımcı olur . Misal:

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

yapabilirsin:

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

Böylece bir dış üyeye (flags yapıya ( MyPipe) iç yapı ( ) ile döküm yoluyla erişebilirsiniz. Benim için, (struct MyWriter_ *) s;bu tür bir işlevsellik yapmak istediğiniz her zaman yapmaktan ziyade, tüm türü yayınlamak daha az kafa karıştırıcıdır . Bu durumlarda, özellikle kodunuzda tekniği yoğun bir şekilde kullanırsanız, kısa referanslar büyük önem taşır.

Son olarak, typedefed türlerinin son yönü, makroların aksine, bunları genişletememesidir. Örneğin, aşağıdakilere sahipsiniz:

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

sonra beyan edebilirsin

unsigned X x; but not
unsigned Y y;

Depolama tanımlayıcıları ( volatileve const) için geçerli olmadığından, bunu yapılar için gerçekten önemsemiyoruz .


1
MyPipe *s; MyWriter *self = (MyWriter *) s;ve sıkı bir takma ad kırdınız.
Jonathon Reinhart

@JonathonReinhart Bunun nasıl önlenebileceğinden bahsetmek açıklayıcı olacaktır, örneğin, büyük bir mutlulukla mutlu GTK + bunun etrafında nasıl çalışır: bugzilla.gnome.org/show_bug.cgi?id=140722 / mail.gnome.org/archives/gtk -devel-list / 2004-Nisan / msg00196.html
underscore_d

"typedef bir tür için bir takma ad ekler ve bunu etiket ad alanında bulur. Yani" typedef struct Tag{ ...members... }Type; "iki şeyi tanımlar" anlamsızdır. typedef etiketleri tanımlarsa, buraya 'Tür' de bir etiket olmalıdır. Gerçek şu ki (. Emin veya 2 türleri ve 1 etiket değil) tanımı 2 etiketleri ve 1 türünü tanımlar geçerli: struct Tag, Tagve Type. struct Tagkesinlikle bir çeşittir. Tagbir etikettir. ancak kafa karışıklığı Typebir etiket mi yoksa tip mi
Qwertyzw

12

İleri bildirimlerin typedef ile bile mümkün olduğunu düşünmüyorum. Struct, enum ve union kullanımı bağımlılıklar (bilir) iki yönlü olduğunda bildirimlerin iletilmesine izin verir.

Stil: C ++ 'da typedef kullanımı oldukça mantıklı. Birden fazla ve / veya değişken parametre gerektiren şablonlarla uğraşırken neredeyse gerekli olabilir. Typedef, adlandırmayı düz tutmaya yardımcı olur.

C programlama dilinde öyle değil. Typedef'in kullanımı çoğunlukla veri yapısı kullanımını gizlemekten başka bir amaca hizmet etmez. Bir veri türünü bildirmek için yalnızca {struct (6), enum (4), union (5)} tuş vuruşlarının sayısı kullanıldığından, yapının takma adı için neredeyse hiç kullanım yoktur. Bu veri türü bir birleşim mi yoksa bir yapı mı? Basit, daktilo edilmemiş bildirimi kullanmak, ne tür olduğunu hemen bilmenizi sağlar.

Linux'un nasıl yazıldığına dikkat ederek bu takma adından kaçınarak saçmalık tipedef getiriyor. Sonuç minimalist ve temiz bir stildir.


10
Temizlemek structher yerde tekrar etmeyecekti ... Typedef'in yeni tiplerini yap. Ne kullaniyorsun? Türleri. Biz yok care bir yapı, sendika veya numaralandırma eğer o typedef neden olduğunu,.
GManNickG

13
Hayır, yok o bir enum veya bazı atom tipine karşı, bir yapı ya da birlik bile olsa umrumda. Bir yapıyı bir tamsayıya veya bir işaretçiye (veya bu konu için başka bir türe) zorlayamazsınız; bu, bazen bir bağlam depolamak zorunda olduğunuz tek şeydir. 'Struct' veya 'union' anahtar kelimelerinin bulunması, mantığın yerini iyileştirir. Kimse yapının içinde ne olduğunu bilmen gerektiğini söylemiyor .
Bernd Jendrissek

1
@BerndJendrissek: Yapılar ve birlikler diğer türlerden farklıdır, ancak istemci kodu bu iki şeyden hangisinin (yapı veya birlik) FILEolduğu gibi bir şeyle ilgilenmesi gerekir mi?
supercat

4
@supercat FILE typedef'in iyi bir kullanımıdır. Bence typedef aşırı kullanılmış , dilin bir yanlış özelliği değil. Her şey için typedef kullanan IMHO "spekülatif aşırı genel" kod kokusudur. Değişkenleri asla FILE * foo olarak bildirdiğinize, asla FILE foo olarak bildirdiğinize dikkat edin. Benim için bu önemli.
Bernd Jendrissek

2
@supercat: "Dosya tanıtıcı değişkenler FILE * yerine FILE türündeyse ..." Ancak bu tam olarak typedefs'in etkinleştirdiği belirsizliktir! Sadece bir DOSYA * almayı fopen yapmaya alışkınız, bu yüzden endişelenmiyoruz, ancak typedef'i her eklediğinizde başka bir bilişsel yük ekliyorsunuz: Bu API foo_t args veya foo_t * istiyor mu? Açıkça 'yapı' taşımak, işlev tanımı başına birkaç karakter daha pahasına olursa, muhakemenin yerini iyileştirir.
Bernd Jendrissek

4

Temel bilgilerle başlayalım ve yolumuza devam edelim.

İşte Yapı tanımına bir örnek:

struct point
  {
    int x, y;
  };

İşte adı point isteğe bağlıdır.

Bir Yapı, tanımı sırasında veya sonrasında bildirilebilir.

Tanımlama sırasında bildirme

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

Tanımdan sonra bildirme

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

Şimdi, yukarıdaki son durumu dikkatlice not edin; struct pointkodunuzda daha sonraki bir noktada bu türü oluşturmaya karar verirseniz, bu tür yapıları bildirmek için yazmanız gerekir .

Enter typedef. Programınızda daha sonra aynı planı kullanarak yeni Yapı (Yapı özel bir veri türüdür) oluşturmak typedefistiyorsanız, bazı yazma işlemlerini ilerletebildiğiniz için tanımı sırasında kullanmak iyi bir fikir olabilir.

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

Points first_point, second_point;

Özel türünüzü adlandırırken dikkat edin

Hiçbir şey özel tür adınızın sonunda _t son ekini kullanmanızı engellemez, ancak POSIX standardı standart kitaplık türü adlarını belirtmek için _t sonekinin kullanımını saklı tutar.


3

(isteğe bağlı olarak) yapıya verdiğiniz ad etiket adı olarak adlandırılır ve belirtildiği gibi kendi içinde bir tür değildir. Türüne ulaşmak için struct önekini gerektirir.

GTK + bir yana, tagname yapı türü için bir typedef gibi yaygın olarak kullanılan bir şey olduğundan emin değilim, bu yüzden tanınan C ++ ve struct anahtar sözcüğünü atlayabilir ve tagname türü adı olarak da kullanabilirsiniz:


    struct MyStruct
    {
      int i;
    };

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


1

typedef, ko-bağımlı bir veri yapıları seti sağlamayacaktır. Bu typdef ile yapamazsınız:

struct bar;
struct foo;

struct foo {
    struct bar *b;
};

struct bar {
    struct foo *f;
};

Tabii ki her zaman ekleyebilirsiniz:

typedef struct foo foo_t;
typedef struct bar bar_t;

Bunun amacı tam olarak nedir?


1

A> typdef , veri türleri için daha anlamlı eş anlamlılar oluşturulmasına izin vererek bir programın anlamına ve belgelenmesine yardımcı olur . Ek olarak, bir programı taşınabilirlik sorunlarına (K&R, pg147, C prog lang) göre parametrelendirmeye yardımcı olurlar.

B> bir yapı bir tür tanımlar . Yapılar, tek bir birim olarak kullanım kolaylığı (K&R, pg127, C prog lang.) İçin bir çeşit koleksiyonunun uygun şekilde gruplandırılmasını sağlar.

Bir yapının tip tanımlanması yukarıdaki A'da açıklanmaktadır.

D> Bana göre, yapılar özel türler veya kaplar veya koleksiyonlar veya ad alanları veya karmaşık türlerken, typdef sadece daha fazla takma ad oluşturmak için bir araçtır.


0

C99 'da çıkıyor typedef gereklidir. Eski, ancak bir çok araç (ala HackRank) saf C uygulaması olarak c99 kullanıyor. Ve burada typedef gereklidir.

Gereksinim değiştiğinde değişmeleri gerektiğini (belki iki C seçeneğine sahip olduklarını) söylemiyorum, sitede röportajlar yapmak için çalışanlarımız SOL olurdu.


2
"C99'da çıkıyor typedefgerekiyor." Ne demek istiyorsun?
Julien Lopez

Soru C, değil C++. Gelen Ctypedefs olan 'gereklidir' (ve büyük olasılıkla her zaman olacaktır). 'Zorunlu' olduğu gibi, bir değişken bildiremezsiniz Point varName;ve türü struct Point;, a olmadan eşanlamlı hale getirebilirsiniz typedef struct Point Point;.
yyny

0

'C' programlama dilinde 'typedef' anahtar sözcüğü, bazı nesneler için yeni bir ad bildirmek için kullanılır (struct, array, function..enum type). Örneğin, bir 'struct-s' kullanacağım. 'C' de genellikle 'ana' fonksiyonun dışında bir 'yapı' ilan ederiz. Örneğin:

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

}

Bir yapı türü kullanmaya her karar verdiğimde bu anahtar kelime 'struct' bir şey 'ad' gerekir. 'Typedef' basitçe bu türü yeniden adlandıracak ve bu yeni adı programımda her istediğimde kullanabiliyorum. Yani kodumuz:

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

}

Tüm programınızda kullanılacak bazı yerel nesneleriniz varsa (yapı, dizi, değerli) bir 'typedef' kullanarak bir ad verebilirsiniz.


-2

C dilinde, struct / union / enum, C dili önişlemcisi tarafından işlenen makro talimatıdır ("#include" ve diğerlerini tedavi eden önişlemciyle hata yapmayın)

yani :

struct a
{
   int i;
};

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

b yapısı aşağıdaki gibi harcanır:

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

ve böylece, derleme zamanında yığın gibi gelişir: b: int ai int i int j

bu yüzden de kendini ifade eden yapılara sahip olmak zor, C önişlemci sonlandırılamayan bir déclaration döngüsünde.

typedef türü belirleyicidir, yani sadece C derleyicisi tarafından işlenir ve derleyici kod uygulamasını optimize etmek için istediği gibi yapabilir. Ayrıca, önişlemci gibi aptalca yapıları ile aptalca bir tür üyesi harcamak değil, ancak daha karmaşık referans inşaat algoritması kullanın, bu yüzden inşaat gibi:

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

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

izin verilir ve tamamen işlevseldir. Bu uygulama aynı zamanda compilator tipi dönüştürmeye erişim sağlar ve yürütme iş parçacığı başlatma işlevlerinin uygulama alanından ayrıldığında bazı hata efektlerini kaldırır.

Bu, C typedef'lerin C ++ sınıfına yalnız yapılardan daha yakın olduğu anlamına gelir.


3
Kendini referans alan yapılara sahip olmak hiç de zor değil. Sonraki yapı {sonraki yapı * *; int şey; }
Bernd Jendrissek

4
...ne? Ve s'nin çözünürlüğünü tanımlamak için önişlemciyi söylemek yeterince kötü, ama yazınızın geri kalanı o kadar kafa karıştırıcı ki, ondan herhangi bir mesaj almayı zor buluyorum. Söyleyebileceğim tek şey, olsa da, bir sivil senin fikrin olduğunu d ileriye ilan veya opak (işaretçi) üye olarak kullanılamaz tamamen yanlıştır. 1. örneğinizde, önemsiz bir a içerebilir , gerekli değildir. Sadece makro genişlemenin aptal parçaları olduğu ve onlara devrimci yeni güçler verdiği iddiası acı verici bir şekilde yanlıştırstructstypedeftypedefstructstruct bstruct a *typedefstructtypedef
underscore_d
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.