سؤال

هناك الإعلانات التالية:

void qsort(void *lineptr[], int left, int right, int (*comp)(void *, void *));
int numcmp(char *, char *);
int strcmp(char *s, char *t);

ثم، في مكان ما في البرنامج هناك المكالمة التالية:

  qsort((void**) lineptr, 0, nlines-1, 
                    (int (*)(void*,void*))(numeric ? numcmp : strcmp));

(تجاهل الحجج الثلاثة الأولى و numeric).

أسأل ما هذا:

(int (*)(void*,void*))(numeric ? numcmp : strcmp)

انا افهم ذلك qsort يتوقع "مؤشر العمل الذي يحصل على اثنين void مؤشرات وإرجاع int"نظرا لأن الحجة الرابعة، فماذا ما هو مكتوب أعلاه يرضي ذلك؟ يبدو لي مثل نوع من التشكلين لأنه مصنوع من قوسين، لكن ذلك سيكون من الواضح غريبا للغاية. لأنه يأخذ وظيفة ويجعل هذه الوظيفة" مؤشر للعمل الذي يحصل على اثنين void مؤشرات وإرجاع int". وهذا لا معنى له.
(تابعت هنا القاعدة التي type في قوسين قبل أن يعزز المتغير المتغير إلى هذا النوع).

لذلك أعتقد أنني مجرد خطأ، ربما شخص ما يمكن أن يقول لي كيف تقرأ هذا، ما هو النظام؟

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

المحلول

ما يحدث هنا هو بالفعل ممثلين. يتيح تجاهل Ternary لمدة ثانية وتظاهر بأن Nummmp يستخدم دائما. لغرض هذا السؤال، يمكن أن تعمل الوظائف بموجب مؤشرات الوظائف في C. إذا نظرت إلى نوع الرقمي هو في الواقع

(int (*)(int*,int*))

لكي يتم استخدام ذلك بشكل صحيح في QSort، يجب أن يكون لديك معلمات الفراغ. نظرا لأن جميع الأنواع هنا تحتوي جميعها بنفس الحجم فيما يتعلق بالمعلمات وأنواع الإرجاع، فمن الممكن استبدالها من أجل الآخر. كل ما هو مطلوب هو يلقي لجعل المترجم سعيدا.

(int (*)(void*,void*))(numcmp )

نصائح أخرى

لقد فاتتك الخدعة هنا - الجزء

(numeric ? numcmp : strcmp)

يستخدم المشغل Ternary للاختيار أي يتم استدعاء الوظيفة داخل Qsort. إذا كانت البيانات رقمية، فإنها تستخدم Numcmp. إذا لم يكن كذلك، فإنه يستخدم strcmp. سيكون تنفيذ أكثر قابلية للقراءة مثل هذا:

int (*comparison_function)(void*,void*) = 
    (int (*)(void*,void*))(numeric ? numcmp : strcmp);
qsort((void**) lineptr, 0, nlines-1, comparison_function);

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

لاحظ أن التعريف القياسي لل qsort() يشمل const:

void qsort(void *base, size_t nmemb, size_t size,
           int (*compar)(const void *, const void *));

لاحظ أن المقارنة سلسلة يتم منح اثنين "char **"القيم، وليس"char *' القيم.

أكتب المقارنات الخاصة بي بحيث تكون المقصورات غير ضرورية في رمز الاتصال:

#include <stdlib.h>    /* qsort() */
#include <string.h>    /* strcmp() */

int num_cmp(const void *v1, const void *v2)
{
    int i1 = *(const int *)v1;
    int i2 = *(const int *)v2;
    if (i1 < i2)
        return -1;
    else if (i1 > i2)
        return +1;
    else
        return 0;
}

int str_cmp(const void *v1, const void *v2)
{
    const char *s1 = *(const char **)v1;
    const char *s2 = *(const char **)v2;
    return(strcmp(s1, s2));
}

إجبار الأشخاص على كتابة المصابيح في التعليمات البرمجية باستخدام وظائفك قبيحة. لا.

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

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

result = (*pointer_to_function)(arg1, arg2, ...);

في النمط الحديث، هو مكتوب:

result = pointer_to_function(arg1, arg2, ...);

شخصيا، أجد ديرليل واضحا أكثر وضوحا، لكن ليس الجميع يوافق الجميع.

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

تذكر القول المأثور من براين كيرنغان:

تصحيح الأخطاء هو ضعف ما يكتب الكود في المقام الأول. لذلك، إذا كتبت الكود بأنها بذكاء قدر الإمكان، فأنت، بحكم التعريف، وليس ذكيا بما يكفي لتصحيحه.


أقوم بالكثير من الترميز الحرج الأدائي مع المواعيد النهائية الصعبة في الوقت الحقيقي ... وما زلت لم أر مكانا فيه بطانة واحدة كثيفة مناسبة.

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

كما أشار آخرون، ل

(int (*)(void*,void*))(numeric ? numcmp : strcmp)

ثم ما يلي هو نوع من النوع

(int (*)(void*,void*))

والتعبير هو

(numeric ? numcmp : strcmp)

يمكن أن تكون تصريحات C صعبة للغاية، ولكن من الممكن أن تتعلم. تتمثل الطريقة في البدء في الجزء الداخلي ثم انتقل إلى خطوة واحدة واحدة، ثم تركت خطوة واحدة، مستمرة في اليمين، اليسار، اليمين، اليسار، إلخ إلى الخارج حتى الانتهاء. أنت لا تعبر خارج قوسين قبل أن يتم تقييم كل شيء في الداخل. على سبيل المثال بالنسبة للنوع الممثل أعلاه، (*) يشير إلى أن هذا مؤشر. كان المؤشر هو الشيء الوحيد داخل الأقواس، ثم نقيم إلى الجانب الأيمن خارجها. (void*,void*) يشير إلى أن هذا مؤشر له وظيفة مع حجج مؤشرين. أخيرا int يشير إلى نوع المرتجلة من الوظيفة. القوس الخارجي يجعل هذا النوع من النوع يلقي. تحديث: مقالة أكثر تفصيلتين: في اتجاه عقارب الساعة / دوامة و قراءة C التصريحات: دليل للمحيط.

ومع ذلك، فإن الأخبار الجيدة هي أنه على الرغم من أن ما سبق مفيد للغاية لمعرفة ذلك هناك طريقة بسيطة للغاية للغش: ال CDECL. يمكن أن يتحول البرنامج من C إلى الإنجليزية إلى الوصف والعكس صحيح:

cdecl> explain (int (*)(void*,void*))
cast unknown_name into pointer to function (pointer to void, pointer to void) returning int
cdecl> declare my_var as array 5 of pointer to int
int *my_var[5]
cdecl>

ممارسة: أي نوع من المتغير هو i?

int *(*(*i)[])(int *)

الإجابة في rot13. في حال لم يكن لديك CDECL مثبتا على جهازك (ولكن يجب عليك حقا!):

pqrpy> rkcynva vag *(*(*v)[])(vag *)
qrpyner v nf cbvagre gb neenl bs cbvagre gb shapgvba (cbvagre gb vag) ergheavat cbvagre gb vag
pqrpy>

ربما قرأت ذلك مثل هذا:

typedef int (*PFNCMP)(void *, void *);

PFNCMP comparison_function;

if (numeric)
{
    comparison_function =  numcmp;
}
else
{
    comparison_function = strcmp;
}

qsort((void**) lineptr, 0, nlines-1, comparison_function);

المثال في السؤال له حالة صريحة.

منطقك صحيح على ما أعتقد. إنه في الواقع صب "مؤشر للعمل الذي يحصل على مؤشرين الفراغين وإرجاع int" وهو النوع المطلوب من خلال توقيع الطريقة.

كلاهما numcmp و strcmp هي مؤشرات للوظائف التي تأخذ اثنين char* كما المعلمات وإرجاع int. وبعد ال qsort روتين يتوقع مؤشر وظيفة يأخذ اثنين void* كما المعلمات وإرجاع int. وبعد وبالتالي يلقي. هذا آمن، منذ void* بمثابة مؤشر عام. الآن، في قراءة الإعلان: دعنا نأخذ لك strcmpإعلان إعلان:

 int strcmp(char *, char *);

مترجم يقرأ ذلك strcmp في الواقع:

 int (strcmp)(char *, char *)

وظيفة (تتحلل إلى مؤشر إلى وظيفة في معظم الحالات) التي تأخذ اثنين char * الحجج. نوع المؤشر strcmp لذلك:

 int (*)(char *, char *)

وبالتالي، عندما تحتاج إلى وضع وظيفة أخرى لتكون متوافقة مع strcmp كنت تستخدم أعلاه كما اكتب لإلحاق ل.

وبالمثل، منذ ذلك الحين qsortحجة المقارنة تأخذ اثنين void *S وهكذا يلقي الغريب!

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