2014-11-18 16 views
21

Khi tôi biên dịch mẫu mã này sử dụng g ++, tôi nhận được cảnh báo này:Điều này có thực sự phá vỡ các quy tắc bí danh nghiêm ngặt không?

cảnh báo: dereferencing type-punned con trỏ sẽ phá vỡ quy tắc nghiêm ngặt-aliasing [-Wstrict-aliasing]

Mã:

#include <iostream> 

int main() 
{ 
    alignas(int) char data[sizeof(int)]; 
    int *myInt = new (data) int; 
    *myInt = 34; 

    std::cout << *reinterpret_cast<int*>(data); 
} 

Trong trường hợp này, không phải data bí danh một int, và do đó đưa nó trở lại một int sẽ không vi phạm quy tắc bí danh nghiêm ngặt? Hay tôi đang thiếu thứ gì đó ở đây?

Edit: Strange, khi tôi xác định data như thế này:

alignas(int) char* data = new char[sizeof(int)]; 

Cảnh báo trình biên dịch sẽ biến mất. Việc phân bổ stack có tạo sự khác biệt với tính năng bí danh nghiêm ngặt không? Thực tế là nó là một char[] và không phải là char* có nghĩa là nó không thể thực sự bí danh bất kỳ loại nào?

+3

@molbdnilo char * luôn có thể là bí danh –

+0

@ShafikYaghmour Có, tất nhiên. Làm sao tôi có thể quên được? – molbdnilo

+0

Có thể vì 'dữ liệu' đã là bí danh cho' & data [0] '? Ngoài ra 'int const * data;' là một kết hợp gần hơn với 'int data [1];' – rafeek

Trả lời

13

Cảnh báo hoàn toàn hợp lý. Con trỏ bị phân hủy thành data không không trỏ đến một đối tượng thuộc loại int và việc truyền con trỏ không thay đổi điều đó. Xem [basic.life]/7:

Nếu sau thời gian tồn tại của một đối tượng đã kết thúc và trước khi lưu trữ mà đối tượng chiếm được tái sử dụng hoặc phát hành, một đối tượng mới là tạo ra tại vị trí lưu trữ mà các đối tượng ban đầu chiếm đóng, một con trỏ rằng chỉ đến đối tượng gốc, một tài liệu tham khảo mà gọi đến đối tượng gốc, hoặc tên của đối tượng gốc sẽ tự động tham khảo các đối tượng mới và, một khi cuộc đời của đối tượng mới đã bắt đầu , có thể được sử dụng để thao tác obj mới ect, nếu:
(7.1) - [..]
(7.2) - đối tượng mới là loại giống như đối tượng gốc (bỏ qua các cấp cao nhất cv-vòng loại),

Đối tượng mới không phải là mảng char, nhưng là int. P0137, mà chính thức hóa khái niệm trỏ, thêm launder:

[Note: Nếu những điều kiện không được đáp ứng, một con trỏ đến đối tượng mới thể được lấy từ một con trỏ đại diện cho địa chỉ của của nó lưu trữ bằng cách gọi số std::launder (18.6 [support.dynamic]). - lưu ý cuối ]

I.e. đoạn mã của bạn có thể được sửa đổi như vậy:

std::cout << *std::launder(reinterpret_cast<int*>(data)); 

..hoặc chỉ khởi tạo một con trỏ mới từ kết quả của vị trí mới, cũng loại bỏ cảnh báo.

+0

Vì vậy, mặc dù thời gian 'char []' đã kết thúc, con trỏ 'dữ liệu' phân rã thành vẫn là một cách hợp lệ để truy cập vào int được lưu trữ bên trong? –

+0

Tôi hiểu, cảm ơn. Nỗi sợ chính của tôi là trình biên dịch có thể giả định rằng 'dữ liệu' không thể đặt biệt hiệu' int', và do đó có thể tối ưu hóa hiệu ứng '* myInt = 34;' sẽ có trong câu lệnh in. –

+0

Nếu một trình biên dịch đưa ra một cảnh báo không chính xác về một tình huống như thế này, tôi sẽ ít nhất lo lắng về khả năng sai lệch. Chính xác về mặt kỹ thuật sẽ không giúp [nếu mọi thứ bị hỏng] (https://bugzilla.redhat.com/show_bug.cgi?id=638477#c129). – Kevin

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