Đang sử dụng malloc cho hành vi không xác định int cho đến C ++ 20


96

Tôi đã được thông báo rằng mã sau có hành vi không xác định cho đến C ++ 20:

int *p = (int*)malloc(sizeof(int));
*p = 10;

Có đúng như vậy không?

Đối số là thời gian tồn tại của intđối tượng không được bắt đầu trước khi gán giá trị cho nó ( P0593R6 ). Để khắc phục sự cố, newnên sử dụng vị trí :

int *p = (int*)malloc(sizeof(int));
new (p) int;
*p = 10;

Chúng ta có thực sự phải gọi một hàm tạo mặc định nhỏ để bắt đầu vòng đời của đối tượng không?

Đồng thời, mã không có hành vi không xác định trong C. Tuy nhiên, nếu tôi cấp phát một intmã trong C và sử dụng nó trong mã C ++ thì sao?

// C source code:
int *alloc_int(void)
{
    int *p = (int*)malloc(sizeof(int));
    *p = 10;
    return p;
}

// C++ source code:
extern "C" int *alloc_int(void);

auto p = alloc_int();
*p = 20;

Nó vẫn còn là hành vi không xác định?


8
Đối với int? Không, cho std::string? Đúng.
Eljay

8
@Eljay Đối với int, cũng có. Nó chỉ là nó sẽ không gây ra vấn đề trong thực tế nếu bạn không làm điều đó. Đối với std::string, nó chỉ rõ ràng sẽ gây ra vấn đề.
Barry

Trước C ++ 20, bạn có thể thêm một vị trí mới. Sau đó nó sẽ được hình thành tốt và có thể sẽ không tốn kém gì.
François Andrieux

8
Các quy tắc mới trong C ++ 20 thay đổi điều này là gì?
Kevin

4
Có nên không int *p = (int*)malloc(sizeof(int)); p = new(p) int;? Tôi đã từng nhận ra rằng việc không chỉ định kết quả của vị trí mới cũng có thể gây ra các hiệu ứng chết người (mặc dù nó có thể trông hơi ngớ ngẩn).
Scheff

Câu trả lời:


62

Có đúng không?

Đúng. Về mặt kỹ thuật, không có phần nào của:

int *p = (int*)malloc(sizeof(int));

thực sự tạo ra một đối tượng kiểu int, vì vậy hội nghị plà UB vì không có thực tế intở đó.

Chúng ta có thực sự phải gọi hàm tạo mặc định là tầm thường để bắt đầu thời gian sống của đối tượng không?

Bạn có phải theo mô hình đối tượng C ++ để tránh hành vi không xác định trước C ++ 20 không? Đúng. Liệu có bất kỳ trình biên dịch nào thực sự gây hại khi bạn không làm điều này? Không phải là tôi biết.

[...] Nó vẫn là hành vi chưa xác định?

Đúng. Trước C ++ 20, bạn vẫn chưa thực sự tạo một intđối tượng ở đâu nên đây là UB.


Nhận xét không dành cho thảo luận mở rộng; cuộc trò chuyện này đã được chuyển sang trò chuyện .
Makyen

Tại sao ngôn ngữ trong timsong-cpp.github.io/cppwp/n3337/basic.life#1.1 không đủ để nó không phải là UB? Rốt cuộc, việc lưu trữ có kích thước và sự liên kết thích hợp đã thu được inttrong ví dụ - thời gian tồn tại của intđối tượng bắt đầu từ đó.
avakar

41

Vâng, đó là UB. Danh sách các cách mà một intcó thể tồn tại đã được liệt kê, và không có cách nào áp dụng ở đó, trừ khi bạn hiểu rằng malloc là do mãn kinh.

Nó được nhiều người coi là một lỗ hổng trong tiêu chuẩn, nhưng một trong những tầm quan trọng thấp, bởi vì việc tối ưu hóa được thực hiện bởi các trình biên dịch C ++ xung quanh bit cụ thể của UB đó không gây ra vấn đề với trường hợp sử dụng đó.

Đối với câu hỏi thứ 2, C ++ không bắt buộc C ++ và C tương tác như thế nào. Vì vậy, tất cả các tương tác với C là ... UB, hay còn gọi là hành vi không được xác định theo tiêu chuẩn C ++.


5
Bạn có thể mở rộng danh sách liệt kê các cách để một int tồn tại không? Tôi nhớ đã hỏi một câu hỏi tương tự về tuổi thọ của các loại nguyên thủy và được cho biết rằng nguyên thủy có thể "tồn tại" chỉ đơn giản bằng cách nói rằng nó tồn tại bởi vì thông số kỹ thuật không nói khác. Có vẻ như tôi có thể đã bỏ lỡ một phần hữu ích của thông số kỹ thuật! Tôi muốn biết tôi nên đọc phần nào!
Cort Ammon

7
@CortAmmon Danh sách liệt kê các cách để một đối tượng (thuộc bất kỳ loại nào) tồn tại trong C ++ 20 nằm trong [intro.object] : (1) theo định nghĩa (2) theo biểu thức mới (3) ngầm định theo các quy tắc mới trong P0593 (4) thay đổi thành viên tích cực của một công đoàn (5) tạm thời. (3) là mới trong C ++ 20, (4) là mới trong C ++ 17.
Barry

3
Tương tác C / C ++ có thực sự là UB? Sẽ có ý nghĩa hơn nếu được thực thi xác định, thay vì không xác định, nếu không, sẽ rất lạ nếu thậm chí có cả extern "C"cú pháp.
Ruslan

4
@Ruslan: Việc triển khai được miễn phí để xác định bất kỳ hành vi nào mà ISO C ++ vẫn chưa được xác định. (Ví dụ: gcc -fno-strict-aliasinghoặc MSVC theo mặc định). Nói "thực thi được xác định" sẽ yêu cầu tất cả các triển khai C ++ phải xác định theo cách nào đó mà chúng tương tác với một số triển khai C, vì vậy sẽ rất hợp lý nếu bạn muốn thực hiện bất cứ điều gì như vậy hay không.
Peter Cordes

4
@PeterCordes: Tôi tự hỏi tại sao nhiều người không nhận ra sự khác biệt đó giữa IDB và UB, và chấp nhận một số quan niệm viễn tưởng rằng việc Tiêu chuẩn không bắt buộc tất cả các quy trình triển khai đều có ý nghĩa ngụ ý một nhận định rằng không có triển khai nào được mong đợi để làm như vậy, và các triển khai không làm như vậy không được coi là kém hơn.
supercat
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.