هو أفضل من استخدام C الفراغ الحجج "الفراغ فو(الفراغ)" أو ليس "الفراغ فو()"?[مكررة]

StackOverflow https://stackoverflow.com/questions/693788

  •  22-08-2019
  •  | 
  •  

سؤال

هذا السؤال سبق الجواب هنا:

ما هو الأفضل: void foo() أو void foo(void)?مع أنها باطلة تبدو قبيحة وغير متناسقة ، ولكن قيل لي أن هذا أمر جيد.هل هذا صحيح ؟

تحرير:أنا أعرف بعض المجمعين تفعل أشياء غريبة ، ولكن إذا أنا باستخدام فقط دول مجلس التعاون الخليجي ، void foo() حسنا ؟ سوف foo(bar); ثم تكون مقبولة ؟

هل كانت مفيدة؟

المحلول

void foo(void);

هذا هو الطريق الصحيح أن نقول "لا" المعلمات في ج كما يعمل في C++.

ولكن:

void foo();

يعني أشياء مختلفة في C و C++!في C يعني "يمكن أن تأخذ أي عدد من المعلمات من الأنواع الغير معروفة" ، في C++ يعني نفس foo(void).

متغير الحجة قائمة الوظائف بطبيعتها الأمم المتحدة typesafe وينبغي تجنبها قدر الإمكان.

نصائح أخرى

هناك طريقتان لتحديد المعلمات في C.واحد هو استخدام معرف قائمة أخرى باستخدام نوع المعلمة القائمة.معرف قائمة يمكن حذفها ، ولكن اكتب قائمة لا يمكن.لذا القول بأن وظيفة واحدة لا يأخذ الحجج في تعريف وظيفة يمكنك القيام بذلك مع (محذوفة) معرف قائمة

void f() {
    /* do something ... */
}

و هذا مع المعلمة نوع القائمة:

void f(void) {
    /* do something ... */
}

إذا كان في معلمة القائمة نوع واحد فقط نوع المعلمة هو باطل (يجب أن يكون أي اسم ثم) فهذا يعني أن الدالة لا يأخذ الحجج.ولكن هذين طرق تحديد وظيفة يكون هناك اختلاف بشأن ما يعلنون.

معرف قوائم

الأول يعرف أن وظيفة يأخذ عدد محدد من الحجج ، ولكن لا تعول ترسل ولا أنواع ما هو مطلوب - كما هو الحال مع كل وظيفة الإعلانات التي تستخدم معرف القوائم.لذا المتصل إلى معرفة أنواع العد بالضبط قبل اليد.حتى إذا كان المتصل باستدعاء الدالة يعطيها بعض الجدل ، سلوك غير معرف.كومة يمكن أن تصبح تالفة على سبيل المثال ، لأن يسمى تتوقع وظيفة تخطيط مختلف عندما مكاسب التحكم.

باستخدام معرف قوائم المعلمات وظيفة هو مستنكر.انه كان يستخدم في أيام قديمة و لا تزال موجودة في الكثير من رمز الإنتاج.أنها يمكن أن يتسبب في خطر بسبب تلك الحجة الترقيات (إذا كان الترويج حجة نوع لا تتطابق مع المعلمة نوع من تعريف الدالة, سلوك غير معرف سواء!) و هي أقل بكثير من آمن بالطبع.لذلك استخدم دائما void الشىء وظائف دون المعلمات في كل من فقط-الإعلانات التعاريف من الوظائف.

المعلمة القائمة نوع

والثاني يعرف أن وظيفة يأخذ صفر الحجج والتواصل أيضا أن مثل مع جميع الحالات التي تكون فيها الدالة أعلن باستخدام المعلمة القائمة نوع, وهو ما يسمى prototype.إذا كان الطالب باستدعاء الدالة ويعطيها بعض الجدل ، وهذا هو خطأ المحول البرمجي يبصق مناسب خطأ.

الطريقة الثانية من إعلان وظيفة لديها الكثير من الفوائد.واحد بالطبع هو أن كمية وأنواع المعلمات يتم التحقق.وثمة فرق آخر هو أن لأن المترجم يعرف أنواع المعلمة ، فإنه يمكن تطبيق الضمني التحويلات من الحجج إلى نوع من المعلمات.إذا أي نوع المعلمة القائمة الحالية التي لا يمكن القيام به, و الحجج يتم تحويلها إلى تعزيز أنواع (الذي يسمى الافتراضي الحجة تعزيز). char سوف تصبح int, على سبيل المثال ، في حين float سوف تصبح double.

النوع المركب على وظائف

بالمناسبة, إذا كان الملف يحتوي على كل حذف معرف قائمة المعلمة القائمة نوع, نوع المعلمة قائمة "انتصارات".نوع الوظيفة في نهاية يحتوي على النموذج:

void f();
void f(int a) {
    printf("%d", a);
}

// f has now a prototype. 

ذلك لأن كلا من الإعلانات لا أقول أي شيء متناقضة.الثانية ، ومع ذلك ، كان له رأي في ذلك.وهو أن حجة واحدة مقبولة.نفس يمكن القيام به في الاتجاه المعاكس

void f(a) 
  int a;
{ 
    printf("%d", a);
}

void f(int);

الأول يحدد وظيفة باستخدام معرف قائمة ، في حين أن الثانية ثم يقدم النموذج ، باستخدام إعلانا يتضمن نوع معلمة قائمة.

void foo(void) هو أفضل لأنه صراحة ويقول:لا المعايير المسموح بها.

void foo() يعني يمكن (تحت بعض المجمعين) إرسال المعلمات على الأقل إذا كان هذا هو إعلان وظيفة الخاص بك بدلا من تعريفها.

ونقلت C99

هذا الجواب يهدف إلى اقتباس وشرح الأجزاء ذات الصلة من C99 N1256 القياسية مشروع.

تعريف declarator

مصطلح declarator سوف يأتي الكثير, لذلك دعونا نفهم ذلك.

من قواعد اللغة ، نجد أن نؤكد الشخصيات declarators:

int f(int x, int y);
    ^^^^^^^^^^^^^^^

int f(int x, int y) { return x + y; }
    ^^^^^^^^^^^^^^^

int f();
    ^^^

int f(x, y) int x; int y; { return x + y; }
    ^^^^^^^

Declarators هي جزء من كل وظيفة الإعلانات التعاريف.

هناك 2 أنواع من declarators:

  • المعلمة القائمة نوع
  • معرف قائمة

المعلمة القائمة نوع

الإعلانات تبدو مثل:

int f(int x, int y);

تعريفات تبدو مثل:

int f(int x, int y) { return x + y; }

ويسمى نوع المعلمة قائمة لأننا يجب أن تعطي نوع من كل معلمة.

معرف قائمة

تعريفات تبدو مثل:

int f(x, y)
    int x;
    int y;
{ return x + y; }

الإعلانات تبدو مثل:

int g();

لا يمكننا أن تعلن وظيفة مع مجموعة غير فارغة معرف قائمة:

int g(x, y);

لأن 6.7.5.3 "وظيفة declarators (بما في ذلك النماذج)" يقول:

3 معرف قائمة في وظيفة declarator التي ليست جزءا من تعريف هذه الدالة يجب أن تكون فارغة.

ويسمى معرف قائمة لأننا تعطي إلا معرفات x و y على f(x, y), أنواع تأتي بعد.

هذا هو طريقة قديمة و لا ينبغي أن تستخدم بعد الآن. 6.11.6 وظيفة declarators يقول:

1 استخدام وظيفة declarators مع أقواس فارغة (غير أولي تنسيق نوع المعلمة declarators) هو الزائل الميزة.

و مقدمة يفسر ما هو الزائل ميزة:

بعض الميزات هي التي عفا عليها الزمن ، الأمر الذي يعني أنها يمكن النظر الانسحاب في المراجعات المستقبلية من هذا المعيار الدولي.يتم الاحتفاظ بها لأن من استخدامها على نطاق واسع ، ولكن استخدامها في تطبيقات جديدة (على التنفيذ الميزات) أو برامج جديدة (اللغة [6.11] أو ميزات المكتبة [7.26]) هو تثبيط

و() مباراة و(الفراغ) على الإعلانات

عندما كنت أكتب فقط:

void f();

هو بالضرورة معرف قائمة الإعلان ، لأن 6.7.5 "Declarators" يقول يحدد قواعد النحو التالي:

direct-declarator:
    [...]
    direct-declarator ( parameter-type-list )
    direct-declarator ( identifier-list_opt )

لذلك المعرف فقط-قائمة الإصدار يمكن أن تكون فارغة لأنه هو اختياري (_opt).

direct-declarator هو فقط النحوي العقدة التي تحدد قوسين (...) جزء من declarator.

لذلك كيف يمكننا إزالة الغموض واستخدام أفضل نوع المعلمة قائمة دون المعلمات ؟ 6.7.5.3 وظيفة declarators (بما في ذلك النماذج) يقول:

10 حالة خاصة لم يكشف عن اسمه المعلمة من نوع باطلة البند الوحيد في قائمة تحدد أن وظيفة لديها المعلمات.

لذلك:

void f(void);

هو الطريق.

هذا هو السحر جملة صراحة مسموح, وبما أننا لا يمكن استخدام void نوع الحجة في أي طريقة أخرى:

void f(void v);
void f(int i, void);
void f(void, int);

ماذا يمكن أن يحدث إذا كنت تستخدم f() الإعلان ؟

ربما سوف رمز تجميع ما يرام: 6.7.5.3 وظيفة declarators (بما في ذلك النماذج):

14 قائمة فارغة في وظيفة declarator التي ليست جزءا من تعريف هذه الدالة ينص على أن أي معلومات عن عدد أو أنواع من المعلمات يتم توفيره.

لذلك يمكنك الحصول على بعيدا مع:

void f();
void f(int x) {}

مرة أخرى, UB يمكن أن يرتفع (وإذا كنت محظوظا المترجم سوف اقول لكم), وسيكون لديك صعوبة في معرفة لماذا:

void f();
void f(float x) {}

انظر: لماذا فارغة إعلان العمل على التعاريف مع الباحث الحجج ولكن ليس من أجل تعويم الحجج ؟

و() و(الفراغ) عن التعاريف

f() {}

مقابل

f(void) {}

متشابهة ، ولكن ليست متطابقة.

6.7.5.3 وظيفة declarators (بما في ذلك النماذج) يقول:

14 قائمة فارغة في وظيفة declarator التي هي جزء من تعريف هذه الدالة لتحديد وظيفة لديها المعلمات.

والتي تبدو مشابهة إلى وصف f(void).

ولكن لا يزال... يبدو أن:

int f() { return 0; }
int main(void) { f(1); }

مطابقة معرف السلوك ، في حين:

int f(void) { return 0; }
int main(void) { f(1); }

هو غير المطابقة كما نوقشت في: لماذا دول مجلس التعاون الخليجي تسمح الوسيطات التي تم تمريرها إلى وظيفة محددة أن تكون مع الحجج ؟

TODO أفهم بالضبط لماذا.مع كونه من أولي أم لا.تعريف النموذج.

إلى جانب الخلافات النحوية ، وكثير من الناس يفضلون أيضا باستخدام void function(void) بالنسبة pracitical أسباب:

إذا كنت تستخدم وظيفة البحث و تريد العثور على تنفيذ وظيفة, يمكنك البحث عن function(void), ، وأنه سيعود النموذج وكذلك تنفيذ.

إذا حذفت الثانية void, عليك البحث عن function() ولذلك أيضا تجد جميع المكالمات وظيفة ، مما يجعل difficulter أن تجد التنفيذ الفعلي.

في C++, هناك لا الفرق في main() و main(void).

ولكن في C ، main() وسوف يطلق مع أي عدد من المعلمات.

على سبيل المثال:

main ( ){
    main(10,"abc",12.28);
    //Works fine !
    //It won't give the error. The code will compile successfully.
    //(May cause Segmentation fault when run)
}

main(void) سوف يطلق دون أية معلمات.لو كنا في محاولة لتمرير ثم هذا في نهاية المطاف يؤدي إلى خطأ برنامج التحويل البرمجي.

على سبيل المثال:

main (void) {
     main(10,"abc",12.13);
     //This throws "error: too many arguments to function ‘main’ "
}
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top