Apa yang dimaksud dengan {0} saat menginisialisasi objek?

252

Kapan {0}digunakan untuk menginisialisasi objek, apa artinya? Saya tidak dapat menemukan referensi ke {0}mana pun, dan karena kurung kurawal pencarian Google tidak membantu.

Kode contoh:

SHELLEXECUTEINFO sexi = {0}; // what does this do?
sexi.cbSize = sizeof(SHELLEXECUTEINFO);
sexi.hwnd = NULL;
sexi.fMask = SEE_MASK_NOCLOSEPROCESS;
sexi.lpFile = lpFile.c_str();
sexi.lpParameters = args;
sexi.nShow = nShow;

if(ShellExecuteEx(&sexi))
{
    DWORD wait = WaitForSingleObject(sexi.hProcess, INFINITE);
    if(wait == WAIT_OBJECT_0)
        GetExitCodeProcess(sexi.hProcess, &returnCode);
}

Tanpa itu, kode di atas akan macet saat runtime.

Mahmoud Al-Qudsi
sumber

Jawaban:

302

Apa yang terjadi di sini disebut inisialisasi agregat . Berikut adalah definisi (disingkat) agregat dari bagian 8.5.1 dari spesifikasi ISO:

Agregat adalah array atau kelas tanpa konstruktor yang dideklarasikan pengguna, tidak ada anggota data non-statis pribadi atau yang dilindungi, tanpa kelas dasar, dan tanpa fungsi virtual.

Sekarang, menggunakan {0}untuk menginisialisasi agregat seperti ini pada dasarnya adalah trik untuk 0semuanya. Ini karena ketika menggunakan inisialisasi agregat Anda tidak perlu menentukan semua anggota dan spek mensyaratkan bahwa semua anggota yang tidak ditentukan menjadi default diinisialisasi, yang berarti disetel ke 0tipe sederhana.

Berikut adalah kutipan yang relevan dari spec:

Jika ada lebih sedikit inisialisasi dalam daftar daripada ada anggota dalam agregat, maka setiap anggota yang tidak diinisialisasi secara eksplisit harus diinisialisasi-standar. Contoh:

struct S { int a; char* b; int c; };
S ss = { 1, "asdf" };

menginisialisasi ss.adengan 1, ss.bdengan "asdf", dan ss.cdengan nilai ekspresi bentuk int(), yaitu 0,.

Anda dapat menemukan spesifikasi lengkap tentang topik ini di sini

Don Neufeld
sumber
15
Respon luar biasa. Hanya ingin menambahkan bahwa menginisialisasi agregat dengan {0} sama dengan menginisialisasinya hanya {}. Mungkin yang pertama membuatnya lebih jelas bahwa tipe bawaan mendapatkan nol.
James Hopkin
7
Beberapa kompiler tersedak {}, itulah sebabnya {0} digunakan
Branan
10
Tidak cukup .. Dalam C ++, jika anggota pertama tidak dapat dikonstruksi nol maka {0} tidak akan berfungsi. Contoh: struct A {B b; int i; char c; }; struct B {B (); B (string); }; A a = {}; // pernyataan ini tidak dapat ditulis ulang sebagai 'A a = {0}'.
Aaron
25
@Branan, itu karena di C "{}" tidak valid. Dalam C ++ itu. @ don.neufeld, ini telah berubah dengan C ++ 03 (default-diinisialisasi -> nilai-diinisialisasi). Kutipan mengutip Standar C ++ 98, ingatlah.
Johannes Schaub - litb
3
Jadi, apakah ini menggantikan kebutuhan untuk menggunakan ZeroMemory () (dalam VC ++)?
Ray
89

Satu hal yang perlu diperhatikan adalah bahwa teknik ini tidak akan mengatur padding byte ke nol. Sebagai contoh:

struct foo
{
    char c;
    int  i;
};

foo a = {0};

Tidak sama dengan:

foo a;
memset(&a,0,sizeof(a));

Dalam kasus pertama, pad byte antara c dan i tidak diinisialisasi. Kenapa kamu peduli? Nah, jika Anda menyimpan data ini ke disk atau mengirimnya melalui jaringan atau apa pun, Anda bisa memiliki masalah keamanan.

Harold Ekstrom
sumber
13
Tentu saja, ini hanya masalah keamanan jika Anda 'menulis (f, & a, sizeof (a))', yang dapat menghasilkan pengkodean file yang berbeda pada prosesor / kompiler yang berbeda. Output yang diformat dengan baik akan aman tanpa memset.
Aaron
3
Juga, jika Anda mengirim barang melalui jaringan, Anda akan selalu mengatur perataan yang akan dikemas. Dengan begitu Anda mendapatkan byte bantalan tambahan sesedikit mungkin.
Mark Kegel
18
Perlu dicatat bahwa walaupun spek tidak memerlukan padding untuk diinisialisasi, kompiler waras mana pun akan, karena hanya membutuhkan waktu untuk menginisialisasi "sekitar" itu.
Tomas
4
Saya ingin mempermasalahkan penggunaan kata "tidak". Padding byte didefinisikan sebagai implementasi. Kompiler bebas untuk mengubah foo a = {0} menjadi memset (& a, 0, sizeof (a)) pada waktu luangnya sendiri. Tidak diperlukan untuk "melewati" byte padding dan hanya mengatur foo.c dan foo.i. +1 untuk bug keamanan (potensial) yang ada
SecurityMatt
3
@Leushenko titik 19 mengatakan "semua sub-proyek yang tidak diinisialisasi secara eksplisit", jadi saya akan condong ke titik 21 (yang Anda kutip) ceroboh. Akan sangat aneh jika spec memungkinkan padding tidak diinisialisasi hingga saat initializer terakhir, setelah itu padding harus di-zeroed, terutama mengingat bahwa initializer mungkin tampak tidak sesuai ketika initializer yang ditunjuk digunakan.
MM
20

Perhatikan bahwa penginisialisasi agregat kosong juga berfungsi:

SHELLEXECUTEINFO sexi = {};
char mytext[100] = {};
dalle
sumber
11

Sebagai jawaban mengapa ShellExecuteEx()crash: SHELLEXECUTEINFOstruct "sexi" Anda memiliki banyak anggota dan Anda hanya menginisialisasi beberapa dari mereka.

Misalnya, anggota sexi.lpDirectorydapat menunjuk ke mana saja, tetapi ShellExecuteEx()masih akan mencoba menggunakannya, maka Anda akan mendapatkan pelanggaran akses memori.

Ketika Anda memasukkan baris:

SHELLEXECUTEINFO sexi = {0};

sebelum sisa pengaturan struktur Anda, Anda memberi tahu kompiler untuk membatalkan semua anggota struktur sebelum Anda menginisialisasi yang spesifik. Anda ShellExecuteEx()tahu bahwa jika sexi.lpDirectorynol, ia harus mengabaikannya.

snowcrash09
sumber
7

Saya juga menggunakannya untuk menginisialisasi string misalnya.

char mytext[100] = {0};
Adam Pierce
sumber
5
Padahal, tentu saja, jika mytext sedang digunakan string, char mytext [100]; mytext [0] = '\ 0'; akan memiliki efek yang sama dengan memberikan string kosong, tetapi hanya menyebabkan implementasi nol byte pertama.
Chris Young
@ Chris: Saya sering berharap ada sintaksis untuk inisialisasi objek parsial. Mampu "mendeklarasikan dan menginisialisasi" x, kemudian melakukan hal yang sama dengan y, lalu z, jauh lebih baik daripada harus mendeklarasikan x, y, dan z, dan kemudian menginisialisasi x, y, dan z, tetapi menginisialisasi 100 byte ketika hanya yang sebenarnya membutuhkan inisialisasi tampaknya agak boros.
supercat
7

{0}adalah penginisialisasi yang valid untuk semua jenis (objek lengkap), dalam C dan C ++. Ini adalah idiom umum yang digunakan untuk menginisialisasi objek ke nol (baca terus untuk melihat apa artinya).

Untuk tipe skalar (tipe aritmatika dan pointer), kurung tidak diperlukan, tetapi mereka diizinkan secara eksplisit. Mengutip konsep N1570 dari standar ISO C, bagian 6.7.9:

Inisialisasi untuk skalar harus berupa ekspresi tunggal, secara opsional tertutup dalam kurung kurawal.

Ini menginisialisasi objek ke nol ( 0untuk bilangan bulat, 0.0untuk floating-point, pointer nol untuk pointer).

Untuk jenis non-skalar (struktur, array, serikat), {0}menentukan bahwa elemen pertama objek diinisialisasi ke nol. Untuk struktur yang mengandung struktur, susunan struktur, dan sebagainya, ini diterapkan secara rekursif, sehingga elemen skalar pertama diatur ke nol, sesuai jenisnya. Seperti pada penginisialisasi apa pun, elemen apa pun yang tidak ditentukan ditetapkan ke nol.

Kawat gigi menengah ( {, }) dapat dihilangkan; misalnya keduanya valid dan setara:

int arr[2][2] = { { 1, 2 }, {3, 4} };

int arr[2][2] = { 1, 2, 3, 4 };

itulah sebabnya Anda tidak harus menulis, misalnya, { { 0 } }untuk jenis yang elemen pertamanya adalah non-skalar.

Jadi ini:

some_type obj = { 0 };

adalah cara singkat menginisialisasi objke nol, yang berarti bahwa setiap sub-objek skalar objdiatur ke 0jika itu adalah bilangan bulat, 0.0jika itu floating-point, atau null pointer jika itu adalah pointer.

Aturannya mirip untuk C ++.

Dalam kasus khusus Anda, karena Anda menetapkan nilai ke sexi.cbSizedan sebagainya, jelas bahwa itu SHELLEXECUTEINFOadalah tipe struct atau kelas (atau mungkin gabungan, tetapi mungkin tidak), jadi tidak semua ini berlaku, tetapi seperti yang saya katakan { 0 }adalah umum idiom yang dapat digunakan dalam situasi yang lebih umum.

Ini tidak (harus) setara dengan menggunakan memsetuntuk mengatur representasi objek ke semua-bit-nol. Baik floating-point 0.0maupun null pointer harus direpresentasikan sebagai all-bits-zero, dan { 0 }penginisialisasi tidak harus menetapkan padding byte ke nilai tertentu. Pada sebagian besar sistem, kemungkinan besar memiliki efek yang sama.

Keith Thompson
sumber
1
Dalam C ++, {0}bukan penginisialisasi yang valid untuk objek tanpa konstruktor yang menerima 0; juga untuk agregat yang elemen pertamanya adalah seperti itu (atau agregat tanpa elemen)
MM
3

Sudah beberapa saat sejak saya bekerja di c / c ++ tetapi IIRC, cara pintas yang sama dapat digunakan untuk array juga.

μBio
sumber
2

Saya selalu bertanya-tanya, mengapa Anda harus menggunakan sesuatu seperti

struct foo bar = { 0 };

Berikut ini adalah contoh kasus untuk dijelaskan:

check.c

struct f {
    int x;
    char a;
} my_zero_struct;

int main(void)
{
    return my_zero_struct.x;
}

Saya mengkompilasi dengan gcc -O2 -o check check.cdan kemudian output tabel simbol dengan readelf -s check | sort -k 2(ini dengan gcc 4.6.3 di ubuntu 12.04.2 pada sistem x64). Kutipan:

59: 0000000000601018     0 NOTYPE  GLOBAL DEFAULT  ABS __bss_start
48: 0000000000601018     0 NOTYPE  GLOBAL DEFAULT  ABS _edata
25: 0000000000601018     0 SECTION LOCAL  DEFAULT   25 
33: 0000000000601018     1 OBJECT  LOCAL  DEFAULT   25 completed.6531
34: 0000000000601020     8 OBJECT  LOCAL  DEFAULT   25 dtor_idx.6533
62: 0000000000601028     8 OBJECT  GLOBAL DEFAULT   25 my_zero_struct
57: 0000000000601030     0 NOTYPE  GLOBAL DEFAULT  ABS _end

Bagian penting di sini adalah, yaitu my_zero_structsetelah __bss_start. Bagian ".bss" dalam program C adalah bagian dari memori yang disetel ke nol sebelum main disebut see wikipedia on .bss .

Jika Anda mengubah kode di atas menjadi:

} my_zero_struct = { 0 };

Maka executable "check" yang dihasilkan terlihat persis sama setidaknya dengan kompiler gcc 4.6.3 di ubuntu 12.04.2; yang my_zero_structmasih dalam .bssbagian dan dengan demikian maka akan secara otomatis diinisialisasi ke nol, sebelum maindisebut.

Petunjuk dalam komentar, bahwa memsetmungkin menginisialisasi struktur "penuh" juga bukan perbaikan, karena .bssbagian ini dibersihkan sepenuhnya yang juga berarti struktur "penuh" diatur ke nol.

Ini mungkin bahwa standar bahasa C tidak menyebutkan semua ini, tapi di dunia C compiler nyata Aku belum pernah melihat perilaku yang berbeda.

Ingo Blackman
sumber
Variabel global dan statis selalu diinisialisasi secara default ke 0 atau default ctor. Tetapi jika Anda mendeklarasikan instance dari f secara lokal Anda mungkin mendapatkan hasil yang berbeda.
Logman
0

Ini adalah gula sintaksis untuk menginisialisasi seluruh struktur Anda ke nilai kosong / nol / nol.

Versi panjang

SHELLEXECUTEINFO sexi;
sexi.cbSize = 0;
sexi.fMask = 0;
sexi.hwnd = NULL;
sexi.lpVerb = NULL;
sexi.lpFile = NULL;
sexi.lpParameters = NULL;
sexi.lpDirectory = NULL;
sexi.nShow = nShow;
sexi.hInstApp = 0;
sexi.lpIDList = NULL;
sexi.lpClass = NULL;
sexi.hkeyClass = 0;
sexi.dwHotKey = 0;
sexi.hMonitor = 0;
sexi.hProcess = 0;

Versi pendek

SHELLEXECUTEINFO sexi = {0};

Bukankah itu jauh lebih mudah?

Ini juga bagus karena:

  • Anda tidak harus memburu setiap anggota dan menginisialisasi itu
  • Anda tidak perlu khawatir bahwa Anda mungkin tidak menginisialisasi anggota baru ketika mereka ditambahkan nanti
  • Anda tidak perlu meneleponZeroMemory
Ian Boyd
sumber
-5

{0} adalah array anonim yang mengandung elemennya sebagai 0.

Ini digunakan untuk menginisialisasi satu atau semua elemen array dengan 0.

mis. int arr [8] = {0};

Dalam hal ini semua elemen arr akan diinisialisasi sebagai 0.

nitin prajapati
sumber
4
{0}bukan array anonim. Itu bahkan bukan ekspresi. Ini adalah inisialisasi.
Keith Thompson