stdcall এবং cdecl


92

এখানে (অন্যদের মধ্যে) দুই ধরণের কলিং কনভেনশন রয়েছে - স্টডক্যাল এবং সিডিসিএল । তাদের সম্পর্কে আমার কয়েকটি প্রশ্ন রয়েছে:

  1. যখন কোনও সিডিসিএল ফাংশন বলা হয়, একজন কলকারী কীভাবে জানবেন যে এটি স্ট্যাকটি খালি করা উচিত? কল সাইটে, কলকারী কি জানেন যে ফাংশনটি বলা হচ্ছে এটি একটি সিডিসিএল বা একটি স্টডক্যাল ফাংশন? এটা কিভাবে কাজ করে ? ফোনকারীটি কীভাবে জানতে পারে যে এটি স্ট্যাকটি মুক্ত করবে কিনা? নাকি দায়বদ্ধতার দায়িত্ব?
  2. Stdcall হিসাবে ঘোষিত কোনও ফাংশন যদি কোনও ফাংশনকে কল করে (যার সিডেক্সেল হিসাবে একটি কলিং কনভেনশন রয়েছে), বা অন্যভাবে, তবে এটি কি অনুচিত?
  3. সাধারণভাবে, আমরা কি বলতে পারি যে কোন কলটি দ্রুততর হবে - সিডিসিএল বা স্টেটক্যাল?

9
এখানে অনেক ধরণের কলিং কনভেনশন রয়েছে, এর মধ্যে মাত্র দুটি two en.wikipedia.org/wiki/X86_calling_conventions
গরুর হাঁসের

4
দয়া করে, একটি সঠিক উত্তর চিহ্নিত করুন
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;

তবে, ধরে নিলাম যে প্রোগ্রামার সংকলক ত্রুটিগুলি উপেক্ষা করবে না, সংকলকটি স্ট্যাকটি পরিষ্কার করার জন্য প্রয়োজনীয় কোড তৈরি করবে কারণ এটি জড়িত ফাংশনগুলির কলিং কনভেনশনগুলি জানতে পারবে।

(২) উভয় উপায়েই কাজ করা উচিত। আসলে, উইন্ডোজ এপিআইয়ের সাথে যোগাযোগ করে এমন কোডে এটি প্রায়শই ঘন ঘন ঘটে কারণ __cdeclভিজ্যুয়াল সি ++ সংকলক অনুসারে সি এবং সি ++ প্রোগ্রামগুলির জন্য ডিফল্ট এবং উইনাপি ফাংশনগুলি __stdcallকনভেনশনটি ব্যবহার করে

(3) দুজনের মধ্যে কোনও বাস্তব পারফরম্যান্সের পার্থক্য থাকা উচিত নয়।


একটি ভাল উদাহরণের জন্য +1 এবং সম্মেলনের ইতিহাস কল করার বিষয়ে রেমন্ড চেনের পোস্ট। আগ্রহীদের জন্য, এর অন্যান্য অংশগুলিও ভাল পড়া read
অরেগনঘস্ট

রেমন্ড চেনের জন্য +1। বিটিডব্লিউ (ওটি): কেন আমি ব্লগ অনুসন্ধান বাক্স ব্যবহার করে অন্যান্য অংশগুলি খুঁজে পাচ্ছি না? গুগল সেগুলি খুঁজে পাচ্ছে, তবে এমএসডিএন ব্লগগুলি নয়?
নর্ডিক মেইনফ্রেমে

44

সিডিসিএল আর্গুমেন্টগুলিকে বিপরীত ক্রমে স্ট্যাকের দিকে ঠেলে দেওয়া হয়, কলার স্ট্যাকটি সাফ করে দেয় এবং প্রসেসরের রেজিস্ট্রি দিয়ে ফলাফলটি ফিরে আসে (পরে আমি এটি "রেজিস্টার এ" বলব)। এসটিডিসিএলে একটি পার্থক্য রয়েছে, কলার স্ট্যাক সাফ করে না, কল করে do

আপনি জিজ্ঞাসা করছেন কোনটি দ্রুত? কেউ না. আপনার যতক্ষণ সম্ভব নেটিভ কলিং কনভেনশন ব্যবহার করা উচিত। বাহ্যিক গ্রন্থাগারগুলি ব্যবহার করার জন্য যখন নির্দিষ্ট কোনও কনভেনশন প্রয়োজন তখন কেবল কোনও উপায় না থাকলে কনভেনশন পরিবর্তন করুন।

এছাড়াও, অন্যান্য কনভেনশন রয়েছে যে সংকলকটি ডিফল্ট হিসাবে বেছে নিতে পারে অর্থাৎ ভিজ্যুয়াল সি ++ সংকলক FASTCALL ব্যবহার করে যা প্রসেসরের রেজিস্টারগুলির আরও ব্যাপক ব্যবহারের কারণে তাত্ত্বিকভাবে দ্রুত faster

সাধারণত আপনাকে অবশ্যই কোনও বাহ্যিক গ্রন্থাগারে পাস হওয়া কলব্যাক ফাংশনগুলিতে একটি যথাযথ কলিং কনভেনশন স্বাক্ষর দিতে হবে অর্থাৎ qsortসি লাইব্রেরি থেকে কলব্যাক অবশ্যই সিডিইসিএল হতে হবে (যদি পূর্বনির্ধারিত সংকলকটি অন্য কনভেনশন ব্যবহার করে তবে আমাদের অবশ্যই কলব্যাকটি সিডিসিএল হিসাবে চিহ্নিত করতে হবে) বা বিভিন্ন উইনাপি কলব্যাক অবশ্যই হতে হবে এসটিডিসিএল (পুরো উইনপিআইটি এসটিডিসিএল)।

অন্যান্য সাধারণ ক্ষেত্রে হতে পারে যখন আপনি কিছু বাহ্যিক কার্যের জন্য পয়েন্টারগুলি সঞ্চয় করছেন অর্থাৎ উইনএপিআই ফাংশনে একটি পয়েন্টার তৈরি করতে তার প্রকারের সংজ্ঞাটি এসটিডিসিএল দিয়ে চিহ্নিত করতে হবে।

এবং নীচে একটি সংকলক কীভাবে তা দেখায় তার একটি উদাহরণ রয়েছে:

/* 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; }

সিডিসিএল:

/* 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)

এসটিডিসিএল:

/* 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)

দ্রষ্টব্য: __ফ্যাস্কলটি __cdecl এর চেয়ে দ্রুত এবং STDCALL উইন্ডোজ for৪-বিটের জন্য ডিফল্ট কলিং কনভেনশন রয়েছে
dns

ওহ সুতরাং এটি অবশ্যই রিটার্ন ঠিকানাটি পপ করতে হবে, যুক্তি ব্লকের আকার যুক্ত করতে হবে, তারপরে পূর্বে পপড ফিরতি ঠিকানায় ঝাঁপ দাও? (একবার আপনি পুনরায় কল করলে আপনি স্ট্যাকটি আর পরিষ্কার করতে পারবেন না, তবে একবার স্ট্যাকটি পরিষ্কার করার পরে আপনি আর রিটকে কল করতে পারবেন না (যেহেতু আপনি স্ট্যাকটি রিট করার জন্য আপনাকে নিজেকে সমাহিত করতে হবে এবং আপনি যে স্ট্যাকটি পরিষ্কার করেন নি সেই ইস্যুতে আপনাকে ফিরিয়ে আনতে হবে)
দিমিত্রি

বিকল্পভাবে, পপ রেজি 1 তে ফিরে আসে, বেস পয়েন্টারে স্ট্যাক পয়েন্টার সেট করে, তারপরে রেজি 1
দিমিত্রি

বিকল্পভাবে, স্ট্যাকের পয়েন্টার মানটিকে স্ট্যাকের শীর্ষ থেকে নীচে নিয়ে যান, পরিষ্কার করুন, তারপরে রিট কল করুন
দিমিত্রি

15

আমি একটি পোস্ট লক্ষ্য করেছিলাম যাতে বলা হয় যে আপনি __stdcallকোনও __cdeclভিসার বিপরীতে কল দিলে কিছু যায় আসে না । এটা করে.

কারণ: __cdeclডাকা ফাংশনগুলিতে পাস হওয়া আর্গুমেন্টগুলির সাথে কলিং ফাংশন দ্বারা স্ট্যাকটি সরিয়ে ফেলা হয়, ইন __stdcall, আর্গুমেন্টগুলি কল ফাংশন দ্বারা স্ট্যাক থেকে সরিয়ে ফেলা হয়। যদি আপনি একটি __cdeclদিয়ে কোনও ফাংশন কল করেন __stdcall, স্ট্যাকটি একেবারেই পরিষ্কার হয় না, সুতরাং শেষ পর্যন্ত যখন __cdeclআর্গুমেন্ট বা রিটার্ন ঠিকানার জন্য স্ট্যাকড ভিত্তিক রেফারেন্স ব্যবহার করে বর্তমান স্ট্যাক পয়েন্টারে পুরানো ডেটা ব্যবহার করা হবে। আপনি যদি একটি __stdcallথেকে কোনও ফাংশন কল করেন __cdecl, __stdcallফাংশনটি স্ট্যাকের আর্গুমেন্টগুলি পরিষ্কার করে এবং তারপরে __cdeclফাংশনটি আবার এটি করে, সম্ভবত কলিং ফাংশনগুলি ফেরত তথ্য সরিয়ে দেয়।

সি এর জন্য মাইক্রোসফ্ট কনভেনশন নামগুলিতে ম্যাঙ্গাল করে এটিকে রোধ করার চেষ্টা করে। একটি __cdeclফাংশন একটি আন্ডারস্কোর সহ উপস্থাপিত হয়। একটি __stdcallফাংশন একটি আন্ডারস্কোর সহ উপসর্গযুক্ত এবং একটি চিহ্ন "@" এবং প্রত্যাহার করা বাইটের সংখ্যার সাথে প্রত্যয়যুক্ত। যেমন __cdeclচ (x) এর হিসাবে লিঙ্ক করা হয়েছে _f, __stdcall f(int x)যেমন লিঙ্ক করা হয়েছে _f@4যেখানে sizeof(int)4 বাইট হয়)

যদি আপনি লিঙ্কারটি অতিক্রম করার ব্যবস্থা করেন তবে ডিবাগিং মেস উপভোগ করুন।


3

আমি @ adf88 এর উত্তরে উন্নতি করতে চাই আমি অনুভব করি যে এসটিডিসিএল এর সিউডোকোড বাস্তবে এটি কীভাবে ঘটে তার উপায় প্রতিফলিত করে না। 'ক', 'বি', এবং 'সি' ফাংশন বডিটির স্ট্যাক থেকে পপ করা হয় না। পরিবর্তে তারা সেই retনির্দেশের দ্বারা পপ করা হয় ( ret 12এই ক্ষেত্রে ব্যবহৃত হবে) যে এক ঝাঁকুনিতে কলারের কাছে ফিরে আসে এবং একই সাথে স্ট্যাক থেকে 'এ', 'বি' এবং 'সি' পপ করে।

এখানে আমার উপলব্ধি অনুসারে আমার সংস্করণ সংশোধন করা হয়েছে:

এসটিডিসিএল:

/* 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

এটি ফাংশনের ধরণে নির্দিষ্ট করা আছে। আপনার যদি কোনও ফাংশন পয়েন্টার থাকে, তখন এটি স্পষ্টত স্টাডক্যাল না করে সিডিডেল বলে ধরে নেওয়া হয়। এর অর্থ হ'ল আপনি যদি একটি স্ট্ডকাল পয়েন্টার এবং একটি সিডিকেল পয়েন্টার পান তবে আপনি সেগুলি বিনিময় করতে পারবেন না। দুটি ফাংশন প্রকার ইস্যু ছাড়াই একে অপরকে কল করতে পারে, আপনি যখন অন্যটির প্রত্যাশা করেন তখন এটি কেবল এক ধরণের হয়। গতির ক্ষেত্রে, তারা উভয়ই একই ভূমিকা পালন করে, খুব সামান্য ভিন্ন জায়গায়, এটি সত্যই অপ্রাসঙ্গিক।


1

আহ্বানের সময় কলার এবং কলিকে একই সম্মেলনটি ব্যবহার করা দরকার - এটি নির্ভরযোগ্যতার সাথে কাজ করতে পারে way কলার এবং কলি উভয়ই পূর্বনির্ধারিত প্রোটোকল অনুসরণ করে - উদাহরণস্বরূপ, কাকে স্ট্যাকটি পরিষ্কার করা দরকার। যদি কনভেনশনগুলি আপনার প্রোগ্রামের সাথে মেলে না এমন সংশোধিত আচরণে চলে - সম্ভবত কেবল দর্শনীয়ভাবে ক্র্যাশ হয়।

কেবলমাত্র অনুরোধ সাইটের জন্য এটি প্রয়োজন - কলিং কোড নিজেই কোনও কলিং কনভেনশন সহ একটি ফাংশন হতে পারে।

এই সম্মেলনগুলির মধ্যে পারফরম্যান্সের ক্ষেত্রে আপনার কোনও বাস্তব পার্থক্য লক্ষ্য করা উচিত নয়। যদি এটি কোনও সমস্যা হয়ে থাকে তবে আপনার সাধারণত কম কল করা প্রয়োজন - উদাহরণস্বরূপ, অ্যালগরিদম পরিবর্তন করুন।


1

এই জিনিসগুলি সংকলক- এবং প্ল্যাটফর্ম-নির্দিষ্ট। সি বা সি ++ স্ট্যান্ডার্ড কেউই সি ++ ব্যতীত কনভেনশন কল করার বিষয়ে কিছুই বলেন না extern "C"

একজন কলকারী কীভাবে জানেন যে এটি স্ট্যাকটি মুক্ত করে দেওয়া উচিত?

কলকারী ফাংশনটির কলিং কনভেনশন জানেন এবং সে অনুযায়ী কলটি পরিচালনা করে।

কল সাইটে, কলকারী কি জানেন যে ফাংশনটি বলা হচ্ছে এটি একটি সিডিসিএল বা একটি স্টডক্যাল ফাংশন?

হ্যাঁ.

এটা কিভাবে কাজ করে ?

এটি ফাংশন ঘোষণার অংশ।

ফোনকারীটি কীভাবে জানতে পারে যে এটি স্ট্যাকটি মুক্ত করবে কিনা?

কলকারী কলিং কনভেনশনগুলি জানেন এবং সে অনুযায়ী কাজ করতে পারেন।

নাকি দায়বদ্ধতার দায়িত্ব?

না, কলিং কনভেনশন কোনও ফাংশনের ঘোষণার অংশ, তাই সংকলকটি যা জানার প্রয়োজন তা সমস্তই জানে।

Stdcall হিসাবে ঘোষিত কোনও ফাংশন যদি কোনও ফাংশনকে কল করে (যার সিডেক্সেল হিসাবে একটি কলিং কনভেনশন রয়েছে), বা অন্যভাবে, তবে এটি কি অনুচিত?

না কেন এটা করা উচিত?

সাধারণভাবে, আমরা কি বলতে পারি যে কোন কলটি দ্রুততর হবে - সিডিসিএল বা স্টেটক্যাল?

আমি জানি না। এটা পরীক্ষা করো.


0

ক) যখন একজন সিডিসিএল ফাংশন কলকারী কল করে, একজন কলকারী কীভাবে জানতে পারে যে এটি স্ট্যাকটি খালি করা উচিত?

cdeclপরিবর্তক (ইত্যাদি বা ফাংশন পয়েন্টার টাইপ) ফাংশন প্রোটোটাইপ অংশ তাই আহ্বানকারী সেখান থেকে তথ্য পান এবং সেই অনুযায়ী কাজ করে।

খ) স্টাডকাল হিসাবে ঘোষিত কোনও ফাংশন যদি কোনও ফাংশনকে কল করে (যার সিডিকেল হিসাবে কলিং কনভেনশন রয়েছে) বা অন্যভাবে, তবে এটি কি অনুচিত?

না এটা ঠিক আছে.

গ) সাধারণভাবে, আমরা কি বলতে পারি যে কোন কলটি দ্রুততর হবে - সিডিসিএল বা স্টেটক্যাল?

সাধারণভাবে, আমি এই জাতীয় কোনও বক্তব্য থেকে বিরত থাকব। পার্থক্য বিষয় যেমন। যখন আপনি va_arg ফাংশন ব্যবহার করতে চান। তাত্ত্বিকভাবে, এটি stdcallদ্রুত এবং ছোট কোড উত্পন্ন করতে পারে কারণ এটি স্থানীয়দের পপিংয়ের সাথে যুক্তিগুলি পপিংয়ের সাথে একত্রিত করতে দেয়, তবে ওটিওএইচ cdecl, আপনি যদি চালাক হন তবে আপনি একই জিনিসটি করতে পারেন।

কলিং কনভেনশনগুলি যে লক্ষ্যগুলি দ্রুত হতে হয় সাধারণত কিছু নিবন্ধক-পাসিং করে।


-1

কলিং কনভেনশনগুলির সি / সি ++ প্রোগ্রামিং ভাষাগুলির সাথে কোনও সম্পর্ক নেই এবং সংকলক প্রদত্ত ভাষাটি কীভাবে প্রয়োগ করে তা সুনির্দিষ্ট। যদি আপনি ধারাবাহিকভাবে একই সংকলকটি ব্যবহার করেন তবে আপনার কখনই কনভেনশন কল করার বিষয়ে চিন্তা করার দরকার নেই।

যাইহোক, কখনও কখনও আমরা সঠিকভাবে আন্তঃচালিত করতে বিভিন্ন সংকলক দ্বারা বাইনারি কোড সংকলিত করতে চাই। যখন আমরা এটি করি তখন আমাদের অ্যাপ্লিকেশন বাইনারি ইন্টারফেস (এবিআই) নামক কিছু সংজ্ঞা দেওয়া দরকার। এবিআই সংজ্ঞা দেয় কীভাবে সংকলক সি / সি ++ উত্সটিকে মেশিন-কোডে রূপান্তর করে। এর মধ্যে কলিং কনভেনশন, নাম ম্যাংলিং, এবং ভি-টেবিল বিন্যাস অন্তর্ভুক্ত থাকবে। cdelc এবং stdcall হল দুটি ভিন্ন কলিং কনভেনশন যা সাধারণত x86 প্ল্যাটফর্মে ব্যবহৃত হয়।

উত্স শিরোনামে কলিং কনভেনশন সম্পর্কিত তথ্য স্থাপন করে, সংকলক প্রদত্ত এক্সিকিউটেবলের সাথে সঠিকভাবে আন্তঃ-পরিচালনা করতে কোন কোড তৈরি করতে হবে তা জানতে পারবে।

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.