2008-11-18 18 views
119

tôi đang học về lập trình Win32, và nguyên mẫu WinMain trông giống như:__stdcall là gì?

int WINAPI WinMain (HINSTANCE instance, HINSTANCE prev_instance, PSTR cmd_line, int cmd_show) 

tôi đã nhầm lẫn như những gì nhận dạng WINAPI này là cho và nhận thấy:

#define WINAPI  __stdcall 

Điều này làm gì? Tôi đang bối rối bởi điều này có một cái gì đó ở tất cả sau khi một loại trở lại. __stdcall là gì? Điều gì có nghĩa là khi có thứ gì đó giữa kiểu trả về và tên hàm?

+1

@AndrewProck Các "giả" thay đổi là ổn trong trường hợp này, nhưng nói chung bạn có thể sử dụng ' ' s để có được xung quanh ngớ ngẩn (và phản tác dụng - trường hợp tại điểm) tối thiểu 6 ký tự. –

Trả lời

137

__stdcall là quy ước gọi được sử dụng cho hàm. Điều này cho trình biên dịch biết các quy tắc áp dụng cho việc thiết lập ngăn xếp, đẩy các đối số và nhận được một giá trị trả lại.

Có một số quy ước gọi điện khác, __cdecl, __thiscall, __fastcall và được đặt tên tuyệt vời __naked. __stdcall là quy ước gọi điện chuẩn cho các cuộc gọi hệ thống Win32.

Wikipedia bao gồm details.

Điều quan trọng là khi bạn gọi một chức năng bên ngoài mã của bạn (ví dụ: API OS) hoặc hệ điều hành đang gọi bạn (như trường hợp ở đây với WinMain). Nếu trình biên dịch không biết quy ước gọi điện chính xác thì có thể bạn sẽ gặp phải các sự cố rất lạ vì ngăn xếp sẽ không được quản lý chính xác.

+8

Xem câu hỏi này để biết ví dụ về sự cố rất nguy hiểm http://stackoverflow.com/questions/696306/run-time-check-failure-0-loading-queryfullprocessimagename-from-kernel32-dll – sharptooth

+0

Nếu tôi không nhầm, sau đó các quy ước gọi này kiểm soát cách trình biên dịch tạo ra mã lắp ráp. Khi giao tiếp với mã lắp ráp, sau đó nó là quan trọng mà quy ước gọi là lưu ý để ngăn chặn các vấn đề ngăn xếp. Có một bảng tốt ở đây ghi lại một số quy ước: https://msdn.microsoft.com/en-us/library/984x0h58.aspx –

+0

Chúng ta có thể nói rằng điều này sẽ vô hiệu hóa một số tối ưu hóa bất hợp pháp không? – kesari

5

Nó phải liên quan đến cách gọi hàm - về cơ bản thứ tự mà mọi thứ được đặt trên ngăn xếp và ai chịu trách nhiệm dọn dẹp.

Đây là tài liệu hướng dẫn, nhưng nó không có nghĩa là nhiều, trừ khi bạn hiểu được phần đầu tiên:
http://msdn.microsoft.com/en-us/library/zxk0tw93.aspx

31

C hoặc C++ tự không xác định những định danh. Chúng là các phần mở rộng của trình biên dịch và phù hợp với một số quy ước gọi điện. Điều đó quyết định vị trí đặt đối số, theo thứ tự nào, trong đó hàm được gọi sẽ tìm thấy địa chỉ trả về, v.v. Ví dụ, __fastcall có nghĩa là các đối số của các hàm được truyền qua các thanh ghi.

Wikipedia Article cung cấp tổng quan về các quy ước gọi khác nhau được tìm thấy ở đó.

15

Câu trả lời cho đến nay đã đề cập đến chi tiết, nhưng nếu bạn không có ý định bỏ lắp ráp, thì tất cả những gì bạn cần biết là cả người gọi và callee đều phải sử dụng cùng một quy ước gọi điện, nếu không bạn ' sẽ gặp lỗi khó tìm.

9

Tôi đồng ý rằng tất cả các câu trả lời đều đúng, nhưng đây là lý do. Các trình biên dịch C và C++ của Microsoft cung cấp các quy ước gọi khác nhau cho tốc độ (dự định) của các cuộc gọi hàm trong các hàm C và C++ của ứng dụng. Trong mỗi trường hợp, người gọi và callee phải đồng ý về quy ước gọi điện để sử dụng. Bây giờ, bản thân Windows cung cấp các hàm (API) và các hàm đã được biên dịch, vì vậy khi bạn gọi chúng, bạn phải tuân theo chúng. Mọi lệnh gọi tới API Windows và gọi lại từ API Windows, phải sử dụng quy ước __stdcall.

+2

và không được nhầm lẫn với _standard_call ở chỗ nó là chuẩn-c! người ta có thể nghĩ rằng đó sẽ là điểm của __stdcall nếu không hiểu rõ hơn –

+2

Một nitpick nhỏ: có một vài API Windows sử dụng __cdecl thay vì __stdcall - thường là các biến có tham số biến, chẳng hạn như wsprintf(). –

+0

Bạn nói đúng. Nó được đặt tên giống như một hàm CRT nhưng nó là một API. Bạn có biết làm thế nào để P/Gọi nó từ C#? –

4

__stdcall được sử dụng để đặt đối số hàm trong ngăn xếp. Sau khi hoàn thành chức năng, nó sẽ tự động giải phóng bộ nhớ. Điều này được sử dụng cho các đối số cố định.

void __stdcall fnname (int, int*) 
{ 
    ... 
} 

int main() 
{ 
    CreateThread (NULL, 0, fnname, int, int*......) 
} 

Ở đây fnname có args nó trực tiếp đẩy vào ngăn xếp.

1

Tôi chưa bao giờ phải sử dụng này trước khi đến ngày hôm nay. Bởi vì trong mã của tôi, tôi đang sử dụng multi-threadding và API đa luồng mà tôi đang sử dụng là một cửa sổ (_beginthreadex).

Để bắt đầu chủ đề:

_beginthreadex(NULL, 0, ExecuteCommand, currCommand, 0, 0); 

Chức năng ExecuteCommand PHẢI sử dụng từ khóa __stdcall trong chữ ký phương pháp để cho beginthreadex gọi nó là:

unsigned int __stdcall Scene::ExecuteCommand(void* command) 
{ 
    return system(static_cast<char*>(command)); 
}