C programlama dili standartlarına uygun bir yapı nasıl başlatılır


466

Bir yapı elemanını başlatmak, bildirimde ve başlatmada bölünmek istiyorum. Sahip olduğum şey bu:

typedef struct MY_TYPE {
  bool flag;
  short int value;
  double stuff;
} MY_TYPE;

void function(void) {
  MY_TYPE a;
  ...
  a = { true, 15, 0.123 }
}

Bu, MY_TYPEC programlama dili standartlarına (C89, C90, C99, C11, vb.) Göre yerel bir değişken tanımlamanın ve başlatmanın yolu mu? Yoksa daha iyi veya en azından çalışan bir şey var mı?

Güncelleme, her alt öğeyi ihtiyaçlarıma göre ayarladığım statik bir başlatma öğesine sahip oldum.


3
gerçekten daha iyi bir cevap kabul etmelisiniz, bazı kötü kodlama kılavuzu kullanmak zorunda olduğunuzu görüyorum, ama yine de diğer insanlara bunu yapmanın doğru yolu olduğunu önermemelisiniz ..
Karoly Horvath

@KarolyHorvath, iyi cevapların çoğu C99'a özgüdür. Belki de sorum bir stackoverflow.com/questions/6624975/… kopyası ?
cringe

eğer orijinal niyetiniz buysa, muhtemelen evet, ama o zaman 1) oylar çok yanıltıcı olurdu. 2) en iyi arama sonuçlarından bu C99 yolunu gösteren tek ..... bu C99 gösteri için bu sayfayı yeniden kullanmak daha iyi olurdu ... (görünüşe göre insanlar nasıl göstermek için bu sayfayı bağlantı başladı yap)
Karoly Horvath

10
İlginçtir ki (ve büyük ölçüde yükseltilmiş) cevabın aslen yayınlandığı gibi bile soruyu cevaplamaması ilginçtir. Belirlenmiş ilklendiriciler etmektir OP'ın sorunu çözmek değil bölmek başlatma gelen beyanı. 1999 öncesi C için, tek gerçek çözüm her üyeye atamaktır; C99 ve sonrası için, CesarB'nin cevabında olduğu gibi bileşik bir değişmez çözümdür. (Tabii ki, göstergeleri olan veya olmayan gerçek bir başlatıcı daha iyi olurdu, ancak görünüşe göre OP gerçekten kötü bir kodlama standardı ile üzülmüştü.)
Keith Thompson

32
Açıkçası, "ANSI C" terimi şimdi ANSI tarafından benimsenen 2011 ISO standardını ifade etmektedir. Ancak pratikte "ANSI C" terimi genel olarak (resmi olarak kullanılmayan) 1989 standardını ifade eder. Örneğin, "gcc -ansi" hala 1989 standardını uygulamaktadır. 1990, 1999 ve 2011 standartlarını yayınlayan ISO olduğundan, "ANSI C" teriminden kaçınmak ve herhangi bir karışıklık olasılığı varsa standardın tarihini belirtmek en iyisidir.
Keith Thompson

Yanıtlar:


728

(ANSI) C99'da, bir yapıyı başlatmak için belirlenmiş bir başlatıcı kullanabilirsiniz:

MY_TYPE a = { .flag = true, .value = 123, .stuff = 0.456 };

Düzenleme: Diğer üyeler sıfır olarak başlatılır: "Atlanan alan üyeleri, statik depolama süresi olan nesnelerle aynı şekilde örtük olarak başlatılır." ( https://gcc.gnu.org/onlinedocs/gcc/Designated-Inits.html )


87
harika, teşekkürler. em bile iç içe yerleştirebilirsiniz: statik oxeRay2f ray = {.unitDir = {.x = 1.0f, .y = 0.0f}};
orion elenzil

4
Bu soruya hiç cevap vermiyor. OP, başlatmayı deklarasyondan ayırmak istiyor.
osvein

3
C99! = ANSI C - Yani bu C89'da veya C90'da çalışmaz.
Tim Wißmann

3
C99 olan ANSI C bakınız bu
Cutberto Ocampo

3
@CutbertoOcampo Şimdi ne olduğunu anlıyorum. Orijinal soru ANSI C'de çözümler istiyordu, açıkçası sadece C89 için cevaplar istiyordu. 2016'da soru düzenlendi ve "ANSI C" kaldırıldı, bu da bu cevap ve yorumların neden "(ANSI) C99" dan bahsettiğini anlamayı zorlaştırıyor.
Étienne

198

Bunu bileşik bir değişmezle yapabilirsiniz . Bu sayfaya göre, C99'da ( ANSI C olarak da sayılır ) çalışır.

MY_TYPE a;

a = (MY_TYPE) { .flag = true, .value = 123, .stuff = 0.456 };
...
a = (MY_TYPE) { .value = 234, .stuff = 1.234, .flag = false };

Başlatıcılardaki adlandırma isteğe bağlıdır; ayrıca şunu da yazabilirsiniz:

a = (MY_TYPE) { true,  123, 0.456 };
...
a = (MY_TYPE) { false, 234, 1.234 };

Öyleyse, belirtilen başlatıcılar aynı anda beyan ettiğiniz ve tanımladığınız zamanlar içindir, ancak bileşik değişmezler zaten bildirilmiş bir değişken tanımladığınızda içindir?
Geremia

@Alemi, her iki durumda da yapıyı tanımlamanız gerekir, ancak belirlenmiş başlatıcılarla, yapıdaki değişiklikler durumunda kodu daha okunabilir ve hatalara karşı daha dayanıklı hale getirirsiniz.
hoijui

2
Bu biraz zor. Birçok kez (bazıları başarılı) belirlenmiş başlatıcıları kullanmaya çalıştım. Bununla birlikte, şimdi sadece ve @ philant örneğiniz sayesinde), başlatıcı nesne oluşturma sırasında kullanılmazsa bir dökümün olması gerektiği anlaşılmıştır. Ancak, şimdi de başlangıç ​​değerlerini nesne oluşturma dışında bir zamanda kullanılırsa (örneğinizde olduğu gibi), o zaman tam olarak belirlenmiş bir başlatıcı değil, bileşik bir değişmez olarak adlandırıldığını öğrendim . ideone.com/Ze3rnZ
sherrellbc

93

ANSI C 99 hakkında zaten bir cevap aldığını görüyorum, bu yüzden ANSI C 89 hakkında bir kemik atacağım. ANSI C 89 bir yapıyı bu şekilde başlatmanıza izin verir:

typedef struct Item {
    int a;
    float b;
    char* name;
} Item;

int main(void) {
    Item item = { 5, 2.2, "George" };
    return 0;
}

Hatırlanması gereken önemli bir şey, yapıda bir nesneyi / değişkeni bile başlattığınız anda, diğer tüm değişkenleri varsayılan değere sıfırlanır.

Yapınızdaki değerleri başlatmazsanız, tüm değişkenler "çöp değerleri" içerecektir.

İyi şanslar!


3
Başlatmada değişkenler kullanılabilir mi?
Camgöbeği

2
@Cyan Otomatik saklama süresine sahip nesneler içindir. Bkz. 6.7.9 13) open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf . Çünkü küresel nesneler gerçek anlamıyla sınırlıdır. Sabit olsalar bile, diğer global nesneleri bile kullanamazsınız.
PSkocik

Yani C 99 ile tek fark atamaları belirleyememeniz?
Akaisteph7

42

a = (MYTYPE){ true, 15, 0.123 };

C99'da iyi olur


1
Dürüstçe cevabın en yararlı olduğunu düşünüyorum, ama her neyse.
user12211554

22

Neredeyse tamamladın ...

MY_TYPE a = { true,15,0.123 };

'Struct initialize c' hakkında hızlı arama bana bunu gösterir


1
Bence bu standart C için geçerli, ancak ANSI C (99?) İçin geçerli değil. Ayrıca bir kerede beyan ve başlatma yapmama izin vermeyecek kodlama kurallarına bağlıyım, bu yüzden onu bölmek ve her alt öğeyi başlatmak zorundayım.
cringe

8
Başlatma yalnızca bildirim noktasında gerçekleşebilir. 'Başlatır' demek budur. Aksi takdirde, tanımlanmamış bir değerin değişkeninizin / yapınızın temel değeri olmasına izin verir ve daha sonra bir değer atarsınız.
Kieveli

8
um ... Kasten kötü olan kodlama kurallarınız mı var? Belki de onları "Kaynak Edinimi Başlatmadır" olarak yazan adamı tanıtmalısınız ( en.wikipedia.org/wiki/RAII )
James Curran

Güven bana, gerçekten, gerçekten, bu noktada bu kuralların bir kısmını değiştiremiyorum. Kodlamak korkunç geliyor. Ve 70'lerin dışında Revizyon numarası gibi yönetim bilgilerine sahip statik kod başlıkları gibi birçok başka kural var. Kaynak değişmemiş olsa bile her sürüm için değişiklikler ...
cringe

19

C programlama dili standardı ISO / IEC 9899: 1999 (genellikle C99 olarak bilinir) , bir yapının veya birleşimin üyelerini aşağıdaki gibi başlatmak için belirlenmiş bir başlatıcı kullanılmasına izin verir :

MY_TYPE a = { .stuff = 0.456, .flag = true, .value = 123 };

ISO / IEC 9899: 1999 standardının paragraph 7bölümünde şu şekilde tanımlanmıştır 6.7.8 Initialization:

Bir belirleyici forma sahipse
. tanımlayıcı
geçerli nesne (aşağıda tanımlanır) yapıya veya birleşim tipine sahip olacak ve tanımlayıcı bu tipteki bir üyenin adı olacaktır.

O Not paragraph 9Aynı bölümün belirtiyor:

Açıkça aksi belirtilmedikçe, bu alt hükmün amaçları doğrultusunda, isimsiz yapı ve birlik tipi nesnelerin üyeleri başlatmaya katılmazlar. Yapı nesnelerinin adsız üyeleri, başlatma işleminden sonra bile belirsiz bir değere sahiptir.

Ancak GNU GCC uygulamasında atlanan üyeler sıfır veya sıfır benzeri tipe uygun değer olarak başlatılır. Bölüm 6.27 GNU GCC belgelerinin Atanmış Başlatıcıları bölümünde belirtildiği gibi :

Atlanan alan üyeleri, statik depolama süresine sahip nesnelerle aynı şekilde başlatılır.

Microsoft Visual C ++ derleyicisi, resmi blog yazısı C ++ Uygunluk Yol Haritası'na göre 2013 sürümünden bu yana belirlenen başlatıcıları desteklemelidir . Paragraf Initializing unions and structsait ilklendiriciler MSDN Visual Studio belgelerine makalesinde isimsiz üyeleri GNU GCC benzer sıfır gibi uygun değerlere başlatıldı düşündürmektedir.

ISO / IEC 9899: 1999'un yerini alan ISO / IEC 9899: 2011 standardı (yaygın olarak C11 olarak bilinir), bölüm altında belirtilen başlatıcıları tutar 6.7.9 Initialization. Ayrıca tutarparagraph 9 değişmeden kalır.

ISO / IEC 9899: 2011'in yerini alan yeni ISO / IEC 9899: 2018 standardı (yaygın olarak C18 olarak bilinir), bölüm altındaki belirlenmiş başlatıcıları korur 6.7.9 Initialization. Ayrıca paragraph 9değişmeden kalır.


3
Başlangıçta bunu kabul edilen cevaba düzenleme olarak önerdim, ancak reddedildi. Böylece bir cevap olarak gönderiyorum.
PF4Public

"İsimsiz üye" nin "atlanan alanlar" anlamına gelmediğini, ancak sendika gibi "isimsiz üyeler" anlamına geldiğine inanıyorum struct thing { union { char ch; int i; }; };. Standart daha sonra şöyle der: "Brace ile çevrelenmiş bir listede toplamın öğeleri veya üyeleri olduğundan daha az başlatıcı varsa veya dizideki öğelere göre bilinen boyuttaki bir diziyi başlatmak için kullanılan bir dizgi değişmezinde daha az karakter varsa, agreganın geri kalanı, statik depolama süresine sahip nesnelerle aynı şekilde başlatılır. "
emersion

14

Ron Nuni'nin dediği gibi:

typedef struct Item {
    int a;
    float b;
    char* name;
} Item;

int main(void) {
    Item item = {5, 2.2, "George"};
    return 0;
}

Hatırlanması gereken önemli bir şey: yapıdaki bir nesneyi / değişkeni bile başlattığınız anda, diğer tüm değişkenleri varsayılan değere sıfırlanır.

Yapınızdaki değerleri başlatmazsanız (yani, yalnızca bu değişkeni bildirirseniz), tümü variable.members"çöp değerleri" içerecektir, yalnızca bildirim yerel olduğunda !

Beyan genel veya statik ise (bu durumda olduğu gibi), başlatılmamış variable.membersolanların tümü otomatik olarak şu şekilde başlatılır:

  • 0 tamsayılar ve kayan nokta için
  • '\0'çünkü char(elbette bu aynı 0vechar bir tam sayı türüdür)
  • NULL işaretçiler için.

Yerel / küresel hakkında ilginç olan bu, yapısal zaman değerleri için de geçerlidir. Yerel bir tane vardı ve bir durumda DST açıktı, başka bir şey değildi, yaptığım bir şey yüzünden değil. DST (tm_isdst alanı) kullanmıyordum, bu strptime'den oldu, ancak time_t'e dönüştürüldüğümde bir saat kapalıydı.
Alan Corey

3
void function(void) {
  MY_TYPE a;
  a.flag = true;
  a.value = 15;
  a.stuff = 0.123;
}

25
Bir kez 'başlatma ödevden farklıdır' duydum. öyleyse bu, başlatma yerine bir görev değil mi?
Hayri Uğur Koltuk

2

MS C99'a güncelleme yapmadıysa, MY_TYPE a = {true, 15,0.123};



2

Bu cevapların hiçbirini beğenmedim ve kendi cevaplarımı aldım. Bunun ANSI C olup olmadığını bilmiyorum, varsayılan modda sadece GCC 4.2.1. Basamaklamaları asla hatırlayamıyorum, bu yüzden verilerimin bir alt kümesiyle başlıyorum ve kapanana kadar derleyici hata mesajlarıyla savaşıyorum. Okunabilirlik ilk önceliğim.

    // in a header:
    typedef unsigned char uchar;

    struct fields {
      uchar num;
      uchar lbl[35];
    };

    // in an actual c file (I have 2 in this case)
    struct fields labels[] = {
      {0,"Package"},
      {1,"Version"},
      {2,"Apport"},
      {3,"Architecture"},
      {4,"Bugs"},
      {5,"Description-md5"},
      {6,"Essential"},
      {7,"Filename"},
      {8,"Ghc-Package"},
      {9,"Gstreamer-Version"},
      {10,"Homepage"},
      {11,"Installed-Size"},
      {12,"MD5sum"},
      {13,"Maintainer"},
      {14,"Modaliases"},
      {15,"Multi-Arch"},
      {16,"Npp-Description"},
      {17,"Npp-File"},
      {18,"Npp-Name"},
      {19,"Origin"}
    };

Veriler, başka bir şeye masaj yapmak için arama-değiştirdiğiniz sekmeyle ayrılmış bir dosya olarak hayata başlayabilir. Evet, bu Debian meselesi. Yani {} dizisini gösteren bir dış çift, daha sonra içerideki her yapı için başka bir çift. Arasındaki virgüllerle. Bir başlığa şeyler koymak kesinlikle gerekli değildir, ancak benim yapılarımda yaklaşık 50 öğe var, bu yüzden her ikisini de kodumdan uzak tutmak için ayrı bir dosyada istiyorum ve böylece değiştirilmesi daha kolay.


3
Yapı dizisinin başlatılması hakkında hiçbir soru yoktu! Demek istediğim, cevabınız ek yük içeriyor.
Victor Signaevskyi

Demek istediğim, daha sonra her değeri ayarlamak için = kullanarak vs zaten içindeki değerleri ile yapı ilan edildi sanırım.
Alan Corey

Bu ilke ister bir yapı ister bir dizi olsun, geçerlidir. = Kullanmak gerçekten başlatma değil, düşünmezdim.
Alan Corey

0

C'deki yapı şöyle bildirilebilir ve başlatılabilir:

typedef struct book
{
    char title[10];
    char author[10];
    float price;
} book;

int main() {
    book b1={"DS", "Ajay", 250.0};

    printf("%s \t %s \t %f", b1.title, b1.author, b1.price);

    return 0;
}

0

Okudum başlatılıyor Agrega türleri için Microsoft Visual Studio 2015 Belgeleri , henüz ile başlatılıyor her türlü{...} burada açıklandı, ancak nokta ile başlatma '' işaretleme '' olarak belirtilmedi. Ayrıca çalışmaz.

C99 standart bölümü 6.7.8 Başlatma, göstergelerin olasılığını açıklar, ancak bence karmaşık yapılar için gerçekten açık değildir. PDF olarak C99 standart .

Aklımda, daha iyi olabilir

  1. = {0};Tüm statik veriler için -initialization komutunu kullanın . Makine kodu için daha az çaba harcanır.
  2. Başlamak için makrolar kullanın, örneğin

    typedef MyStruct_t{ int x, int a, int b; } MyStruct; define INIT_MyStruct(A,B) { 0, A, B}

Makro uyarlanabilir, argüman listesi değiştirilmiş yapı içeriğinden bağımsız olabilir. Daha az elemanın başlatılması gerekmekte. İç içe yapı için de uygundur. 3. Basit bir form: Bir altyordamda başlat:

void init_MyStruct(MyStruct* thiz, int a, int b) {
  thiz->a = a; thiz->b = b; }

C. Kullanımında nesneye dayalı gibi bu rutin görünüyor thiz, değil thisde C ++ ile derlemek!

MyStruct data = {0}; //all is zero!
init_MyStruct(&data, 3, 456);

0

Yapımı başlatmak için güzel bir yol arıyordum ve aşağıdakileri kullanmalıyım (C99). Bu, ya tek bir yapıyı ya da bir yapı dizisini düz tiplerle aynı şekilde başlatmama izin verir.

typedef struct {
    char *str;
    size_t len;
    jsmntok_t *tok;
    int tsz;
} jsmn_ts;

#define jsmn_ts_default (jsmn_ts){NULL, 0, NULL, 0}

Bu kodda şu şekilde kullanılabilir:

jsmn_ts mydata = jsmn_ts_default; /* initialization of a single struct */

jsmn_ts myarray[10] = {jsmn_ts_default, jsmn_ts_default}; /* initialization of
                                                    first 2 structs in the array */

Ancak bu, bir yapı dizisini varsayılan değerlere başlatmaz. Bu verilen değerlere dizideki ilk yapı başlatır jsmn_ts_default, ama dizi üyeleri başlatılır olduğu, bu araçların, statik depolama süresi sanki yapıların geri kalan başlatılır NULLve 0. Ancak, değiştirirseniz #define jsmn_ts_default (jsmn_ts){"default", sizeof "default", NULL, 0}yalnızca ilk dizi öğesinin bu şekilde başlatıldığını göreceksiniz.
ex nihilo

Evet, daha fazla testten döndüm, int a[10] = {1};==1
gönderiyi
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.