stdcall और cdecl


92

वहाँ (दूसरों के बीच) बुला सम्मेलनों के दो प्रकार हैं - stdcall और cdecl । मेरे पास उन पर कुछ सवाल हैं:

  1. जब cdecl फ़ंक्शन कहा जाता है, तो एक कॉलर को कैसे पता चलेगा कि उसे स्टैक को मुक्त करना चाहिए? कॉल साइट पर, क्या कॉलर को पता है कि क्या कहा जा रहा है कि फ़ंक्शन एक सीडीसीएल या एक सीडीसीएल फ़ंक्शन है? यह कैसे काम करता है ? कॉलर को कैसे पता चलेगा कि उसे स्टैक को मुक्त करना चाहिए या नहीं? या यह लिंकर्स जिम्मेदारी है?
  2. यदि एक फ़ंक्शन जिसे stdcall के रूप में घोषित किया जाता है, तो एक फ़ंक्शन (जिसमें कॉलिंग कन्वेंशन cdecl के रूप में होता है), या दूसरा तरीका गोल है, क्या यह अनुचित होगा?
  3. सामान्य तौर पर, हम कह सकते हैं कि कौन सी कॉल तेज होगी - सीडीसीएल या स्टडकल?

9
कई प्रकार के कॉलिंग कन्वेंशन हैं, जिनमें से सिर्फ दो हैं। en.wikipedia.org/wiki/X86_calling_conventions
मूचिंग डक

1
कृपया, एक सही उत्तर अंकित करें
ceztko

जवाबों:


79

रेमंड चेन क्या __stdcallऔर क्या __cdeclकरता है का एक अच्छा अवलोकन देता है

(1) किसी फ़ंक्शन को कॉल करने के बाद कॉल करने वाले को स्टैक को साफ करने के लिए "जानता" है क्योंकि कंपाइलर उस फ़ंक्शन के कॉलिंग कन्वेंशन को जानता है और आवश्यक कोड उत्पन्न करता है।

void __stdcall StdcallFunc() {}

void __cdecl CdeclFunc()
{
    // The compiler knows that StdcallFunc() uses the __stdcall
    // convention at this point, so it generates the proper binary
    // for stack cleanup.
    StdcallFunc();
}

इस तरह से कॉलिंग कन्वेंशन को बेमेल करना संभव है :

LRESULT MyWndProc(HWND hwnd, UINT msg,
    WPARAM wParam, LPARAM lParam);
// ...
// Compiler usually complains but there's this cast here...
windowClass.lpfnWndProc = reinterpret_cast<WNDPROC>(&MyWndProc);

इतने सारे कोड नमूने यह गलत है यह भी अजीब नहीं है। यह इस तरह होना चाहिए:

// CALLBACK is #define'd as __stdcall
LRESULT CALLBACK MyWndProc(HWND hwnd, UINT msg
    WPARAM wParam, LPARAM lParam);
// ...
windowClass.lpfnWndProc = &MyWndProc;

हालाँकि, प्रोग्रामर ने माना कि संकलक त्रुटियों को अनदेखा नहीं करता है, संकलक कोड को ठीक से साफ करने के लिए आवश्यक कोड उत्पन्न करेगा क्योंकि इसमें शामिल कार्यों के कॉलिंग कन्वेंशनों को पता चल जाएगा।

(२) दोनों तरह से काम करना चाहिए। वास्तव में, इस कोड में कम से कम काफी अक्सर होता है कि Windows API साथ सूचना का आदान, क्योंकि __cdeclC और C ++ प्रोग्राम के लिए डिफ़ॉल्ट विज़ुअल सी ++ संकलक के अनुसार है और WinAPI कार्यों का उपयोग __stdcallसम्मेलन

(३) दोनों के बीच कोई वास्तविक प्रदर्शन अंतर नहीं होना चाहिए।


एक अच्छे उदाहरण के लिए +1 और कन्वेंशन इतिहास के बारे में रेमंड चेन की पोस्ट। रुचि रखने वाले किसी भी व्यक्ति के लिए, उसके अन्य भाग एक अच्छी तरह से पढ़े जाते हैं।
ओरेगॉनहोस्ट

रेमंड चेन के लिए +1। Btw (OT): ऐसा क्यों है कि मैं ब्लॉग खोज बॉक्स का उपयोग कर अन्य भागों को नहीं ढूंढ सकता हूं? Google उन्हें ढूंढता है, लेकिन MSDN ब्लॉग्स नहीं?
नॉर्डिक मेनफ्रेम

44

CDECL तर्कों में रिवर्सल ऑर्डर में स्टैक पर धकेल दिया जाता है, कॉलर स्टैक को साफ़ करता है और परिणाम प्रोसेसर रजिस्ट्री के माध्यम से वापस आ जाता है (बाद में मैं इसे "रजिस्टर ए" कहूंगा)। STDCALL में एक अंतर है, कॉल करने वाले ने स्टैक को साफ नहीं किया है, बछड़ा करते हैं।

आप पूछ रहे हैं कि कौन सा तेज है। कोई नहीं। जब तक आप कर सकते हैं आपको देशी कॉलिंग कन्वेंशन का उपयोग करना चाहिए। अगर कोई रास्ता नहीं है, तो केवल बाहरी पुस्तकालयों का उपयोग करने के लिए, जो कुछ सम्मेलन का उपयोग करने के लिए निश्चित सम्मेलन की आवश्यकता होती है, तो सम्मेलन को बदलें।

इसके अलावा, ऐसे अन्य कन्वेंशन हैं जो कंपाइलर डिफ़ॉल्ट रूप में चुन सकते हैं यानी विजुअल C ++ कंपाइलर FASTCALL का उपयोग करता है जो कि प्रोसेसर रजिस्टरों के अधिक व्यापक उपयोग के कारण सैद्धांतिक रूप से तेज है।

आमतौर पर आपको कुछ बाहरी लाइब्रेरी को दिए गए कॉलबैक फ़ंक्शंस के लिए एक उचित कॉलिंग कन्वेंशन सिग्नेचर देना होगा यानी qsortC लाइब्रेरी से कॉलबैक CDECL होना चाहिए (यदि डिफ़ॉल्ट रूप से कंपाइलर अन्य कन्वेंशन का उपयोग करता है तो हमें कॉलबैक को CDECL के रूप में चिह्नित करना होगा) या अन्य WinAPI कॉलबैक होना चाहिए STDCALL (संपूर्ण WinAPI STDCALL है)।

अन्य सामान्य मामला तब हो सकता है जब आप कुछ बाहरी कार्यों के लिए पॉइंटर्स स्टोर कर रहे हों अर्थात WinAPI फ़ंक्शन के लिए एक पॉइंटर बनाने के लिए इसकी प्रकार परिभाषा को STDCALL के साथ चिह्नित किया जाना चाहिए।

और नीचे एक उदाहरण दिखाया गया है कि कंपाइलर यह कैसे करता है:

/* 1. calling function in C++ */
i = Function(x, y, z);

/* 2. function body in C++ */
int Function(int a, int b, int c) { return a + b + c; }

CDECL:

/* 1. calling CDECL 'Function' in pseudo-assembler (similar to what the compiler outputs) */
push on the stack a copy of 'z', then a copy of 'y', then a copy of 'x'
call (jump to function body, after function is finished it will jump back here, the address where to jump back is in registers)
move contents of register A to 'i' variable
pop all from the stack that we have pushed (copy of x, y and z)

/* 2. CDECL 'Function' body in pseudo-assembler */
/* Now copies of 'a', 'b' and 'c' variables are pushed onto the stack */
copy 'a' (from stack) to register A
copy 'b' (from stack) to register B
add A and B, store result in A
copy 'c' (from stack) to register B
add A and B, store result in A
jump back to caller code (a, b and c still on the stack, the result is in register A)

STDCALL:

/* 1. calling STDCALL in pseudo-assembler (similar to what the compiler outputs) */
push on the stack a copy of 'z', then a copy of 'y', then a copy of 'x'
call
move contents of register A to 'i' variable

/* 2. STDCALL 'Function' body in pseaudo-assembler */
pop 'a' from stack to register A
pop 'b' from stack to register B
add A and B, store result in A
pop 'c' from stack to register B
add A and B, store result in A
jump back to caller code (a, b and c are no more on the stack, result in register A)

नोट: __fastcall तेजी __cdecl से है और STDCALL Windows 64-bit के लिए डिफ़ॉल्ट कॉलिंग सम्मेलन है
DNS

ओह, इसलिए इसे रिटर्न एड्रेस पॉप करना होगा, लॉजिक ब्लॉक साइज जोड़ना होगा, फिर पहले पॉप किए गए रिटर्न एड्रेस पर जाएं। (एक बार रिटायर कॉल करने के बाद, आप अब स्टैक को साफ नहीं कर सकते हैं, लेकिन एक बार स्टैक को साफ करने के बाद, आप अब रिट को कॉल नहीं कर सकते (तब से आपको अपने आप को वापस स्टैक पर रिटायर करने के लिए दफनाने की आवश्यकता होगी, आपको इस मुद्दे पर वापस लाएगा कि आपने स्टैक को साफ नहीं किया है)
दिमित्री

वैकल्पिक रूप से, reg1 पर पॉप लौटें, स्टैक पॉइंटर को बेस पॉइंटर पर सेट करें, फिर reg1 पर जाएं
दिमित्री

वैकल्पिक रूप से, स्टैक के ऊपर से स्टैक पॉइंटर मान को नीचे, साफ, फिर कॉल करें
दिमित्री

15

मैंने एक पोस्टिंग पर ध्यान दिया है जो कहता है कि इससे कोई फर्क नहीं पड़ता कि आप __stdcallएक __cdeclया वीजा वर्से से कॉल करते हैं। ऐसा होता है।

कारण: __cdeclजिन तर्कों को कॉल फ़ंक्शन के लिए पास किया जाता है, उन्हें कॉलिंग फ़ंक्शन द्वारा स्टैक के रूप में हटा दिया जाता है, और __stdcall, इन तर्कों को स्टैक से हटा दिया जाता है। यदि आप किसी __cdeclफ़ंक्शन को कॉल करते हैं __stdcall, तो स्टैक को बिल्कुल भी साफ नहीं किया जाता है, इसलिए अंततः जब __cdeclतर्कों या रिटर्न एड्रेस के लिए स्टैक्ड आधारित संदर्भ का उपयोग करता है तो मौजूदा स्टैक पॉइंटर पर पुराने डेटा का उपयोग करेगा। यदि आप किसी __stdcallफ़ंक्शन को कॉल करते हैं __cdecl, तो __stdcallफ़ंक्शन स्टैक पर तर्कों को साफ करता है, और फिर __cdeclफ़ंक्शन इसे फिर से करता है, संभवतः कॉलिंग फ़ंक्शन को हटाकर जानकारी वापस करता है।

सी के लिए Microsoft सम्मेलन नामों को प्रबंधित करके इसे दरकिनार करने की कोशिश करता है। एक __cdeclफ़ंक्शन एक अंडरस्कोर के साथ उपसर्ग है। एक __stdcallफ़ंक्शन अंडरस्कोर के साथ उपसर्ग करता है और एक हस्ताक्षर "@" और प्रत्यय की संख्या के साथ प्रत्यय हटा दिया जाता है। Eg __cdeclf (x) के रूप में जुड़ा हुआ है _f, जहां 4 बाइट्स के __stdcall f(int x)रूप में जुड़ा हुआ है)_f@4sizeof(int)

यदि आप लिंकर को पिछले करने का प्रबंधन करते हैं, तो डिबगिंग गड़बड़ का आनंद लें।


3

मैं @ adf88 के उत्तर पर सुधार करना चाहता हूं। मुझे लगता है कि STDCALL के लिए छद्म कोड वास्तविकता में कैसे होता है इसका तरीका नहीं दर्शाता है। 'a', 'b', और 'c' फ़ंक्शन बॉडी में स्टैक से पॉप नहीं होते हैं। इसके बजाय वे retनिर्देश द्वारा पॉप किए गए हैं ( ret 12इस मामले में उपयोग किया जाएगा) कि एक झपट्टा में कॉलर पर वापस कूदता है और एक ही समय में स्टैक से 'ए', 'बी', और 'सी' पॉप करता है।

यहाँ मेरा संस्करण मेरी समझ के अनुसार सही किया गया है:

STDCALL:

/* 1. calling STDCALL in pseudo-assembler (similar to what the compiler outputs) */
push on the stack a copy of 'z', then copy of 'y', then copy of 'x'
call
move contents of register A to 'i' variable

/* 2. STDCALL 'Function' body in pseaudo-assembler */ copy 'a' (from stack) to register A copy 'b' (from stack) to register B add A and B, store result in A copy 'c' (from stack) to register B add A and B, store result in A jump back to caller code and at the same time pop 'a', 'b' and 'c' off the stack (a, b and c are removed from the stack in this step, result in register A)


2

यह फ़ंक्शन प्रकार में निर्दिष्ट है। जब आपके पास एक फ़ंक्शन पॉइंटर होता है, तो यह स्पष्ट रूप से stdcall नहीं होने पर cdecl माना जाता है। इसका मतलब है कि अगर आपको एक stdcall पॉइंटर और cdecl पॉइंटर मिलता है, तो आप उन्हें एक्सचेंज नहीं कर सकते। दो फ़ंक्शन प्रकार बिना किसी समस्या के एक-दूसरे को कॉल कर सकते हैं, यह केवल एक प्रकार हो रहा है जब आप दूसरे की अपेक्षा करते हैं। गति के रूप में, वे दोनों एक ही भूमिका करते हैं, बस बहुत अलग जगह पर, यह वास्तव में अप्रासंगिक है।


1

कॉल करने वाले और शांत व्यक्ति को आह्वान के बिंदु पर एक ही सम्मेलन का उपयोग करने की आवश्यकता है - यही एकमात्र तरीका है जो मज़बूती से काम कर सकता है। कॉलर और कैली दोनों एक पूर्वनिर्धारित प्रोटोकॉल का पालन करते हैं - उदाहरण के लिए, जिसे स्टैक को साफ करने की आवश्यकता है। यदि कन्वेंशन आपके कार्यक्रम को बेअसर व्यवहार में चलाती है - तो बस शानदार ढंग से क्रैश हो जाता है।

यह केवल प्रति इनोकेशन साइट के लिए आवश्यक है - कॉलिंग कोड स्वयं किसी भी कॉलिंग सम्मेलन के साथ एक फ़ंक्शन हो सकता है।

आपको उन सम्मेलनों के बीच प्रदर्शन में कोई वास्तविक अंतर नहीं देखना चाहिए। यदि यह एक समस्या बन जाती है तो आपको आमतौर पर कम कॉल करने की आवश्यकता होती है - उदाहरण के लिए, एल्गोरिथ्म को बदलें।


1

वे चीजें कंपाइलर- और प्लेटफॉर्म-विशिष्ट हैं। C ++ को छोड़कर न तो C और न ही C ++ मानक कुछ भी कहते हैं extern "C"

एक कॉलर को कैसे पता चलेगा कि उसे स्टैक को मुक्त करना चाहिए?

कॉल करने वाला फ़ंक्शन के कॉलिंग कन्वेंशन को जानता है और तदनुसार कॉल को संभालता है।

कॉल साइट पर, क्या कॉलर को पता है कि क्या कहा जा रहा है कि फ़ंक्शन एक सीडीसीएल या एक सीडीसीएल फ़ंक्शन है?

हाँ।

यह कैसे काम करता है ?

यह फ़ंक्शन डिक्लेरेशन का हिस्सा है।

कॉल करने वाले को कैसे पता चलता है कि उसे स्टैक को मुक्त करना चाहिए या नहीं?

फोन करने वाले को कॉलिंग कन्वेंशन पता है और तदनुसार कार्य कर सकता है।

या यह लिंकर्स जिम्मेदारी है?

नहीं, कॉलिंग कन्वेंशन एक फ़ंक्शन की घोषणा का हिस्सा है, इसलिए कंपाइलर को वह सब कुछ पता है जो उसे जानना चाहिए।

यदि एक फ़ंक्शन जिसे stdcall के रूप में घोषित किया जाता है, तो एक फ़ंक्शन (जिसमें cdecl के रूप में एक कॉलिंग कन्वेंशन है), या दूसरा तरीका गोल है, क्या यह अनुचित होगा?

क्यों?

सामान्य तौर पर, हम कह सकते हैं कि कौन सी कॉल तेज होगी - सीडीसीएल या स्टडकल?

मुझे नहीं पता। झसे आज़माओ।


0

a) जब कॉलर द्वारा cdecl फ़ंक्शन को कॉल किया जाता है, तो कॉल करने वाले को यह कैसे पता चलेगा कि उसे स्टैक को मुक्त करना चाहिए?

cdeclसंशोधक (आदि या समारोह सूचक प्रकार) समारोह प्रोटोटाइप का हिस्सा तो फोन करने वाले वहां से जानकारी प्राप्त करें और उसके अनुसार कार्य करता है।

ख) यदि कोई फ़ंक्शन जिसे stdcall के रूप में घोषित किया जाता है, तो एक फ़ंक्शन (जिसमें cdecl के रूप में एक कॉलिंग सम्मेलन होता है), या दूसरा तरीका गोल है, क्या यह अनुचित होगा?

नहीं यह ठीक है।

ग) सामान्य तौर पर, हम यह कह सकते हैं कि कौन सी कॉल तेज होगी - सीडीसीएल या स्टडकल?

सामान्य तौर पर, मैं ऐसे किसी भी बयान से बचना चाहूंगा। भेद मायने रखता है। जब आप va_arg फ़ंक्शन का उपयोग करना चाहते हैं। सिद्धांत रूप में, यह stdcallतेजी से हो सकता है और छोटे कोड उत्पन्न करता है क्योंकि यह स्थानीय लोगों को पॉपिंग के साथ तर्कों को जोड़ने की अनुमति देता है, लेकिन OTOH के साथ cdecl, आप एक ही काम कर सकते हैं, अगर आप चतुर हैं।

कॉलिंग कन्वेंशन जो तेजी से होने का लक्ष्य रखते हैं, आमतौर पर कुछ रजिस्टर-पासिंग करते हैं।


-1

कॉलिंग सम्मेलनों का C / C ++ प्रोग्रामिंग लैंग्वेज से कोई लेना-देना नहीं है और यह इस बात पर स्पष्ट नहीं है कि किसी कंपाइलर ने दी हुई भाषा को कैसे लागू किया। यदि आप लगातार एक ही कंपाइलर का उपयोग करते हैं, तो आपको कॉलिंग कन्वेंशन के बारे में चिंता करने की आवश्यकता नहीं है।

हालाँकि, कभी-कभी हम चाहते हैं कि बाइनरी कोड अलग-अलग संकलक द्वारा संकलित करके सही ढंग से संचालित हो। जब हम ऐसा करते हैं तो हमें एप्लिकेशन बाइनरी इंटरफेस (एबीआई) नामक कुछ को परिभाषित करने की आवश्यकता होती है। ABI परिभाषित करता है कि कंपाइलर C / C ++ स्रोत को मशीन-कोड में कैसे परिवर्तित करता है। इसमें कॉलिंग कन्वेंशन, नाम मैनलिंग और वी-टेबल लेआउट शामिल होंगे। cdelc और stdcall दो अलग-अलग कॉलिंग कन्वेंशन हैं जिनका आमतौर पर x86 प्लेटफॉर्म पर उपयोग किया जाता है।

सोर्स हेडर में कॉलिंग कन्वेंशन की जानकारी रखने से, कंपाइलर को पता चल जाएगा कि दिए गए एक्जीक्यूटेबल के साथ सही तरीके से इंटर-ऑपरेट करने के लिए किस कोड को जनरेट करना है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.