Việc sử dụng reinterpret_cast trên bộ đệm memcpy có phải không?


8

Cho mã

struct A {};

auto obj = new A;
std::vector<unsigned char> buffer;
buffer.resize(sizeof(obj));
std::memcpy(buffer.data(), &obj, sizeof(obj));  // this copies the pointer, not the object!

// ...

auto ptr = *reinterpret_cast<A**>(buffer.data()); // is this UB?
delete ptr;

là cách sử dụng reinterpret_casttrong trường hợp này UB? Tôi sẽ nói có, bởi vì memcpykhông bắt đầu vòng đời của một cá thể do đó vi phạm quy tắc răng cưa nghiêm ngặt (đó là lý do tại sao std::bit_castđã được thêm vào C ++ 20).

Và nếu tôi thay thế dàn diễn viên bằng một cái khác memcpy(để đọc con trỏ) thì chương trình có được xác định rõ không?


ngôn ngữ hợp pháp sang một bên, nó chỉ đơn giản là sai. Đó là nội dung được chỉ ra bởi buffer.data()cái được cho là chứa một con trỏ A, chứ không phải buffer.data()chính nó là một con trỏ A.
Người kể chuyện - Unslander Monica

3
Có bất kỳ đảm bảo căn chỉnh của các cửa hàng sao lưu được phân bổ bộ nhớ bởi một std::vector? (Tôi cho rằng các bảo đảm của nó là bất cứ điều gì phân bổ của nó đảm bảo.)
Eljay

1
Tôi nghĩ rằng nó phá vỡ răng cưa nghiêm ngặt là tốt.
Một số lập trình viên anh chàng

1
@anastaciu Lần truy cập đầu tiên trên Google - stats.meta.stackexchange.com/q/578335312
Konrad Rudolph

1
OK sau khi đọc lại câu hỏi, đó thực sự là UB vì (1) các yêu cầu căn chỉnh có khả năng bị phá vỡ và (2) ở đây không có A*đối tượng trong bộ đệm. Tiêu chuẩn nói về hàm cấp phát tiêu chuẩn :: Hàm phân bổ: "Trả về: Một con trỏ tới phần tử ban đầu của một mảng lưu trữ có kích thước n * sizeof(T), được căn chỉnh phù hợp cho các đối tượng thuộc loại T ".
n. 'đại từ' m.

Câu trả lời:


9

Có, mã này có hành vi không xác định. Không có đối tượng của loại A*tại vị trí được chỉ bởi buffer.data(). Tất cả những gì bạn đã làm là sao chép biểu diễn đối tượng của một con trỏ như vậy vào vector của bạn [basic.types] / 4 . Vì các con trỏ có thể sao chép một cách tầm thường [basic.types] / 9 , nếu bạn sao chép lại các byte này vào một đối tượng thực tế của loại A*và sau đó deletelà giá trị của điều đó, thì đó sẽ được xác định rõ [basic.types] / 3 . Vậy đây

A* ptr;
std::memcpy(&ptr, buffer.data(), sizeof(ptr));
delete ptr;

sẽ ổn thôi

Lưu ý rằng không phải chính diễn viên gọi ra hành vi không xác định trong ví dụ ban đầu của bạn mà là lần thử tiếp theo của bạn để đọc giá trị của một đối tượng A*không tồn tại trong đó con trỏ thu được thông qua các điểm truyền. Tất cả tồn tại trong đó các điểm con trỏ là một chuỗi các đối tượng của loại unsigned char. Loại A*không phải là loại mà bạn có thể sử dụng để truy cập giá trị được lưu trữ của một đối tượng thuộc loại unsigned char [basic.lval] / 8 .


2
Tôi tin rằng câu trả lời này là đúng nhưng câu cuối cùng hơi ngạc nhiên, vì có rất nhiều mã hiện có thực hiện những gì câu hỏi yêu cầu (hầu như mọi triển khai vùng chứa tùy chỉnh, để bắt đầu), và hiện tại không thực sự tốt cách này trong một số trường hợp.
Konrad Rudolph

1
A* a_ptr; std::memcpy(&a_ptr, buffer.data(), sizeof(a_ptr));để giải nén con trỏ ra khỏi bộ đệm. Thay vì reinterpret_castcâu hỏi của Timo.
Eljay

Ngôn ngữ hợp pháp sang một bên, tại sao điều này thực sự là UB? Bí danh nghiêm ngặt đến với tâm trí, có gì khác cho nó?
divina

@MichaelKenzel có một đối tượng thuộc loại unsigned charđó, có lẽ nhiều hơn một.
n. 'đại từ' m.

1
@MichaelKenzel Tất nhiên là không thể phòng thủ được, vì nó là UB. Nhưng việc khởi tạo vòng đời đối tượng thực sự khó khăn trong (hiện tại) C ++ và hàng tấn mã (nếu không có chất lượng cao!) Không thể thực hiện đúng cách, xem cụ thể p0593r2 §2.3 , nhưng cũng có thể là §2.2. Và vì bạn đề cập đến việc nén: đó thực sự là những gì công ty của tôi làm, và hãy nói rằng cơ sở mã của chúng tôi không chú ý đến những vấn đề này ít nhất (phải thừa nhận, vì phần lớn nó có nguồn gốc từ C thành ngữ).
Konrad Rudolph
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.