2013-06-29 35 views
5

Đoạn mã dưới đây tạo ra một tham chiếu lơ lửng như có thể thấy trong cảnh báo được trình biên dịch phát ra và thực tế là hàm hủy đối tượng A trong hàm g() được gọi trước khi hàm trả về. Người ta cũng có thể xác minh rằng trong main() sau khi "sử dụng ngăn xếp", tham chiếu trả lại có rác, ít nhất là trong bản dựng gỡ lỗi. Nhưng tôi không thể tái tạo cùng một hành vi trong bản phát hành bản phát hành. Tại sao vậy? Loại tối ưu hóa trình biên dịch đang làm gì ở đây để tạo ấn tượng rằng tham chiếu r là Ok?Điều gì xảy ra với đoạn mã bên dưới trong bản dựng bản phát hành?

#include <iostream> 

struct A{ 
    A(int i) : i(i) { std::cout << "Ctor\n"; } 
    A(const A& a) { i = a.i; std::cout << "Copy ctor\n"; } 
    ~A() { std::cout << "Dtor\n"; } 
    int i; 
}; 

A& g(int i) { A x(i); return x; } 

int main() 
{ 
    const A& r = g(1); 
    std::cout << "Using the stack\n";  
    std::cout << r.i << '\n'; // r.i has garbage in debug, but not in a release build. 
} 

PS. Tôi sẽ tranh luận chống lại NRVO, vì hàm không trả về một đối tượng A.

Chỉnh sửa: Trả lời Mark Tolonen. Thậm chí nếu tôi bao gồm các biểu thức sau const A& r = g(1); việc phát hành xây dựng không hiển thị rác trong std::cout << r.i << '\n';

std::cout << "Using the stack ...................................................................................................................\n"; 
std::cout << "Using the stack ...................................................................................................................\n"; 
std::cout << "Using the stack ...................................................................................................................\n"; 
std::cout << "Using the stack ...................................................................................................................\n"; 
+2

Hành vi không xác định là không xác định ... (kết quả đầu ra 1 cho tôi trong cả bản phát hành và gỡ lỗi) – jrok

+0

**** VS2010 **** – WaldB

+1

Đã tháo gỡ được kiểm tra. Nó được tối ưu hóa, hoặc trong đăng ký hoặc sử dụng ngay lập tức. – Immueggpain

Trả lời

1

Dưới đây là những gì mà tốc độ được tối ưu hóa (/ O2 biên dịch switch) phát hành xây dựng của Visual Studio 2012 64-bit thực sự làm khi nó chạy đoạn mã này và in ra một:

int main() 
{ 
000000013F7C7E50 sub   rsp,28h 
    const A& r = g(1); 
000000013F7C7E54 lea   rdx,[string "Ctor\n" (013F83DA4Ch)] 
000000013F7C7E5B lea   rcx,[std::cout (013F85FAA0h)] 
000000013F7C7E62 call  std::operator<<<std::char_traits<char> > (013F7C1500h) 
000000013F7C7E67 lea   rdx,[string "Dtor\n" (013F83DA54h)] 
000000013F7C7E6E lea   rcx,[std::cout (013F85FAA0h)] 
000000013F7C7E75 call  std::operator<<<std::char_traits<char> > (013F7C1500h) 
    std::cout << "Using the stack\n";  
000000013F7C7E7A lea   rdx,[string "Using the stack\n" (013F83DA60h)] 
000000013F7C7E81 lea   rcx,[std::cout (013F85FAA0h)] 
000000013F7C7E88 call  std::operator<<<std::char_traits<char> > (013F7C1500h) 
    std::cout << r.i << '\n'; // r.i has garbage in debug, but not in a release build. 
000000013F7C7E8D lea   rcx,[std::cout (013F85FAA0h)] 
000000013F7C7E94 mov   edx,1 
000000013F7C7E99 call  std::basic_ostream<char,std::char_traits<char> >::operator<< (013F7C1384h) 
000000013F7C7E9E mov   dl,0Ah 
000000013F7C7EA0 mov   rcx,rax 
000000013F7C7EA3 call  std::operator<<<std::char_traits<char> > (013F7C10EBh) 

Lưu ý rằng nó doesn' t thậm chí bận tâm để thực sự tạo ra và phá hủy các đối tượng A. Tất cả những gì nó làm là gọi cout bốn lần. Mỗi lần, rdx giữ đối tượng cần in. Ba chữ cái đầu tiên in các chuỗi "Ctor \ n", "Dtor \ n" và "Sử dụng ngăn xếp \ n". Người cuối cùng có vẻ như nó chỉ in số nguyên trong edx là một số 1.

Trình biên dịch có thể thực sự làm bất cứ điều gì cho hành vi không xác định. Nó in một cái gì đó bên cạnh một cái để tối ưu hóa không gian (/ O1 trình biên dịch chuyển đổi), hoặc như OP tìm thấy, không được tối ưu hóa (/ Od).

+0

Brilliant !!!!!!!!!!!!!!! – WaldB

11

Nó chỉ là hành vi không xác định. Bạn trả lại tạm thời bằng cách tham chiếu, mọi thứ đều có thể xảy ra.

A& g(int i) { A x(i); return x; } là bất hợp pháp.

Trình gỡ lỗi có thể sẽ xóa bộ nhớ và gây ra lỗi do bộ nhớ đã bị xóa.

Phiên bản phát hành không làm phiền. Bạn trả tiền cho những gì bạn sử dụng, phải không? Nó chỉ để lại bộ nhớ bị ảnh hưởng, nhưng đánh dấu nó như là reclaimable bởi hệ điều hành. Tất cả găng tay đều được tắt sau đó.

Đó là một điều tốt (được cho là) ​​đi kèm với trình biên dịch VC++. Bạn sẽ thấy tất cả các loại công cụ xảy ra trong quá trình gỡ lỗi để giúp bạn ... cũng ... gỡ lỗi tốt hơn. Các con trỏ chưa được khởi tạo được đặt thành một số giá trị cụ thể để bạn biết nó chưa được khởi tạo, bộ nhớ bị xóa sau delete để bạn biết nó đã bị xóa. Điều này giúp xác định các vấn đề sớm hơn, vì trong bản phát hành, bạn có thể vẫn thấy bộ nhớ nếu nó không bị ghi đè hoặc truy cập con trỏ chưa được khởi tạo và để nó xuất hiện để hoạt động, v.v. và tại thời điểm bạn phát hiện ra sẽ gây ra rất nhiều tác hại và sẽ rất khó chẩn đoán.

+2

@Elazar UB nói * mọi thứ * được phép xảy ra. Tôi giải thích tại sao (cố gắng ít nhất), nhưng có rất ít hoặc không có lý do gì về UB. Chỉ cần tránh nó. –

+0

Bất cứ điều gì có thể xảy ra, nhưng không có gì tốt :-) – user1764961

+0

Giải thích này là những gì tôi đã yêu cầu. +1 – Elazar

Các vấn đề liên quan