سؤال

ما هو المفيد في هذا C بناء الجملة - استخدام إعلانات دالة النمط "K&R"؟

int func (p, p2)
    void* p;
    int  p2;
{
    return 0;
}

لقد تمكنت من كتابة هذا في Visual Studios 2010beta

// yes, the arguments are flipped
void f()
{
    void* v = 0;
    func(5, v);
}

لا أفهم.ما هو الهدف من بناء الجملة هذا؟أستطيع أن أكتب:

int func (p, p2)
    int  p2;
{
    return 0;
}
// and write
int func (p, p2)
{
    return 0;
}

الشيء الوحيد الذي يبدو أنه يحدده هو عدد المعلمات التي يستخدمها ونوع الإرجاع.أعتقد أن المعلمات بدون أنواع أمر رائع نوعًا ما، ولكن لماذا تسمح بذلك و int paranName بعد معلن الدالة؟هذا غريب.

هل هذا أيضًا لا يزال قياسيًا C؟

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

المحلول

السؤال الذي تطرحه هو في الواقع سؤالان، وليس سؤالًا واحدًا.حاولت معظم الردود حتى الآن تغطية الأمر برمته بإجابة عامة "هذا هو أسلوب K&R"، في حين أن جزءًا صغيرًا فقط منه في الواقع له علاقة بما يُعرف بأسلوب K&R (ما لم تشاهد لغة C بالكامل كـ "نمط K&R" بطريقة أو بأخرى :)

الجزء الأول هو بناء الجملة الغريب المستخدم في الوظيفة تعريف

int func(p, p2)
void *p;
int  p2; /* <- optional in C89/90, but not in C99 */
{
  return 0;
}

هذا هو في الواقع تعريف دالة على نمط K&R.لقد غطت الإجابات الأخرى هذا بشكل جيد.وليس هناك الكثير لذلك، في الواقع.تم إهمال بناء الجملة، ولكنه لا يزال مدعومًا بالكامل حتى في C99 (باستثناء قاعدة "no implicit int" في C99، مما يعني أنه في C99 لا يمكنك حذف إعلان p2).

الجزء الثاني ليس له علاقة بأسلوب K&R.أشير إلى حقيقة أنه يمكن استدعاء الوظيفة باستخدام الوسائط "المتبادلة"، أي.لا يتم إجراء أي فحص لنوع المعلمة في مثل هذه المكالمة.هذا لا علاقة له بتعريف نمط K&R في حد ذاته، ولكنه يتعلق بكل شيء بوظيفتك التي لا تحتوي على نموذج أولي.كما ترى، في لغة C عندما تعلن عن وظيفة مثل هذه

int foo();

في الواقع تعلن وظيفة foo أن يأخذ عدد غير محدد من المعلمات من نوع غير معروف.يمكنك تسميتها كذلك

foo(2, 3);

و كما

j = foo(p, -3, "hello world");

الجواب هلم جرا (لقد فهمت الفكرة) ؛

لن "يعمل" إلا الاستدعاء الذي يحتوي على الوسائط المناسبة (بمعنى أن الآخرين ينتجون سلوكًا غير محدد)، ولكن الأمر متروك لك تمامًا للتأكد من صحته.لا يُطلب من المترجم تشخيص المعلمات غير الصحيحة حتى لو كان يعرف بطريقة سحرية أنواع المعلمات الصحيحة وعددها الإجمالي.

في الواقع، هذا السلوك هو ميزة من لغة C.أمر خطير، ولكن مع ذلك ميزة.انها تسمح لك أن تفعل شيئا من هذا القبيل

void foo(int i);
void bar(char *a, double b);
void baz(void);

int main()
{
  void (*fn[])() = { foo, bar, baz };
  fn[0](5);
  fn[1]("abc", 1.0);
  fn[2]();
}

أي.مزج أنواع الوظائف المختلفة في مصفوفة "متعددة الأشكال" دون أي طباعة (رغم أنه لا يمكن استخدام أنواع الوظائف المتغيرة هنا).مرة أخرى، المخاطر الكامنة في هذه التقنية واضحة تمامًا (لا أتذكر أنني استخدمتها على الإطلاق، لكن يمكنني أن أتخيل أين يمكن أن تكون مفيدة)، ولكن هذه هي لغة C بعد كل شيء.

وأخيرًا، الجزء الذي يربط الجزء الثاني من الإجابة بالجزء الأول.عندما تقوم بإنشاء تعريف دالة على نمط K&R، فإنه لا يقدم نموذجًا أوليًا للوظيفة.بقدر ما يتعلق الأمر بنوع الوظيفة، الخاص بك func يعلن التعريف func مثل

int func();

أي.لم يتم الإعلان عن الأنواع ولا العدد الإجمالي للمعلمات.تقول في مشاركتك الأصلية "...يبدو أنه يحدد عدد المعلمات التي يستخدمها ...".من الناحية الرسمية، لا!بعد نمط K&R ذو المعلمتين func تعريف لا يزال بإمكانك الاتصال به func مثل

func(1, 2, 3, 4, "Hi!");

ولن يكون هناك أي انتهاك للقيد في ذلك.(عادةً، سيعطيك مترجم الجودة تحذيرًا).

أيضا، هناك حقيقة يتم التغاضي عنها في بعض الأحيان وهي أن

int f()
{
  return 0;
}

هو أيضًا تعريف دالة على نمط K&R ولا يقدم نموذجًا أوليًا.لجعلها "حديثة" عليك أن تضع صيغة صريحة void في قائمة المعلمات

int f(void)
{
  return 0;
}

أخيرًا، خلافًا للاعتقاد الشائع، يتم دعم كل من تعريفات الوظائف على نمط K&R وإعلانات الوظائف غير النموذجية بشكل كامل في C99.لقد تم إهمال الأول منذ C89/90، إذا كنت أتذكر بشكل صحيح.يتطلب C99 الإعلان عن الوظيفة قبل الاستخدام الأول، ولكن ليس من الضروري أن يكون الإعلان نموذجًا أوليًا.يبدو أن الارتباك ينبع من الخلط المصطلحي الشائع:كثير من الناس يطلقون على أي إعلان دالة اسم "نموذج أولي"، في حين أن "إعلان الوظيفة" في الواقع ليس هو نفس "النموذج الأولي".

نصائح أخرى

هذا بناء جملة K&R C قديم جدًا (يسبق تاريخ ANSI/ISO C).في الوقت الحاضر، يجب ألا تستخدمه بعد الآن (كما لاحظت بالفعل عيوبه الرئيسية:لن يقوم المترجم بالتحقق من أنواع الوسائط لك).نوع الوسيطة افتراضيًا بالفعل int في المثال الخاص بك.

في ذلك الوقت، تم استخدام بناء الجملة هذا، وقد يجد المرء أحيانًا وظائف مثل

foo(p, q) 
{
    return q + p;
}

والذي كان في الواقع تعريفًا صالحًا، مثل أنواع foo, p, ، و q الافتراضي ل int.

هذا ببساطة بناء جملة قديم، يسبق بناء جملة "ANSI C" الذي قد تكون أكثر دراية به.تسمى "كيه آند آر سي"، عادة.

يدعمها المترجمون لتكون كاملة، ولتكون قادرة على التعامل مع قواعد التعليمات البرمجية القديمة، بطبيعة الحال.

هذا هو بناء جملة K&R الأصلي قبل توحيد لغة C في عام 1989.قدم C89 نماذج أولية للوظائف، مستعارة من C++، وأهمل بناء جملة K&R.لا يوجد سبب لاستخدامه (والعديد من الأسباب لعدم استخدامه) في الكود الجديد.

هذه بقايا عندما لم يكن لدى لغة C نماذج أولية للوظائف.في ذلك الوقت، (على ما أعتقد) كان من المفترض أن تعود الوظائف int وجميع حججها يفترض أن تكون int.لم يتم إجراء أي فحص على معلمات الوظيفة.

من الأفضل لك استخدام النماذج الأولية للوظائف في لغة C الحالية.
وأنت يجب استخدمها في C99 (لا يزال C89 يقبل بناء الجملة القديم).

ويتطلب C99 الإعلان عن الوظائف (ربما بدون نموذج أولي).إذا كنت تكتب دالة جديدة من الصفر، فستحتاج إلى تقديم إعلان ...اجعله نموذجًا أوليًا أيضًا:لن تخسر شيئًا وستحصل على فحص إضافي من المترجم.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top