لماذا يحذر فيسوال C++ على الزهر الضمني من كونست فواد** لإلغاء*في C، ولكن ليس في C++؟




visual-studio visual-c++ (2)

ملخص

محول C / C ++ في ميكروسوفت فيسوال ستوديو يعطي تحذير C4090 عندما يحاول برنامج C لتحويل مؤشر إلى مؤشر إلى البيانات const (مثل const void ** أو const char ** ) إلى void * (على الرغم من أن هذا النوع ليس في الواقع مؤشر const ). حتى أكثر غرابة، نفس المترجم يقبل بصمت رمز متطابقة تجميعها C ++.

ما هو السبب في هذا التناقض، ولماذا فيسوال ستوديو (على عكس المجمعين الآخرين) لديهم مشكلة مع ضمنا تحويل مؤشر إلى مؤشر const في void * ؟

تفاصيل

لدي برنامج C حيث يتم قراءة C- سلاسل مرت في قائمة الوسيطة متغير في مصفوفة (بواسطة حلقة التي يتم استدعاء va_arg ). منذ C- سلاسل من نوع const char * ، الصفيف الذي يتتبع منهم هو من نوع const char ** . هذه المجموعة من المؤشرات إلى سلاسل مع محتوى const هي نفسها المخصصة ديناميكيا (مع calloc ) وأنا calloc قبل عودة الدالة (بعد أن تم معالجة C- سلاسل).

عندما قمت بتجميع هذا الرمز مع cl.exe (في ميكروسوفت فيسوال C ++)، حتى مع مستوى تحذير منخفض، أثارت الدعوة free تحذير C4090 . منذ free يأخذ void * ، وهذا قال لي أن المترجم لم أحب أن كنت قد تحولت const char ** إلى void * . أنا خلقت مثال بسيط لتأكيد هذا، الذي أحاول تحويل const void ** إلى void * :

/* cast.c - Can a const void** be cast implicitly to void* ? */

int main(void)
{
    const void **p = 0;
    void *q;
    q = p;

    return 0;
}

ثم جمعت ذلك على النحو التالي، مؤكدا أن هذا هو ما أثار التحذير:

>cl cast.c
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86
Copyright (C) Microsoft Corporation.  All rights reserved.

cast.c
cast.c(7) : warning C4090: '=' : different 'const' qualifiers
Microsoft (R) Incremental Linker Version 10.00.40219.01
Copyright (C) Microsoft Corporation.  All rights reserved.

/out:cast.exe
cast.obj

وثائق مايكروسوفت بشأن تحذير C4090 يقول:

يتم إصدار هذا التحذير لبرامج C. في برنامج C ++، المترجم يصدر خطأ: C2440.

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

أو حتى فكرت، حتى حاولت تجميع برنامج الاختبار الخاص بي كما C ++ (علم /TP يفعل ذلك):

>cl /TP cast.c
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86
Copyright (C) Microsoft Corporation.  All rights reserved.

cast.c
Microsoft (R) Incremental Linker Version 10.00.40219.01
Copyright (C) Microsoft Corporation.  All rights reserved.

/out:cast.exe
cast.obj

عندما يتم تجميع نفس التعليمات البرمجية ك C ++، لا يحدث خطأ أو تحذير. ومن المؤكد أنني أعيد بناؤها، وأخبر المترجم بالتحذير بأقصى قدر ممكن من القوة:

>cl /TP /Wall cast.c
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86
Copyright (C) Microsoft Corporation.  All rights reserved.

cast.c
Microsoft (R) Incremental Linker Version 10.00.40219.01
Copyright (C) Microsoft Corporation.  All rights reserved.

/out:cast.exe
cast.obj

إنها تنجح بصمت.

تلك التي تم إنشاؤها مع CL.exe ميكروسوفت فيسوال C ++ 2010 إكسبريس إديتيون على جهاز ويندوز 7 ولكن تحدث نفس الأخطاء على جهاز ويندوز زب في كل من فيسوال ستوديو .NET 2003 و cl.exe فيسوال C ++ 2005 إكسبريس إديتيون's cl.exe . لذلك يبدو هذا يحدث على كافة الإصدارات (على الرغم من أنني لم تختبر على كل نسخة ممكنة) وليس مشكلة مع الطريقة التي تم إعداد فيسوال ستوديو على الأجهزة الخاصة بي.

ويجمع نفس الكود دون مشكلة في دول مجلس التعاون الخليجي 4.6.1 على نظام أوبونتو 11.10 (الإصدار سلسلة gcc (Ubuntu/Linaro 4.6.1-9ubuntu3) 4.6.1 )، تم إعداده للتحذير بأقصى قدر ممكن، مثل C89، C99، و C ++:

$ gcc -ansi -pedantic -Wall -Wextra -o cast cast.c
cast.c: In function main’:
cast.c:6:11: warning: variable q set but not used [-Wunused-but-set-variable]

$ gcc -std=c99 -pedantic -Wall -Wextra -o cast cast.c
cast.c: In function main’:
cast.c:6:11: warning: variable q set but not used [-Wunused-but-set-variable]

$ g++ -x c++ -ansi -pedantic -Wall -Wextra -o cast cast.c
cast.c: In function int main()’:
cast.c:6:11: warning: variable q set but not used [-Wunused-but-set-variable]

وهو يحذر من أن q لا تقرأ من بعد تعيينها، ولكن هذا التحذير منطقي وغير ذات صلة.

بالإضافة إلى عدم إطلاق تحذير في دول مجلس التعاون الخليجي مع جميع التحذيرات تمكين، وعدم إطلاق تحذير في C ++ في كل من دول مجلس التعاون الخليجي أو مسفك، يبدو لي أن تحويل من مؤشر إلى مؤشر إلى كونست إلى void * لا ينبغي أن تعتبر مشكلة على الإطلاق، لأن في حين أن void * هو مؤشر إلى غير const ، مؤشر إلى مؤشر إلى كونست هو أيضا مؤشر إلى غير const .

في بلدي رمز العالم الحقيقي (وليس المثال)، وأنا يمكن أن يسكت هذا مع #pragma التوجيه، أو يلقي الصريح، أو عن طريق تجميع C ++ (هيه هيه)، أو يمكنني تجاهل ذلك فقط. ولكني أفضل أن لا تفعل أي من هذه الأشياء، على الأقل ليس قبل أن أفهم لماذا يحدث هذا. (ولماذا لا يحدث في C ++!)

واحد ممكن، تفسير جزئي يحدث لي: على عكس C ++، C يسمح الصب الضمني من void * إلى أي نوع مؤشر إلى البيانات. لذلك يمكن أن يكون مؤشر تم تحويلها ضمنا من const char ** إلى void * ، ومن ثم تحويلها ضمنا من void * إلى char ** ، مما يجعل من الممكن لتعديل البيانات الثابتة يشير إلى مؤشرات إلى، من دون المدلى بها. وهذا سيكون سيئا. ولكن لا أرى كيف أن هذا هو أي أسوأ من كل أنواع الأشياء الأخرى التي يسمح بها C نوع السلامة الأضعف.

أعتقد أن هذا التحذير منطقي نظرا لاختيار عدم تحذير عندما يتم تحويل نوع مؤشر غير void * إلى void * :

/* cast.c - Can a const void** be cast implicitly to void* ? */

int main(void)
{
    const void **p = 0;
    void *q;
    q = p;

    return 0;
}
>cl /Wall voidcast.c
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86
Copyright (C) Microsoft Corporation.  All rights reserved.

voidcast.c
Microsoft (R) Incremental Linker Version 10.00.40219.01
Copyright (C) Microsoft Corporation.  All rights reserved.

/out:voidcast.exe
voidcast.obj

ومع ذلك، إذا كان ذلك مقصودا، فإن:

  1. لماذا تشير مستندات ميكروسوفت إلى أن التعليمات البرمجية التي تنتج هذا التحذير في C ينتج خطأ في C ++؟

  2. إلى جانب تجاهل أو قمع التحذير، هل هناك أي بديل معقول، عندما يجب على المرء أن free مؤشر غير const إلى مؤشر غير const إلى البيانات const (كما في بلدي الوضع في العالم الحقيقي)؟ إذا حدث شيء من هذا القبيل في C ++، يمكن تخزين السلاسل التي تم تمريرها في قائمة الوسيطة المتغيرة في بعض حاويات ستل رفيعة المستوى بدلا من صفيف. لبرنامج C دون الوصول إلى C ++ ستل والتي لا تستخدم مجموعات عالية المستوى خلاف ذلك، وهذا النوع من الشيء ليس خيارا معقولا.

  3. ويعمل بعض المبرمجين في إطار سياسة مؤسسية / تنظيمية لمعالجة التحذيرات كأخطاء. يتم تمكين C4090 حتى مع /W1 . يجب أن يكون الأشخاص قد واجهوا ذلك من قبل. ماذا يفعل هؤلاء المبرمجين؟


على ما يبدو هذا هو مجرد خطأ في فك ++.

إذا كنت تعلن const char **x; والنتيجة هي مؤشر لمؤشر "للقراءة فقط" إلى حرف، وانها ليست في حد ذاتها مؤشر "للقراءة فقط" (يمكنني استخدام مصطلح "للقراءة فقط" لأن مصطلح const -ness يدفع مفهوم خاطئ أن الحرف يجري وأشار إلى ثابت في حين أن هذا كاذبة بشكل عام ... const مع المراجع والمؤشرات هي خاصية مرجع أو مؤشر ويخبر شيئا عن ثبات البيانات وأشار إلى أو المشار إليها).

أي مؤشر القراءة / الكتابة يمكن تحويلها إلى void * و فك ++ لا يوجد لديه سبب حقيقي لإصدار تحذير عند تجميع هذا الرمز، لا في C أو في وضع C++ .

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


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

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

نقاط إضافية للإبلاغ أيضا عن الخطأ لمورد المترجم.

أما بالنسبة لل 1. يبدو أنه يشير إلى ضم ضمنا const T * إلى void * ، والتي ينبغي أن تكون تحذيرا في C وخطأ في C ++.







const