stdarg.h
<stdarg.h> هو ملف رأس في مكتبة C القياسية للغة البرمجة C، يسمح للدوال بقبول عدد غير محدد من الوسائط . [ 1 ] يوفر هذا الملف إمكانية المرور على قائمة وسائط الدوال ذات العدد والنوع غير المعروفين. توفر لغة C++ هذه الوظيفة في ملف الرأس<cstdarg>.
<stdarg.h>تُستخدم محتويات عادةً في الدوال المتغيرة ، على الرغم من أنه يمكن استخدامها في دوال أخرى (على سبيل المثال، vprintf) يتم استدعاؤها بواسطة الدوال المتغيرة.
إنشاء دوال متغيرة الوسائط
الدوال المتغيرة هي دوال تأخذ عددًا متغيرًا من الوسائط، ويتم تعريفها باستخدام علامة الحذف (...) بدلًا من الوسيط الأخير. مثال على هذه الدوال هو printf. وبالمثل، تتم التصريحات والتعريفات بنفس الطريقة.
int check ( int a , double b , ...);دالة التحقق ( int a , double b , ...) { // ... }وفقًا للمعيار، فإن الدوال المتغيرة بدون أي معلمات مسماة غير مسموح بها في C17 وما قبلها، ولكن في C++ و C23 [ 2 ] يُسمح بمثل هذا الإعلان.
في لغة C ، يجب أن تسبق الفاصلة علامة الحذف إذا تم تحديد معلمة مسماة، بينما في لغة C++ كان الأمر اختياريًا حتى C++26 عندما تم إهمال هذا الأمر.
بعض تعريفات الدوال على نمط لغة C من K&R لا تستخدم علامات الحذف. [ 3 ]
أنواع stdarg.h
| اسم | وصف | التوافق |
|---|---|---|
va_list | نوع لتكرار الوسائط | C89 |
وحدات الماكرو في ملف stdarg.h
| اسم | وصف | التوافق |
|---|---|---|
va_start | ابدأ بتكرار الحجج باستخدامva_list | C89 |
va_arg | استرجاع وسيط | C89 |
va_end | مجاناًva_list | C89 |
va_copy | انسخ محتويات أحدهما va_listإلى الآخر | C99 |
الوصول إلى الحجج
وفقًا للمعيار، يمكن الوصول إلى الوسائط غير المسماة من خلال متغير من النوع المحدد va_listفي الدالة ذات الوسائط المتعددة، مع va_startتوفير الماكرو أيضًا كمعامل مُسمى أخير للدالة. في C23، الوسيط الثاني اختياري ولن يتم تقييمه. [ 2 ] بعد ذلك، va_argينتج عن كل استدعاء للماكرو الوسيط التالي. الوسيط الأول va_argهو نوع الوسيط va_list، والثاني هو نوع الوسيط التالي الذي يتم تمريره إلى الدالة. كخطوة أخيرة، va_endيجب استدعاء الماكرو على الوسيط va_listقبل أن تُعيد الدالة قيمتها. تجدر الإشارة إلى أنه ليس من الضروري قراءة جميع الوسائط.
يوفر C99 ماكرو إضافيًا، va_copyيمكنه تكرار حالة عنصر ما va_list. يقوم استدعاء الماكرو va_copy(va2, va1)بنسخ الحالة va1إلى العنصر الآخر va2.
لا توجد طريقة محددة لحساب أو تصنيف الوسائط غير المسماة التي تُمرر إلى الدالة ذات الوسائط المتعددة. ينبغي للدالة ببساطة تحديد ذلك بطريقة ما، وتختلف الوسائل المستخدمة. ومن الاصطلاحات الشائعة ما يلي:
- استخدام سلسلة تنسيق تشبه "
printfأو"scanfمع محددات مضمنة تشير إلى أنواع الوسائط. - قيمة حارس في نهاية الوسائط المتغيرة.
- وسيط العد الذي يشير إلى عدد الوسائط المتغيرة.
تمرير وسائط غير مسماة إلى استدعاءات أخرى
بما أن حجم قائمة الوسائط غير المسماة غير معروف عمومًا، فإن اصطلاحات الاستدعاء التي تستخدمها معظم المترجمات لا تسمح بتحديد حجم كتلة الوسائط غير المسماة التي يشير إليها المؤشر va_listداخل الدالة المستقبلة. ونتيجةً لذلك، لا توجد طريقة عامة وموثوقة لتمرير الوسائط غير المسماة إلى دالة أخرى ذات عدد متغير من الوسائط. حتى في الحالات التي يكون فيها تحديد حجم قائمة الوسائط ممكنًا بوسائل غير مباشرة (على سبيل المثال، عن طريق تحليل سلسلة التنسيق الخاصة بـ fprintf())، لا توجد طريقة قابلة للنقل لتمرير العدد المحدد ديناميكيًا من الوسائط إلى الاستدعاء الداخلي ذي عدد الوسائط المتغير، حيث يجب أن يكون عدد وحجم الوسائط التي يتم تمريرها إلى مثل هذه الاستدعاءات معروفين عمومًا في وقت الترجمة. إلى حد ما، يمكن تخفيف هذا القيد باستخدام وحدات الماكرو ذات عدد الوسائط المتغير بدلًا من الدوال ذات عدد الوسائط المتغير. بالإضافة إلى ذلك، توفر معظم إجراءات المكتبة القياسية vإصدارات بديلة مسبوقة بـ تقبل مرجعًا إلى قائمة الوسائط غير المسماة (أي متغيرًا مهيأً va_list) بدلًا من قائمة الوسائط غير المسماة نفسها. على سبيل المثال، vfprintf()يُعد إصدارًا بديلًا من fprintf()يتوقع قيمةً va_listبدلًا من قائمة الوسائط غير المسماة الفعلية. يمكن لدالة متغيرة الوسائط مُعرَّفة من قِبل المستخدم تهيئة va_listمتغير باستخدام va_start`variadic` وتمريره إلى دالة مناسبة من المكتبة القياسية، ما يعني فعليًا تمرير قائمة الوسائط غير المُسماة بالمرجع بدلًا من تمريرها بالقيمة. ولأنه لا توجد طريقة موثوقة لتمرير قوائم الوسائط غير المُسماة بالقيمة في لغة C، فإن توفير دوال واجهة برمجة تطبيقات متغيرة الوسائط دون توفير دوال مكافئة تقبل ` variadic` va_listيُعد ممارسة برمجة سيئة.
أمان النوع
تُوفّر بعض تطبيقات لغة C امتداداتٍ تسمح للمُصرّف بالتحقق من الاستخدام الصحيح لسلاسل التنسيق وعلامات الحفظ. وبدون هذه الامتدادات، لا يستطيع المُصرّف عادةً التحقق مما إذا كانت الوسائط غير المُسمّاة المُمرّرة من النوع الذي تتوقعه الدالة، أو تحويلها إلى النوع المطلوب. لذلك، يجب توخي الحذر لضمان صحة هذا الأمر، حيث ينتج سلوك غير مُعرّف إذا لم تتطابق الأنواع. على سبيل المثال، إذا كان النوع المتوقع هو int*، فيجب تمرير مؤشر فارغ كـ (int*)NULL. كتابة فقط NULLستؤدي إلى وسيط من النوع إما intأو void*، وكلاهما غير صحيح. هناك اعتبار آخر وهو ترقيات الوسائط الافتراضية المُطبّقة على الوسائط غير المُسمّاة. floatسيتم ترقية تلقائيًا إلى double. وبالمثل، intسيتم ترقية الوسائط من الأنواع الأضيق من إلى intأو unsigned int. يجب أن تتوقع الدالة التي تستقبل الوسائط غير المُسمّاة النوع المُرقّى.
يحتوي GCC على خاصية تتحقق من الوسائط المُمرَّرة : [ 4 ] تُحدد خاصية التنسيق أن الدالة تأخذ وسائط من نوع ` format` أو `style` ، والتي يجب التحقق من نوعها مقابل سلسلة التنسيق. على سبيل المثال، التصريح:[[gnu::format(style,format_index,first_to_check)]]printfscanfstrftimestrfmon
[[ gnu :: format ( printf , 2 , 3 )]] extern int my_printf ( void * obj , const char * fmt , ...);[ 5 ] يتسبب ذلك في قيام المترجم بفحص الوسائط في استدعاءات الدالة للتأكد my_printfمن اتساقها مع printfوسيطة سلسلة تنسيق النمط my_format.
مثال
#include <stdio.h> #include <stdarg.h>// حساب مجموع المتغيرات int sum ( int count , ...) { va_list ap ; int total = 0 ; va_start ( ap , count ); for ( int i = 0 ; i < count ; i ++ ) { total += va_arg ( ap , int ); } va_end ( ap ); return total ; }int main ( void ) { printf ( "%i " , sum ( 2 , 1 , 3 )); return 0 ; }يجب أن يُنتج هذا البرنامج المخرجات التالية:
4
<varargs.h>
حددت الإصدارات القديمة من معيار POSIX ملف رأس قديم <varargs.h>، يعود تاريخه إلى ما قبل توحيد لغة C، ويوفر وظائف مشابهة لـ <stdarg.h>. هذا الملف ليس جزءًا من معيار ISO C ولا من معيار POSIX. يحتوي الملف، كما هو محدد في الإصدار الثاني من مواصفات UNIX الموحدة ، على جميع وظائف C89 <stdarg.h>، باستثناء ما يلي:
- لا يمكن استخدامه في تعريفات نمط C الجديد القياسي
- يمكن حذف الوسيط المعطى (يتطلب معيار لغة C وسيطًا واحدًا على الأقل).
تختلف واجهة المستخدم أيضًا. على سبيل printargsالمثال، سيكتب المرء بدلاً من ذلك:
#include <stdio.h> #include <varargs.h>/** * هذه لغة C من K&R (قبل C89). * لا يوجد نوع "void"؛ استخدم قيمة إرجاع ضمنية من نوع int. */ printargs ( arg1 , va_alist ) va_dcl /* لا يوجد فاصلة منقوطة هنا! */ { va_list ap ; int i = 0 ;va_start ( ap ); /* يتم إعطاء قائمة va_list فقط! */ for ( i = arg1 ; i >= 0 ; i = va_arg ( ap , int )) { printf ( "%d " , i ); } va_end ( ap ); putchar ( '\n' ); return ; }ويُطلق عليه نفس الاسم.
varargs.hيتطلب الأمر تعريفات دوال من النمط القديم نظرًا لطريقة عمل التنفيذ. [ 6 ] في المقابل، لا يمكن دمج تعريفات الدوال من النمط القديم مع stdarg.h...
مراجع
- ↑ "IEEE Std 1003.1
stdarg.h" . تم الاطلاع عليه بتاريخ 2009-07-04 . - 1 2 جيلدينج، أليكس؛ مينيد، جانهيد (2022-04-15). "WG14-N2975 : تخفيف متطلبات قوائم المعلمات المتغيرة، الإصدار 3" (PDF) .
- ↑ "تعريفات الدوال القديمة" . لغة GNU C. 2007. تم الاطلاع عليه بتاريخ 15 نوفمبر 2024 .
- ↑ مايكل كيريسك (16 يناير 2026). "gnu::format(3attr - صفحة دليل لينكس" . man7.org . man7.
- ↑ "5.27 امتدادات لعائلة لغة C - تعريف سمات الدوال" . تم الاطلاع عليه بتاريخ 2009-01-03 .
- ↑ "مواصفات يونكس الفردية " . تم الاسترجاع في 2007-08-01 .
varargs.h
- رؤوس مكتبة C القياسية
