"نوع المؤشر غير المتوافق" تحذير المترجم للحجة الرابعة من QSort

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

سؤال

أحاول استخدام المكتبة القياسية qsort لفرز مجموعة من الشخصيات الواسعة:

wchar_t a = L'a';
wchar_t a1 = L'ä';
wchar_t b = L'z';
wchar_t chararray[] = {b, a, a1};  
length = wcslen(chararray);

qsort(chararray, length, sizeof(wchar_t), wcscoll);

الآن أعتقد أن الوظائف المعنية لها هذه النماذج الأولية:

int wcscoll(const wchar_t *ws1, const wchar_t *ws2);
void qsort(void *base, size_t num, size_t size, int (*comp_func)(const void *, const void *))

النتائج هي تمامًا كما هو متوقع ، لكن لماذا أحصل على تحذير المترجم "passing argument 4 of ‘qsort’ from incompatible pointer type"؟ وكيف يمكنني أن ألقي wcscoll لتناسب النموذج الأولي؟

يختفي التحذير إذا حددت وأمر في وظيفة مقارنة منفصلة:

int widecharcomp(const void *arg1, const void *arg2)
{
    return wcscoll(arg1, arg2);
}

... لكن هذا يبدو أنه يجب أن يكون له معالجة خطأ عندما لا تكون الوسائط من النوع wchar_t *.

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

المحلول

هناك مشكلتان: لقد قمت بخلط wchar_t و wchar_t*, ، وحاولت أن تنقل أ wchar_t* ك void*.

أولا ، لقد أخبرت qsort لفرز مجموعة من wchar_t. ولكن wcscoll لا يقارن wchar_t, ، يقارن سلاسل الأحرف الواسعة التي لها النوع wchar_t*. حقيقة أن المقارنة التي نجحت تنجح ترجع إلى بيانات الاختبار التي تحدث بشكل جيد في كلا التفسيرين.

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

علاوة على ذلك ، حتى لو كان لديك مجموعة من wchar_t*, ، لا يمكنك أن تمر بشكل محول wcscoll كحجة ل qsort. القضية هي أنه لا يوجد ضمان wchar_t* و void* لديهم نفس التمثيل. بعض الآلات لها مؤشرات الكلمات التي لديها تمثيل مختلف من مؤشرات البايت ؛ على مثل هذه الآلة ، qsort من شأنه أن يمر مؤشرات بايت إلى عناصر الصفيف إلى wcscoll, وهذا لن يعمل بسبب wcscoll يتوقع مؤشرات بايت. الحل هو كتابة دالة غلاف تافهة تؤدي التحويل إذا لزم الأمر. غالبًا ما يكون الغلاف التافهة ضروريًا مع qsort.

نصائح أخرى

لقد فعلت ذلك إلى حد كبير بالطريقة الصحيحة. وثائق دول مجلس التعاون الخليجي ل strcoll و wcscoll يعطي مثالًا مشابهًا لهذا كطريقة صحيحة للاستخدام strcoll أو wcscoll مع qsort.

 /* This is the comparison function used with qsort. */

 int
 compare_elements (char **p1, char **p2)
 {
   return strcoll (*p1, *p2);
 }

 /* This is the entry point---the function to sort
    strings using the locale's collating sequence. */

 void
 sort_strings (char **array, int nstrings)
 {
   /* Sort temp_array by comparing the strings. */
   qsort (array, nstrings,
          sizeof (char *), compare_elements);
 }

هذا المثال في الواقع يرفع التحذير الذي تريد التخلص منه ، ولكن مرة أخرى يمكن الحصول عليها من خلال تغيير char** إلى const void* في الحجج إلى compare_elements, ثم الصريح الصريح ل const char**.

أنت على صواب في ملاحظة أن هذا النوع غير آمن ، لكن السلامة من النوع ليست واحدة من النقاط القوية لـ C. لا يحتوي C على أي شيء مثل الأدوية أو القوالب ، وبالتالي فإن الطريقة الوحيدة التي يمكن أن تعمل QSort على نوع تعسفي هي وظيفة المقارنة لقبولها void*س. الأمر متروك للمبرمج للتأكد من عدم استخدام وظيفة المقارنة في سياق قد يتم تمريره للوسائط التي ليست من النوع المتوقع.

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

return wcscoll(arg1, arg2);

أنت تمر بالفعل wscoll أ wchar_t** عندما يتوقع wchar_t*. الطريقة الصحيحة للقيام بذلك ، مع قمع التحذير ، ستكون:

int widecharcomp(const void *arg1, const void *arg2)
{
    return wcscoll(*(const w_char_t**)arg1, *(const w_char_t**)arg2);
}

كما هو قبيح.

يحرر:

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

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

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

qsort(chararray, length, sizeof(wchar_t), (int(*)(const void*,const void*))wcscoll);

أو اجعلها أكثر قابلية للقراءة باستخدام typedef لنوع وظيفة المقارنة:

typedef
int (*comp_func_t)(const void *, const void *);

/* ... */
qsort(chararray, length, sizeof(wchar_t), (comp_func_t) wcscoll);

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


يحرر:

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

int wchar_t_coll( const void* p1, const void* p2)
{
    wchar_t s1[2] = {0};
    wchar_t s2[2] = {0};

    s1[0] = * (wchar_t*)p1;
    s2[0] = * (wchar_t*)p2;

    return wcscoll( s1, s2);
}

لاحظ أيضًا أن chararray أنت تمر إلى wcslen() لم يتم إنهاءها بشكل صحيح - ستحتاج إلى ملف 0 في نهاية المهيئة:

wchar_t chararray[] = {b, a, a1, 0};  

لا يمكنك إلقاء مؤشر وظيفة إلى نوع مختلف ، فإن الحل الحالي الخاص بك جيدًا

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