__Stdcall nedir?


151

Win32 programlama hakkında öğreniyorum ve WinMainprototip şöyle görünüyor:

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

Bu WINAPItanımlayıcının ne olduğu konusunda şaşkındım ve buldum:

#define WINAPI      __stdcall

Bu ne yapar? Bir dönüş türü sonra hiç bir şey olması ile kafam karıştı. Ne __stdcalliçin? Dönüş türü ve işlev adı arasında bir şey olduğunda ne anlama gelir?


2
@AndrewProck "Kukla" değişiklik bu durumda iyiydi, ama genel olarak  s en az saçma (ve karşı-üretken - durumda) 6 karakterini aşmak için kullanabilirsiniz.
Adi Inbar

Yanıtlar:


170

__stdcallişlev için kullanılan çağrı kuralıdır. Bu, derleyiciye yığını ayarlamak, bağımsız değişkenleri itmek ve bir dönüş değeri almak için geçerli kuralları söyler.

Diğer çağrı sözleşmelerin vardır, __cdecl, __thiscall, __fastcallve harika adlandırılmış __declspec(naked). __stdcallWin32 sistem çağrıları için standart çağrı kuralıdır.

Wikipedia ayrıntıları kapsar .

Öncelikle kodunuzun dışındaki bir işlevi (ör. Bir OS API'si) çağırırken veya işletim sistemi sizi çağırırken (WinMain'de olduğu gibi) önemlidir. Derleyici doğru arama kuralını bilmiyorsa, yığın doğru yönetilmeyeceği için çok garip çökmeler yaşayabilirsiniz.


8
Çok starnge kilitlenmelerinin bir örneği için bu soruya bakın stackoverflow.com/questions/696306/…
sharptooth

Yanılmıyorsam, bu çağrı kuralları derleyicinin montaj kodunu nasıl ürettiğini kontrol eder. Montaj kodu ile arabirim oluştururken, yığın sorunlarını önlemek için çağrı kuralının dikkate alınması önemlidir. Burada bazı sözleşmeleri belgeleyen güzel bir tablo var: msdn.microsoft.com/en-us/library/984x0h58.aspx
Nicholas Miller

Bunun bazı yasa dışı optimizasyonları devre dışı bırakacağını söyleyebilir miyiz?
kesari

40

C veya C ++ 'ın kendisi bu tanımlayıcıları tanımlamaz. Bunlar derleyici uzantılarıdır ve belirli çağrı kurallarını temsil eder. Bu, argümanların nereye, hangi sırayla, çağrılan işlevin dönüş adresini nerede bulacağını belirler. Örneğin, __fastcall işlevlerin argümanlarının yazmaçlardan geçirildiği anlamına gelir.

Vikipedi Madde orada öğrendim farklı arama kuralları genel bir bakış sağlar.


18

Şimdiye kadar cevaplar ayrıntıları kapsıyor, ancak montajı düşürmek istemiyorsanız, bilmeniz gereken tek şey, hem arayan hem de arayanın aynı arama kuralını kullanması gerektiğidir, aksi takdirde hatalar alırsınız. bulmak zor.


12

Şimdiye kadarki tüm cevapların doğru olduğuna katılıyorum, ama nedeni bu. Microsoft'un C ve C ++ derleyicileri, bir uygulamanın C ve C ++ işlevleri içinde işlev çağrılarının (amaçlanan) hızı için çeşitli çağrı kuralları sağlar. Her durumda, arayan ve arayan kişi hangi çağrı kuralını kullanacağına karar vermelidir. Şimdi, Windows'un kendisi işlevler (API'lar) sağlar ve bunlar zaten derlenmiştir, bu nedenle onları çağırdığınızda bunlara uymanız gerekir. Windows API'lerine yapılan tüm çağrılar ve Windows API'larından yapılan geri çağrılar __stdcall kuralını kullanmalıdır.


3
ve standart-c olması nedeniyle _standard_call ile karıştırılmamalıdır! biri daha iyi bilmiyorsa bu __stdcall noktası olabilir
Johannes Schaub - litb

3
Küçük bir nitpick: __stdcall yerine __cdecl kullanan birkaç Windows API vardır - genellikle wsprintf () gibi değişken sayıda parametre alan URL'ler.
Michael Burr

Haklısın. CRT işlevi gibi görünmek için adlandırılmış, ancak bir API. Herhangi bir şansla nasıl P / C # dan çağırmak biliyor musunuz?
Windows programcısı

Bunu test etmedim, ama pinvoke.net bu imzayı veriyor: "statik extern int wsprintf ([Out] StringBuilder lpOut, lpFmt, ...);"
Michael Burr

Sezgim C # derleyicisinin __cdecl kuralını kullanmayı bilmediğini söylüyor.
Windows programcısı



4

__stdcall işlev bağımsız değişkenlerini yığına koymak için kullanılır. İşlevi tamamladıktan sonra otomatik olarak belleği yeniden konumlandırır. Bu, sabit argümanlar için kullanılır.

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

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

Burada fnname , doğrudan yığının içine ittiği argümanlarına sahiptir.


1

Bunu bugüne kadar hiç kullanmak zorunda kalmadım. Çünkü benim kodda ben çok iş parçacığı ve kullanıyorum çoklu iş parçacığı API kullanıyorum windows bir (_beginthreadex).

Konuyu başlatmak için:

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

ExecuteCommand fonksiyonu GEREKİR beginthreadex aramak için sırayla yöntem imzası __stdcall anahtar sözcüğü kullanın:

unsigned int __stdcall Scene::ExecuteCommand(void* command)
{
    return system(static_cast<char*>(command));
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.