Apakah melompati inisialisasi variabel tidak terbentuk atau apakah itu menyebabkan perilaku yang tidak terdefinisi?

17

Pertimbangkan kode ini:

void foo()
{
    goto bar;
    int x = 0;
    bar: ;
}

GCC dan Dentang menolaknya , karena lompatan untuk bar:mem - bypass inisialisasi variabel. MSVC tidak mengeluh sama sekali (kecuali menggunakan xsetelah bar:menyebabkan peringatan).

Kita dapat melakukan hal serupa dengan switch:

void foo()
{
    switch (0)
    {
        int x = 0;
        case 0: ;
    }
}

Sekarang ketiga kompiler memancarkan kesalahan .

Apakah potongan-potongan itu buruk bentuknya? Atau apakah mereka menyebabkan UB?

Dulu saya berpikir bahwa keduanya buruk bentuknya, tetapi saya tidak dapat menemukan bagian-bagian standar yang jelas. [stmt.goto] tidak mengatakan apa-apa tentang ini, dan begitu pula [stmt.select] .

HolyBlackCat
sumber
1
Masalah akan lebih sepele jika Anda gunakan xsetelah lompat.
Jarod42
1
bukan standar, tetapi di sini orang dapat menemukan beberapa informasi tentangnya: en.cppreference.com/w/cpp/language/goto khususnya: "Jika transfer kontrol memasuki ruang lingkup variabel otomatis apa pun (misalnya dengan melompat ke depan melalui deklarasi pernyataan), program ini salah bentuk (tidak dapat dikompilasi), kecuali ... "
idclev 463035818
Tambahkan /permissive-bendera ke MSVC dan itu akan mengeluh juga. Saya tidak tahu apakah perilaku MSVC tanpa bendera itu didefinisikan dengan baik (saya akan berasumsi demikian, kalau tidak mengapa mereka mengizinkannya?).
walnut
@walnut "jika tidak, mengapa mereka mengizinkannya" Mungkin untuk kompatibilitas ke belakang, atau karena mereka tidak terlalu peduli dengan standar. Semua kompiler utama tidak sesuai dengan standar di bawah pengaturan default.
HolyBlackCat

Jawaban:

20

Ini terbentuk buruk ketika inisialisasi tidak kosong.

[stmt.dcl]

3 Dimungkinkan untuk mentransfer ke dalam blok, tetapi tidak dengan cara yang melewati deklarasi dengan inisialisasi (termasuk yang dalam kondisi dan init-pernyataan). Sebuah program yang melompat dari titik di mana variabel dengan durasi penyimpanan otomatis tidak dalam ruang lingkup ke titik di mana ruang lingkupnya tidak terbentuk kecuali variabel tersebut memiliki inisialisasi kosong ([basic.life]). Dalam kasus seperti itu, variabel dengan inisialisasi kosong dikonstruksi dalam urutan deklarasi mereka.

Penginisialisasi membuat inisialisasi tidak kosong. Sebaliknya, ini

void foo()
{
    goto bar;
    int x; // no initializer
    bar: ;
}

akan terbentuk dengan baik. Padahal peringatan biasa tentang penggunaan xdengan nilai tak tentu akan berlaku.

StoryTeller - Unslander Monica
sumber
bukankah deklarasi variabel harus menjadi hal pertama dalam cakupan?
Cruncher
4
@Cruncher - C89 membutuhkannya. C ++ tidak pernah melakukannya, dan C modern tidak lagi.
StoryTeller - Unslander Monica
3

Dari pernyataan goto :

Jika transfer kontrol memasuki ruang lingkup variabel otomatis apa pun (mis. Dengan melompati pernyataan pernyataan), program tidak terbentuk (tidak dapat dikompilasi), kecuali semua variabel yang ruang lingkupnya dimasukkan memiliki

  1. jenis skalar dideklarasikan tanpa inisialisasi
  2. tipe kelas dengan konstruktor default sepele dan destruktor sepele dinyatakan tanpa inisialisasi
  3. versi yang memenuhi syarat cv dari salah satu di atas
  4. array salah satu di atas
Pencari kebenaran
sumber