بازگشت مقادیر چندگانه از تابع های ++C – راهنمای کاربردی


شاید تاکنون برایتان پیش آمده باشد که بخواهید مقادیر چندگانهای را از یک تابع یا تابع های ++C بازگشت دهید. بهترین روش برای این کار کدام است؟ در ادامه چند روش برای این کار ارائه کردهایم:
- استفاده از پارامترهای خروجی
auto output_1(int &i1) { i1 = 11; return 12; }
2. استفاده از ساختار محلی
auto struct_2() { struct _ { int i1, i2; }; return _{21, 22}; }
3. استفاده از یک std::pair
auto pair_2() { return std::make_pair(31, 32); }
4. استفاده از یک std::tuple
auto tuple_2() { return std::make_tuple(41, 42); }
در این نوشته تلاش میکنیم بررسی کنیم که کدام یک از روشهای فوق برای بازگشت دادن مقادیر چندگانه در تابعهای ++C بهتر است.
چرا باید بخواهیم مقادیر چندگانهای را بازگشت دهیم؟
یک مثال معمول برای این حالت ()std::from_chars است که یک تابع در ++C نسخه 17 است و شباهت زیادی به ()strtol دارد؛ اما ()from_chars تابعی است که 3 مقدار بازگشت میدهد. یک عدد تجزیهشده، یک کد خطا و یک اشارهگر به کاراکتر نخست نامعتبر.
این تابع از ترکیبی از تکنیکها استفاده میکند. عدد به صورت یک پارامتر خروجی بازگشت مییابد؛ اما کد خطا و اشارهگر به صورت یک ساختار بازگشت مییابند. دلایل این کار را در ادامه بررسی میکنیم.
بازگشت دادن مقادیر چندگانه با استفاده از پارامترهای خروجی
کد نمونه:
این کد به صورت زیر کامپایل میشود:
این روش مزایا و معایبی دارد:
مزایا
- این یک روش کلاسیک و درک آن آسان است.
- با استاندارد ++C کار میکند که شامل استاندارد C یعنی استفاده از اشارهگرها است.
- از overloading تابع پشتیبانی میکند.
معایب
- آدرس پارامتر اول باید پیش از فراخوانی تابع بارگذاری شده باشد.
- پارامتر نخست با استفاده از پشته ارسال میشود و از این رو کند است.
- به دلیل وجود System V AMD64 ABI، میتوانیم ثباتهایی تا 6 آدرس را ارسال کنیم. برای ارسال بیش از 6 پارامتر باید از پشته استفاده شود که باز هم آن را کندتر میسازد.
برای نمایش عملی آخرین عیب در فهرست فوق کد نمونهای با 7 پارامتر خروجی ارائه کردهایم:
و دیاسمبل کد ()output_7 به صورت زیر است:
آدرس هفتم از طریق پشته ارسال میشود و از این رو آدرس را روی پشته قرار میدهیم که در این صورت باید آن را دوباره از پشته بخوانیم و سپس مقدار را به آن آدرس خروجی بدهیم. میبینید که عملیات حافظه زیادی مورد نیاز است.
بازگشت مقادیر چندگانه با استفاده از ساختار محلی
کد نمونه:
Disassembly:
این روش نیز مزایا و معایبی دارد که در ادامه بررسی کردهایم:
مزایا
- با هر استاندارد ++C که شامل C نیز میشود کار میکند؛ با این حال ساختار باید بیرون از دامنه تابع اعلان شود.
- در ثباتها تا 128 بیت را بازگشت میدهد و دیگر نیازی به پشته نیست. بنابراین سریع است.
- به آدرس پارامترها نیاز ندارد و بدین ترتیب کامپایلر بهتر میتواند کد را بهینهسازی کند.
معایب
- نیازمند اعلان binding ساختیافته در نسخه 17 ++C است.
- این تابع نمیتواند overload شود، چون نوع بازگشتی بخشی از شناسه تابع است.
وقتی تلاش میکنیم مقادیر بیشتری را بازگشت دهیم چه اتفاقی رخ میدهد؟ بر اساس System V AMD64 ABI مقادیر تا 128 بیت در RAX و RDX ذخیره میشوند. بنابراین تا چهار عدد صحیح 32 بیتی را میتوان در این ثباتها بازگشت داد. با این حال اگر به یک بایت بیشتر نیاز داشته باشیم باید از پشته استفاده کنیم.
در این مورد نیز همچنان نیاز نداریم آدرسهای پارامترهای خروجی از قبل بارگذاری شده باشند و از این رو سریعتر از روش پارامترهای خروجی است.
بازگشت مقادیر چندگانه با استفاده از std::pair
کد نمونه:
کد ایجاد شده اسمبلی به صورت زیر است:
مزایا
- تنها به یک خط کد نیاز دارد.
- نیازی به اعلان ساختار محلی وجود ندارد.
- همانند ساختارها تا 128 بیت را در ثباتها بازگشت میدهد و به پشته نیازی نیست.
معایب
- Pair به این معنی است که تنها دو مقدار بازگشت مییابند.
- همانند ساختار، تابع نمیتواند overload شود.
بازگشت مقادیر چندگانه با استفاده از std::tuple
کد نمونه:
این کد به صورت زیر کامپایل میشود:
مزایا
- کد منبع همانند روش std::pair یکخطی است.
- برخلاف std::pair به راحتی میتوان مقادیر بیشتری اضافه کرد.
معایب
متأسفانه disassembly یک کیسه مخلوط است. ما باید یک آدرس از چندتایی خروجی را به تابع ارسال کنیم، یعنی برای هر عنصر چندتایی به یک آدرس نیاز داریم. حتی در مورد دو عدد صحیح (64 بیت)، مقادیر بازگشتی همواره روی پشته هستند و از این رو کند است.
چه میشود اگر مقادیر بیشتری را به tuple اضافه کنیم؟
افزودن مقادیر بیشتر تغییر زیادی در disassembly ایجاد نمیکند، چون ما همچنان تنها یک آدرس برای اشاره به پشته بازگشت میدهیم و سپس مقادیر را زیر آن آدرس قرار میدهیم و در نهایت آنها را مجدداً با استفاده از printf() از پشته بارگذاری میکنیم.
این ساختار کندتر از روش pair و ساختار است چون هر دوی آنها تا 128 بیت را در رجیسترها بازگشت میدهند. اما از روش پارامترهای خروجی سریعتر است، چون در آن روش باید چندین آدرس را به تابع ارسال کنیم و نه فقط یک آدرس.
سخن پایانی
سریعترین روش برای بازگشت پارامترهای چندگانه در نسخه C++ 17، استفاده از ساختار محلی و std::pair است. std::pair در مواردی که باید دو مقدار بازگشت یابند به عنوان سریعترین و سادهترین روش بیشترین ترجیح را دارد. استفاده از پارامترهای خروجی در مواردی که تابع overload میشود مورد نیاز است. به همین دلیل است که ()std::from_chars از پارامترهای خروجی استفاده میکند و یک ساختار بازگشت میدهد.
به طور خلاصه باید گفت که std::pair راحتترین و سریعترین روش برای بازگشت دو مقدار است. اگر بخواهیم بیش از دو مقدار را بازگشت دهیم، باید از ساختار محلی (سریعتر) یا از std::tuple (راحتتر) استفاده کنیم.
اگر این مطلب برای شما مفید بوده است، آموزشهای زیر نیز به شما پیشنهاد میشوند:
- مجموعه آموزشهای برنامهنویسی
- آموزش برنامه نویسی C++
- مجموعه آموزشهای مهندسی نرم افزار
- آموزش پیشرفته C++ (شی گرایی در سی پلاس پلاس)
- آیا زبان برنامهنویسی C هنوز ارزش یادگیری دارد؟
- چند نخی (Multi-Threading) در ++C — به زبان ساده
==