Apa itu __stdcall?

151

Saya belajar tentang pemrograman Win32, dan WinMainprototipenya seperti:

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

Saya bingung untuk apa WINAPIpengidentifikasi ini dan menemukan:

#define WINAPI      __stdcall

Apa fungsinya? Saya bingung dengan ini memiliki sesuatu setelah tipe kembali. Untuk apa __stdcall? Apa artinya ketika ada sesuatu antara jenis kembali dan nama fungsi?

Tristan Havelick
sumber
2
@AndrewProck Perubahan "dummy" baik-baik saja dalam kasus ini, tetapi secara umum Anda dapat menggunakan  s untuk berkeliling minimum 6 karakter konyol (dan kontra-produktif).
Adi Inbar

Jawaban:

170

__stdcalladalah konvensi pemanggilan yang digunakan untuk fungsi tersebut. Ini memberi tahu kompilator aturan yang berlaku untuk mengatur tumpukan, mendorong argumen dan mendapatkan nilai balik.

Ada sejumlah konvensi panggilan lain, __cdecl, __thiscall, __fastcalldan mengagumkan bernama __declspec(naked). __stdcalladalah konvensi panggilan standar untuk panggilan sistem Win32.

Wikipedia membahas detailnya .

Ini terutama penting ketika Anda memanggil fungsi di luar kode Anda (misalnya OS OS) atau OS memanggil Anda (seperti halnya di sini dengan WinMain). Jika kompiler tidak mengetahui konvensi pemanggilan yang benar, Anda mungkin akan mendapatkan crash yang sangat aneh karena stack tidak akan dikelola dengan benar.

Rob Walker
sumber
8
Lihat pertanyaan ini untuk contoh crash sangat starnge stackoverflow.com/questions/696306/…
sharptooth
Jika saya tidak salah, maka konvensi pemanggilan ini mengontrol bagaimana kompiler menghasilkan kode rakitan. Saat berinteraksi dengan kode rakitan, penting agar konvensi pemanggilan diperhatikan untuk mencegah masalah tumpukan. Ada tabel yang bagus di sini yang mendokumentasikan beberapa konvensi: msdn.microsoft.com/en-us/library/984x0h58.aspx
Nicholas Miller
Bisakah kita mengatakan bahwa ini akan menonaktifkan optimasi ilegal tertentu?
kesari
40

C atau C ++ sendiri tidak mendefinisikan pengidentifikasi tersebut. Mereka adalah ekstensi kompiler dan berdiri untuk konvensi panggilan tertentu. Itu menentukan di mana harus meletakkan argumen, dalam urutan apa, di mana fungsi yang dipanggil akan menemukan alamat pengirim, dan seterusnya. Sebagai contoh, __fastcall berarti argumen fungsi dilewatkan pada register.

The Wikipedia Pasal memberikan gambaran tentang konvensi panggilan yang berbeda ditemukan di luar sana.

Johannes Schaub - litb
sumber
18

Jawabannya sejauh ini telah mencakup rincian, tetapi jika Anda tidak berniat untuk turun ke pertemuan, maka yang perlu Anda ketahui adalah bahwa penelepon dan callee harus menggunakan konvensi panggilan yang sama, jika tidak Anda akan mendapatkan bug yang sulit ditemukan.

MovEaxEsp
sumber
12

Saya setuju bahwa semua jawaban sejauh ini benar, tetapi inilah alasannya. Kompiler C dan C ++ Microsoft menyediakan berbagai konvensi panggilan untuk kecepatan panggilan fungsi (yang dimaksudkan) dalam fungsi C dan C ++ aplikasi. Dalam setiap kasus, penelepon dan callee harus menyetujui konvensi pemanggilan mana yang akan digunakan. Sekarang, Windows sendiri menyediakan fungsi (API), dan itu sudah dikompilasi, jadi ketika Anda memanggilnya, Anda harus menyesuaikannya. Setiap panggilan ke Windows API, dan callback dari Windows API, harus menggunakan konvensi __stdcall.

Pemrogram Windows
sumber
3
dan jangan bingung dengan _standard_call karena standar-c! orang mungkin berpikir itu akan menjadi titik __stdcall jika seseorang tidak tahu lebih baik
Johannes Schaub - litb
3
Sebuah nitpick kecil: ada beberapa API Windows yang menggunakan __cdecl alih-alih __stdcall - biasanya yang menggunakan parameter jumlah variabel, seperti wsprintf ().
Michael Burr
Kamu benar. Ini dinamai agar terlihat seperti fungsi CRT tetapi ini adalah API. Kebetulan Anda tahu cara P / Panggil dari C #?
Pemrogram Windows
Saya belum menguji ini, tetapi pinvoke.net memberikan tanda tangan ini: "static extern int wsprintf ([Out] StringBuilder lpOut, string lpFmt, ...);"
Michael Burr
Intuisi saya mengatakan bahwa kompiler C # tidak akan tahu untuk menggunakan konvensi __cdecl yang satu itu.
Pemrogram Windows
5

Ini ada hubungannya dengan bagaimana fungsi dipanggil - pada dasarnya urutan di mana hal-hal diletakkan di tumpukan dan siapa yang bertanggung jawab untuk pembersihan.

Berikut dokumentasinya, tetapi itu tidak berarti banyak kecuali Anda memahami bagian pertama:
http://msdn.microsoft.com/en-us/library/zxk0tw93.aspx

Joel Coehoorn
sumber
4

__stdcall digunakan untuk meletakkan argumen fungsi di stack. Setelah fungsi selesai, secara otomatis deallocates memori. Ini digunakan untuk argumen tetap.

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

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

Di sini fnname memiliki args yang langsung mendorong ke stack.

philippe lhardy
sumber
1

Saya tidak pernah menggunakan ini sebelumnya sampai hari ini. Karena dalam kode saya, saya menggunakan multi-threadding dan API multi-threading yang saya gunakan adalah windows one (_beginthreadex).

Untuk memulai utas:

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

Fungsi ExecuteCommand HARUS menggunakan kata kunci __stdcall dalam tanda tangan metode agar beginthreadex menyebutnya:

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