Chuyển đổi thành void ** trên các trình biên dịch khác nhau


9

Tôi đã chạy mã sau thông qua các trình biên dịch khác nhau:

int main()
{
    float **a;
    void **b;
    b = a;
}

Từ những gì tôi đã có thể thu thập, void **không một con trỏ chung có nghĩa là bất kỳ chuyển đổi từ một con trỏ không nên biên dịch hoặc ít nhất là ném một cảnh báo. Tuy nhiên, đây là kết quả của tôi (tất cả được thực hiện trên Windows):

  • gcc - Ném một cảnh báo, như mong đợi.
  • g ++ - Ném lỗi, như mong đợi (điều này là do việc gõ C ++ ít được phép hơn, phải không?)
  • MSVC (cl.exe) - Ném không có cảnh báo nào, ngay cả với / Tường được chỉ định.

Câu hỏi của tôi là: Tôi có thiếu điều gì về toàn bộ vấn đề không và có lý do cụ thể nào khiến MSVC không đưa ra cảnh báo không? MSVC không đưa ra cảnh báo khi chuyển đổi từ void ** sang float **.

Một điều lưu ý khác: Nếu tôi thay thế a = bbằng chuyển đổi rõ ràng a = (void **)b, không có trình biên dịch nào đưa ra cảnh báo. Tôi nghĩ rằng đây là một diễn viên không hợp lệ, vậy tại sao không có bất kỳ cảnh báo nào?

Lý do tôi hỏi câu hỏi này là vì tôi đã bắt đầu học CUDA và trong Hướng dẫn lập trình chính thức ( https://docs.nvidia.com/cuda/cuda-c-programming-guide/index.html#device-memory ) mã sau đây có thể được tìm thấy:

// Allocate vectors in device memory
float* d_A;
cudaMalloc(&d_A, size);

mà nên thực hiện một chuyển đổi ngầm định void **cho &d_A, như là đối số đầu tiên cudaMalloccủa kiểu void **. Mã tương tự có thể được tìm thấy trên tất cả các tài liệu. Đây chỉ là công việc cẩu thả trên kết thúc của NVIDIA hay tôi, một lần nữa, thiếu một cái gì đó? Kể từ khi nvccsử dụng MSVC, mã sẽ biên dịch mà không có cảnh báo.


3
Lỗi từ 3 bài đăng trực tiếp: godbolt.org/z/GQWMNo
Richard Critten

3
Các lỗi mã cho tôi với MSVC. Phiên bản bạn đang sử dụng? Nhưng có, void**không phải là một con trỏ chung. Chỉ void*
NathanOliver

Cảm ơn vì sớm phản hồi! 19.24.28315 cho x64 rõ ràng? Tôi chưa thực sự sử dụng MSVC trước đây.
CaptainProton42

2
(void**)là một phong cách c rõ ràng đúc. Nó bảo trình biên dịch không nhìn kỹ vào những gì bạn đang làm và tin tưởng bạn. Đó là ghi đè rõ ràng của hệ thống an toàn loại và trình biên dịch được yêu cầu chấp nhận về cơ bản bất kỳ loại chuyển đổi nào. Nên tránh các kiểu phôi C, chúng quá mạnh. Sử dụng các diễn viên C ++ như thế static_castsẽ phàn nàn nếu bạn đang cố gắng làm điều gì đó không có ý nghĩa.
François Andrieux

@RichardCritten ngôn ngữ sai - không có lỗi godbolt.org/z/RmFpgN C ++ cuda yêu cầu diễn xuất rõ ràng như thường lệ.
P__J__

Câu trả lời:


4

Tôi có thiếu điều gì về toàn bộ vấn đề không và có lý do cụ thể nào khiến MSVC không đưa ra cảnh báo không? MSVC không đưa ra cảnh báo khi chuyển đổi từ void ** sang float **

Việc gán mà không có cast là vi phạm ràng buộc, vì vậy trình biên dịch tuân thủ tiêu chuẩn sẽ in cảnh báo hoặc lỗi. Tuy nhiên, MSVC không tuân thủ đầy đủ việc thực hiện C.

Một điều lưu ý khác: Nếu tôi thay thế a = b bằng chuyển đổi rõ ràng a = (void **) b, không có trình biên dịch nào đưa ra cảnh báo. Tôi nghĩ rằng đây là một diễn viên không hợp lệ, vậy tại sao không có bất kỳ cảnh báo nào?

Chuyển đổi con trỏ thông qua một diễn viên được cho phép trong một số tình huống. Tiêu chuẩn C cho biết như sau trong phần 6.3.2.3p7:

Một con trỏ tới một loại đối tượng có thể được chuyển đổi thành một con trỏ thành một loại đối tượng khác. Nếu con trỏ kết quả không được căn chỉnh chính xác cho loại được tham chiếu, hành vi không được xác định. Mặt khác, khi được chuyển đổi trở lại một lần nữa, kết quả sẽ so sánh bằng với con trỏ ban đầu. Khi một con trỏ tới một đối tượng được chuyển đổi thành một con trỏ thành một kiểu ký tự, kết quả sẽ trỏ đến byte có địa chỉ thấp nhất của đối tượng. Gia tăng liên tiếp của kết quả, cho đến kích thước của đối tượng, mang lại con trỏ tới các byte còn lại của đối tượng.

Vì vậy, bạn có thể chuyển đổi giữa các loại con trỏ với điều kiện không có vấn đề căn chỉnh và bạn chỉ chuyển đổi trở lại (trừ khi mục tiêu là a char *).

float* d_A;
cudaMalloc(&d_A, size);

...

Đây chỉ là công việc cẩu thả trên kết thúc của NVIDIA hay tôi, một lần nữa, thiếu một cái gì đó?

Có lẽ, chức năng này là hủy bỏ con trỏ đã cho và ghi địa chỉ của một số bộ nhớ được phân bổ. Điều đó có nghĩa là nó đang cố viết cho a float *như thể nó là một void *. Điều này không giống như chuyển đổi điển hình thành / từ a void *. Nói một cách chính xác thì điều này trông giống như hành vi không xác định, mặc dù nó "hoạt động" vì bộ xử lý x86 hiện đại (khi không ở chế độ thực) sử dụng cùng một đại diện cho tất cả các loại con trỏ.


@dbush Rất nhiều thông tin, cảm ơn bạn! Tôi hiểu tại sao nó hoạt động nếu nó hoạt động. Tuy nhiên, hầu hết các trình biên dịch sẽ đưa ra cảnh báo hoặc thậm chí lỗi vì &d_Akhông có loại được yêu cầu?
CaptainProton42

3
Hãy cẩn thận với cách bạn diễn giải điều này, có thể có và có các thủ thuật mẫu giữa nếu bạn biên dịch CUDA với trình biên dịch C ++
Talonmies
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.