Cara menginisialisasi struct sesuai dengan standar bahasa pemrograman C

466

Saya ingin menginisialisasi elemen struct, dibagi dalam deklarasi dan inisialisasi. Inilah yang saya miliki:

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

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

Apakah ini cara untuk mendeklarasikan dan menginisialisasi variabel lokal MY_TYPEsesuai dengan standar bahasa pemrograman C (C89, C90, C99, C11, dll.)? Atau ada yang lebih baik atau setidaknya berfungsi?

Pembaruan Saya akhirnya memiliki elemen inisialisasi statis di mana saya mengatur setiap subelemen sesuai dengan kebutuhan saya.

ngeri
sumber
3
Anda benar-benar harus menerima jawaban yang lebih baik, saya melihat Anda harus menggunakan beberapa panduan pengkodean yang buruk, tetapi Anda tetap tidak menyarankan kepada orang lain bahwa itu adalah cara yang tepat untuk melakukannya ..
Karoly Horvath
@ KarolyHorvath baik, sebagian besar jawaban yang baik adalah khusus untuk C99. Mungkin pertanyaan saya adalah duplikat dari stackoverflow.com/questions/6624975/… ?
ngeri
jika itu niat awal Anda, maka mungkin ya, tapi kemudian 1) suara akan sangat menyesatkan. 2) dari hasil pencarian teratas, ini adalah satu-satunya yang menunjukkan cara C99 ..... akan lebih baik untuk menggunakan kembali halaman ini untuk demonstrasi C99 ... (tampaknya orang-orang mulai menautkan halaman ini untuk menunjukkan cara melakukannya)
Karoly Horvath
10
Menarik bahwa jawaban yang diterima (dan sangat tervvotasikan) tidak benar-benar menjawab pertanyaan, bahkan seperti yang diposting sebelumnya. Inisialisasi yang ditunjuk tidak mengatasi masalah OP, yaitu untuk memisahkan deklarasi dari inisialisasi. Untuk pra-1999 C, satu-satunya solusi nyata adalah dengan menugaskan masing-masing anggota; untuk C99 dan yang lebih baru, kata majemuk majemuk, seperti dalam jawaban CesarB, adalah solusinya. (Tentu saja penginisialisasi aktual, dengan atau tanpa penunjuk, akan lebih baik, tetapi tampaknya OP dibebani dengan standar pengkodean yang sangat buruk.)
Keith Thompson
32
Sebenarnya, istilah "ANSI C" sekarang mengacu pada standar ISO 2011, yang telah diadopsi ANSI. Tetapi dalam prakteknya istilah "ANSI C" biasanya mengacu pada standar 1989 (secara resmi usang). Misalnya, "gcc -ansi" masih memberlakukan standar 1989. Karena ISO yang menerbitkan standar 1990, 1999, dan 2011, yang terbaik adalah menghindari istilah "ANSI C", dan merujuk ke tanggal standar jika ada kemungkinan kebingungan.
Keith Thompson

Jawaban:

728

Di (ANSI) C99, Anda dapat menggunakan penginisialisasi yang ditunjuk untuk menginisialisasi struktur:

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

Sunting: Anggota lain diinisialisasi sebagai nol: "Anggota bidang yang dihilangkan secara implisit diinisialisasi sama dengan objek yang memiliki durasi penyimpanan statis." ( https://gcc.gnu.org/onlinedocs/gcc/Designated-Inits.html )

filant
sumber
87
keren terima kasih. Anda bahkan dapat membuat sarangnya: static oxeRay2f ray = {.unitDir = {.x = 1.0f, .y = 0.0f}};
orion elenzil
4
Ini sama sekali tidak menjawab pertanyaan. OP ingin memisahkan inisialisasi dari deklarasi.
osvein
3
C99! = ANSI C - Jadi ini tidak berfungsi di C89, atau di C90.
Tim Wißmann
3
C99 adalah ANSI C, lihat ini
Cutberto Ocampo
3
@ CutbertoOcampo Saya mengerti apa yang terjadi sekarang. Pertanyaan aslinya adalah meminta solusi dalam ANSI C, jelas dia menginginkan jawaban untuk C89 saja. Pada 2016 pertanyaannya diedit dan "ANSI C" dihapus, yang sekarang membuatnya sulit untuk memahami mengapa jawaban dan komentar ini menyebutkan "(ANSI) C99".
Étienne
198

Anda dapat melakukannya dengan kata majemuk literal . Menurut halaman itu, ia berfungsi di C99 (yang juga dihitung sebagai ANSI C ).

MY_TYPE a;

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

Penunjukan dalam inisialisasi adalah opsional; Anda juga bisa menulis:

a = (MY_TYPE) { true,  123, 0.456 };
...
a = (MY_TYPE) { false, 234, 1.234 };
CesarB
sumber
Jadi, inisialisasi yang ditunjuk untuk ketika Anda mendeklarasikan dan mendefinisikan pada saat yang sama, tetapi literal majemuk adalah untuk ketika Anda mendefinisikan variabel yang sudah dideklarasikan?
Geremia
@Geremia Anda harus terlebih dahulu mendefinisikan struct dalam kedua kasus, tetapi dengan inisialisasi yang ditunjuk, Anda membuat kode lebih mudah dibaca, dan lebih tahan terhadap kesalahan, jika terjadi perubahan dalam struct.
hoijui
2
Ini sedikit rumit. Banyak kali (beberapa berhasil) saya telah mencoba menggunakan inisialisasi yang ditunjuk. Namun, sekarang hanya menjadi jelas (terima kasih untuk contoh Anda dan @ philant) bahwa harus ada pemeran jika penginisialisasi tidak digunakan pada saat pembuatan objek. Namun, saya sekarang juga telah belajar bahwa jika inisialisasi digunakan pada waktu selain dari penciptaan objek (seperti dalam contoh Anda) maka itu disebut sebagai senyawa literal , bukan hanya initializer yang ditunjuk . ideone.com/Ze3rnZ
sherrellbc
93

Saya melihat Anda sudah menerima jawaban tentang ANSI C 99, jadi saya akan melemparkan tulang tentang ANSI C 89. ANSI C 89 memungkinkan Anda untuk menginisialisasi struct dengan cara ini:

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

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

Suatu hal yang penting untuk diingat, pada saat Anda menginisialisasi bahkan satu objek / variabel dalam struct, semua variabel lainnya akan diinisialisasi ke nilai default.

Jika Anda tidak menginisialisasi nilai dalam struct Anda, semua variabel akan berisi "nilai sampah".

Semoga berhasil!

Ron Nuni
sumber
3
Apakah mungkin menggunakan variabel dalam inisialisasi?
Cyan
2
@Cyan Ini untuk objek dengan durasi penyimpanan otomatis. Lihat 6.7.9 13) di open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf . Untuk objek global cukup banyak terbatas pada literal. Anda bahkan tidak dapat menggunakan objek global lainnya, bahkan jika itu adalah const.
PSkocik
Jadi satu-satunya perbedaan dengan C 99 adalah bahwa Anda tidak dapat menentukan penunjukan?
Akaisteph7
42

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

akan baik-baik saja di C99

qrdl
sumber
1
Jujur saya pikir jawabannya adalah yang paling berguna, tetapi apa pun itu.
user12211554
22

Anda hampir mendapatkannya ...

MY_TYPE a = { true,15,0.123 };

Pencarian cepat pada 'struct initialize c' menunjukkan ini kepada saya

Kieveli
sumber
1
Saya pikir ini berlaku untuk standar C, tetapi tidak untuk ANSI C (99?). Saya juga terikat pada aturan pengkodean yang tidak memungkinkan saya untuk melakukan deklarasi dan inisialisasi sekaligus, jadi saya harus membaginya dan menginisialisasi setiap subelemen tunggal.
ngeri
8
Inisialisasi hanya dapat terjadi pada titik deklarasi. Itulah artinya 'menginisialisasi'. Kalau tidak, Anda membiarkan nilai yang tidak ditentukan menjadi nilai awal variabel / struct Anda, dan kemudian menetapkan nilai nanti.
Kieveli
8
um ... Anda memiliki aturan pengkodean yang sengaja buruk? Mungkin Anda harus memperkenalkan orang yang menulisnya ke "Resource Acquisition Is Inisialisasi" ( en.wikipedia.org/wiki/RAII )
James Curran
Percayalah, saya benar-benar, benar-benar, tidak dapat mengubah sedikit aturan ini pada saat ini. Rasanya mengerikan untuk mengkodekannya. Dan ada banyak aturan lain langsung dari tahun 70-an, seperti header kode statis dengan informasi manajemen seperti nomor Revisi. Perubahan untuk setiap rilis, bahkan jika sumbernya tidak berubah ...
ngeri
19

Standar bahasa pemrograman C ISO / IEC 9899: 1999 (umumnya dikenal sebagai C99) memungkinkan seseorang untuk menggunakan inisialisasi yang ditunjuk untuk menginisialisasi anggota struktur atau gabungan sebagai berikut:

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

Ini didefinisikan pada paragraph 7bagian 6.7.8 InitializationISO / IEC 9899: 1999 sebagai:

Jika penunjuk memiliki formulir
. pengidentifikasi
maka objek saat ini (didefinisikan di bawah) harus memiliki tipe struktur atau gabungan dan pengidentifikasi akan menjadi nama anggota jenis tersebut.

Perhatikan bahwa paragraph 9bagian yang sama menyatakan bahwa:

Kecuali jika dinyatakan secara eksplisit sebaliknya, untuk keperluan subayat ini anggota yang tidak disebutkan namanya objek struktur dan jenis serikat tidak berpartisipasi dalam inisialisasi. Anggota objek struktur yang tidak disebutkan namanya memiliki nilai tak tentu bahkan setelah inisialisasi.

Dalam implementasi GCC GNU namun anggota dihilangkan diinisialisasi sebagai nol atau nilai sesuai jenis seperti nol. Seperti yang dinyatakan dalam bagian 6.27 Inisialisasi yang Ditunjuk untuk dokumentasi GNU GCC:

Anggota bidang yang dihilangkan secara implisit diinisialisasi sama dengan objek yang memiliki durasi penyimpanan statis.

Kompiler Microsoft Visual C ++ harus mendukung inisialisasi yang ditunjuk sejak versi 2013 menurut posting blog resmi C ++ Roadform Kesesuaian . Ayat Initializing unions and structsdari initializers artikel di MSDN Visual dokumentasi Studio menunjukkan bahwa anggota yang tidak disebutkan namanya diinisialisasi ke nol seperti nilai-nilai yang sesuai sama dengan GNU GCC.

ISO / IEC 9899: 2011 standar (umumnya dikenal sebagai C11) yang telah menggantikan ISO / IEC 9899: 1999 mempertahankan inisialisasi yang ditunjuk di bawah bagian 6.7.9 Initialization. Itu juga tetap paragraph 9tidak berubah.

New ISO / IEC 9899: 2018 standar (umumnya dikenal sebagai C18) yang telah digantikan ISO / IEC 9899: 2011 mempertahankan ditunjuk initializers bawah bagian 6.7.9 Initialization. Itu juga tetap paragraph 9tidak berubah.

PF4Public
sumber
3
Awalnya saya menyarankan ini sebagai edit untuk jawaban yang diterima, tetapi ditolak. Jadi saya mempostingnya sebagai jawaban.
PF4Public
Saya percaya "Anggota yang tidak disebutkan namanya" tidak berarti "bidang yang dihilangkan", melainkan "anggota anonim" seperti serikat pekerja di struct thing { union { char ch; int i; }; };. Standar kemudian mengatakan: "Jika ada lebih sedikit inisialisasi dalam daftar kurung kurawal daripada ada elemen atau anggota agregat, atau lebih sedikit karakter dalam string literal yang digunakan untuk menginisialisasi array dengan ukuran yang diketahui daripada ada elemen dalam array, sisa agregat harus diinisialisasi secara implisit sama dengan objek yang memiliki durasi penyimpanan statis. "
emersion
14

seperti yang dikatakan Ron Nuni:

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

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

Suatu hal yang penting untuk diingat: pada saat Anda menginisialisasi bahkan satu objek / variabel dalam struct, semua variabel lainnya akan diinisialisasi ke nilai default.

Jika Anda tidak menginisialisasi nilai-nilai dalam struct Anda (yaitu jika Anda hanya mendeklarasikan variabel itu), semua variable.membersakan berisi "nilai-nilai sampah", hanya jika deklarasi itu lokal!

Jika deklarasi global atau statis (seperti dalam kasus ini), semua yang tidak diinisialisasi variable.membersakan diinisialisasi secara otomatis ke:

  • 0 untuk bilangan bulat dan titik apung
  • '\0'untuk char(tentu saja ini sama dengan 0, dan charmerupakan tipe integer)
  • NULL untuk pointer.
r_goyal
sumber
Menarik tentang lokal / global, ini juga berlaku untuk nilai waktu terstruktur. Saya punya satu lokal dan dalam satu kasus DST aktif, di lain itu tidak, bukan karena apa pun yang saya lakukan. Saya tidak menggunakan DST (bidang tm_isdst), ini berasal dari strptime, tetapi ketika saya mengonversi ke time_t, ada yang libur satu jam.
Alan Corey
3
void function(void) {
  MY_TYPE a;
  a.flag = true;
  a.value = 15;
  a.stuff = 0.123;
}
robert
sumber
24
begitu saya pernah mendengar 'inisialisasi berbeda dari tugas'. jadi bukankah ini tugas daripada inisialisasi?
Hayri Uğur Koltuk
2

Jika MS belum memperbarui ke C99, MY_TYPE a = {true, 15,0.123};

eddyq
sumber
2

Saya menemukan cara lain untuk menginisialisasi struct.

Struct:

typedef struct test {
    int num;
    char* str;
} test;

Inisialisasi:

test tt = {
    num: 42,
    str: "nice"
};

Sesuai dokumentasi GCC, sintaks ini sudah usang sejak GCC 2.5 .

MichaelWang
sumber
2

Saya tidak suka jawaban ini jadi saya buat sendiri. Saya tidak tahu apakah ini ANSI C atau tidak, itu hanya GCC 4.2.1 dalam mode standarnya. Saya tidak pernah bisa mengingat tanda kurung jadi saya mulai dengan subset dari data saya dan bertempur dengan pesan kesalahan kompiler sampai ditutup. Keterbacaan adalah prioritas utama saya.

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

Data dapat mulai hidup sebagai file dibatasi-tab yang Anda cari-ganti untuk dipijat menjadi sesuatu yang lain. Ya, ini barang Debian. Jadi satu pasangan luar {} (menunjukkan array), lalu pasangan lain untuk setiap struct di dalamnya. Dengan koma di antara. Menempatkan hal-hal di header tidak sepenuhnya diperlukan, tapi saya punya sekitar 50 item di struct saya jadi saya ingin mereka di file yang terpisah, baik untuk menjaga kekacauan dari kode saya dan jadi lebih mudah untuk mengganti.

Alan Corey
sumber
3
Tidak ada pertanyaan tentang inisialisasi susunan struktur! Maksud saya, jawaban Anda mengandung overhead.
Victor Signaevskyi
Saya kira poin saya adalah mendeklarasikan struct dengan nilai yang sudah ada di dalamnya vs. using = untuk mengatur setiap nilai nanti.
Alan Corey
Prinsip itu berlaku apakah itu satu struct atau array dari mereka. Menggunakan = tidak benar-benar inisialisasi, saya tidak akan berpikir.
Alan Corey
0

Struktur dalam C dapat dideklarasikan dan diinisialisasi seperti ini:

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;
}
Ajay Parashar
sumber
0

Saya telah membaca Dokumentasi Microsoft Visual Studio 2015 untuk Inisialisasi Jenis Agregat , semua bentuk inisialisasi dengan {...}dijelaskan di sana, tetapi inisialisasi dengan titik, bernama '' designator '' tidak disebutkan di sana. Itu tidak bekerja juga.

Standar C99 bab 6.7.8 Inisialisasi menjelaskan kemungkinan perancang, tetapi dalam pikiran saya tidak begitu jelas untuk struct yang kompleks. Standar C99 sebagai pdf .

Dalam pikiran saya, mungkin lebih baik

  1. Gunakan = {0};inisialisasi untuk semua data statis. Itu kurang usaha untuk kode mesin.
  2. Gunakan makro untuk menginisialisasi, misalnya

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

Makro dapat diadaptasi, daftar argumennya dapat independen dari konten struct yang berubah. Adalah tepat jika elemen yang lebih sedikit harus diinisialisasi. Ini juga tepat untuk struct bersarang. 3. Bentuk sederhana adalah: Inisialisasi dalam subrutin:

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

Rutin ini terlihat seperti ObjectOriented di C. Use thiz, bukan thisuntuk mengompilasinya dengan C ++ juga!

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

Saya telah mencari cara yang bagus untuk menginisialisasi struct saya, dan saya harus menggunakan di bawah ini (C99). Ini memungkinkan saya menginisialisasi struktur tunggal atau array struktur dengan cara yang sama seperti tipe polos.

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

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

Ini dapat digunakan dalam kode sebagai:

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 */
div0man
sumber
Tapi ini tidak menginisialisasi array struktur ke nilai default. Ini menginisialisasi struktur pertama dalam array ke nilai yang diberikan jsmn_ts_default, tetapi sisa struktur diinisialisasi seolah-olah array memiliki durasi penyimpanan statis, yang berarti bahwa anggota diinisialisasi ke NULLdan 0. Tetapi jika Anda mengubah #define jsmn_ts_default (jsmn_ts){"default", sizeof "default", NULL, 0}Anda akan melihat bahwa hanya elemen array pertama yang diinisialisasi.
ex nihilo
Ya, saya baru saja kembali dari pengujian lagi, ingin mengedit posting :) btw, perilaku yang sama terjadi dengan int a[10] = {1};Hanya elemen 1 yang akan==1
div0man