__Stdcall là gì?


151

Tôi đang tìm hiểu về lập trình Win32 và WinMainnguyên mẫu trông như sau:

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

Tôi đã bối rối không biết WINAPIđịnh danh này dùng để làm gì và tìm thấy:

#define WINAPI      __stdcall

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


2
@AndrewProck Thay đổi "giả" trong trường hợp này là ổn, nhưng nói chung, bạn có thể sử dụng  s để vượt qua mức tối thiểu 6 ký tự (và phản tác dụng - trong trường hợp).
Adi Inbar

Câu trả lời:


170

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

Có một số công ước gọi khác, __cdecl, __thiscall, __fastcallvà đặt tên là tuyệt vời __declspec(naked). __stdcalllà quy ước gọi tiêu chuẩn cho các cuộc gọi hệ thống Win32.

Wikipedia bao gồm các chi tiết .

Vấn đề chủ yếu là khi bạn gọi một hàm bên ngoài mã của mình (ví dụ: API hệ điều hành) hoặc HĐH đ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 chính xác thì có thể bạn sẽ gặp 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ố sập sao rất nhiều stackoverflow.com/questions/696306/NH
sharptooth

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

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?
kesari

40

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

Bài viết Wikipedia cung cấp một cái nhìn tổng quan về các quy ước gọi khác nhau được tìm thấy ở đó.


18

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


12

Tôi đồng ý rằng tất cả các câu trả lời cho đến nay là chính xác, nhưng đây là lý do. 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 chức năng trong các chức năng 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ề việc sử dụng quy ước gọi nào. Bây giờ, Windows tự cung cấp các hàm (API) và những hàm này đã được biên dịch, vì vậy khi bạn gọi chúng, bạn phải tuân theo chúng. Bất kỳ cuộc gọi nào đến API Windows và cuộc gọi lại từ API Windows, đều phải sử dụng quy ước __stdcall.


3
và không được nhầm lẫn với _st Chuẩn_call ở chỗ nó là tiêu chuẩn-c! người ta có thể nghĩ rằng đó sẽ là điểm của __stdcall nếu người ta không biết rõ hơn
Johannes Schaub - litb

3
Một nitlog nhỏ: có một vài API Windows sử dụng __cdecl thay vì __stdcall - thường là các API có số lượng tham số thay đổi, chẳng hạn như wsprintf ().
Michael Burr

Bạn đúng. Nó được đặt tên trông 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 # không?
Lập trình viên Windows

Tôi chưa thử nghiệm điều này, nhưng pinvoke.net đưa ra chữ ký này: "static extern int wsprintf ([Out] StringBuilder lpOut, chuỗi lpFmt, ...);"
Michael Burr

Trực giác của tôi nói rằng trình biên dịch C # sẽ không biết sử dụng quy ước __cdecl trên đó.
Lập trình viên Windows



4

__stdcall được sử dụng để đặt các đố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 đã lập luận rằng nó trực tiếp đẩy vào ngăn xếp.


1

Tôi chưa bao giờ phải sử dụng điều này trước ngày hôm nay. Đó là 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à windows one (_beginthreadex).

Để bắt đầu chuỗi:

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

Hàm ExecuteCommand PHẢI sử dụng từ khóa __stdcall trong chữ ký phương thức để cho Beginthreadex gọi nó:

unsigned int __stdcall Scene::ExecuteCommand(void* command)
{
    return system(static_cast<char*>(command));
}
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.