Là nhảy qua một khởi tạo biến không được hình thành hoặc nó gây ra hành vi không xác định?


17

Xem xét mã này:

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

GCC và Clang từ chối nó , bởi vì bước nhảy bar:bỏ qua việc khởi tạo biến. MSVC hoàn toàn không phàn nàn (ngoại trừ việc sử dụng xsau khi bar:gây ra cảnh báo).

Chúng ta có thể làm một điều tương tự với một switch:

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

Bây giờ cả ba trình biên dịch phát ra lỗi .

Là những đoạn mã không thành hình? Hay họ gây ra UB?

Tôi đã từng nghĩ rằng cả hai đều không thành hình, nhưng tôi không thể tìm thấy những phần khải thị của tiêu chuẩn. [stmt.goto] không nói bất cứ điều gì về vấn đề này, và cũng như thế [stmt.select] .


1
Vấn đề sẽ tầm thường hơn nếu bạn sử dụng xsau khi nhảy.
Jarod42

1
không phải là tiêu chuẩn, nhưng ở đây người ta có thể tìm thấy một số thông tin về nó: en.cppreference.com/w/cpp/lingu/goto nói riêng: "Nếu việc chuyển quyền kiểm soát đi vào phạm vi của bất kỳ biến tự động nào (ví dụ: bằng cách chuyển tiếp qua một khai báo tuyên bố), chương trình không được định dạng (không thể biên dịch), trừ khi ... "
idclev 463035818

Thêm /permissive-cờ vào MSVC và nó cũng sẽ khiếu nại. Mặc dù tôi không biết liệu hành vi của MSVC không có cờ đó có được xác định rõ hay không (tôi sẽ cho là như vậy, nếu không thì tại sao họ lại cho phép?).
quả óc chó

@walnut "nếu không thì tại sao họ lại cho phép" Có thể vì sự tương thích ngược, hoặc vì họ không quan tâm đến tiêu chuẩn quá nhiều. Tất cả các trình biên dịch chính không tuân thủ tiêu chuẩn trong cài đặt mặc định.
HolyBlackCat

Câu trả lời:


20

Nó không được định hình khi khởi tạo không trống.

[stmt.dcl]

3 Có thể chuyển thành một khối, nhưng không phải theo cách bỏ qua các khai báo với khởi tạo (bao gồm cả các khai báo trong điều kiện và các câu lệnh init). Một chương trình nhảy từ một điểm trong đó một biến có thời lượng lưu trữ tự động không nằm trong phạm vi đến một điểm mà nó nằm trong phạm vi không được định dạng trừ khi biến đó có khởi tạo trống ([basic.life]). Trong trường hợp như vậy, các biến có khởi tạo trống được xây dựng theo thứ tự khai báo của chúng.

Trình khởi tạo làm cho việc khởi tạo không bị bỏ trống. Ngược lại, cái này

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

sẽ được hình thành tốt. Mặc dù những cảnh báo thông thường về việc sử dụng xvới giá trị không xác định sẽ được áp dụng.


Không khai báo biến phải là điều đầu tiên trong một phạm vi?
Cruncher

4
@Cruncher - C89 yêu cầu nó. C ++ không bao giờ làm, và C cũng không hiện đại nữa.
Người kể chuyện - Unslander Monica

3

Từ tuyên bố goto :

Nếu chuyển điều khiển đi vào phạm vi của bất kỳ biến tự động nào (ví dụ: bằng cách chuyển tiếp qua câu lệnh khai báo), chương trình không được định dạng (không thể được biên dịch), trừ khi tất cả các biến có phạm vi được nhập có

  1. các kiểu vô hướng được khai báo mà không cần khởi tạo
  2. các loại lớp với các hàm tạo mặc định tầm thường và các hàm hủy tầm thường được khai báo mà không có bộ khởi tạo
  3. phiên bản đủ điều kiện cv của một trong những điều trên
  4. mảng của một trong những điều trên
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.