لماذا لا يمكنني تحويل "char**" إلى "const char* const*" في لغة C؟

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

سؤال

يعطي مقتطف التعليمات البرمجية التالي (بشكل صحيح) تحذيرًا في لغة C وخطأ في لغة C++ (باستخدام gcc وg++ على التوالي، وتم اختباره مع الإصدارين 3.4.5 و4.2.1؛يبدو أن MSVC لا يهتم):

char **a;
const char** b = a;

أستطيع أن أفهم وأقبل هذا.
الحل C++ لهذه المشكلة هو تغيير b ليكون const char * const *، مما لا يسمح بإعادة تعيين المؤشرات ويمنعك من التحايل على const-correctness (الأسئلة الشائعة حول لغة C++).

char **a;
const char* const* b = a;

ومع ذلك، في C النقي، لا يزال الإصدار المصحح (باستخدام const char * const *) يعطي تحذيرًا، ولا أفهم السبب.هل هناك طريقة للالتفاف حول هذا دون استخدام الجبيرة؟

للتوضيح:
1) لماذا يؤدي هذا إلى إنشاء تحذير في لغة C؟يجب أن يكون آمنًا تمامًا، ويبدو أن مترجم C++ يتعرف عليه على هذا النحو.
2) ما هي الطريقة الصحيحة لقبول هذا char** كمعلمة أثناء القول (وفرض المترجم) أنني لن أقوم بتعديل الأحرف التي يشير إليها؟على سبيل المثال، إذا أردت كتابة دالة:

void f(const char* const* in) {
  // Only reads the data from in, does not write to it
}

وأردت استدعائه على char**، ما هو النوع الصحيح للمعلمة؟

يحرر:شكرًا لمن أجابوا، وخاصة أولئك الذين تناولوا السؤال و/أو تابعوا إجاباتي.

لقد قبلت الإجابة بأن ما أريد القيام به لا يمكن تحقيقه بدون طاقم الممثلين، بغض النظر عما إذا كان ذلك ممكنًا أم لا.

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

المحلول

لقد واجهت نفس المشكلة منذ بضع سنوات وأزعجتني بلا نهاية.

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

في النهاية، إنها مجرد مشكلة في معيار C.آمل أن يتناول المعيار (أو التقرير الفني) التالي هذا الأمر.

نصائح أخرى

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

char **a;
const char* const* b = a;

لكن هذا لن:

const char **a;
const char* const* b = a;

وبدلاً من ذلك، يمكنك إلقاءها:

char **a;
const char* const* b = (const char **)a;

ستحتاج إلى نفس طاقم الممثلين لاستدعاء الدالة f() كما ذكرت.بقدر ما أعرف، لا توجد طريقة لإجراء تحويل ضمني في هذه الحالة (باستثناء C++).

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

لقد حددت المشكلة بالفعل -- هذا الرمز ليس صحيحًا بشكل ثابت."Const right" يعني أنه، باستثناء const_cast وC-style casts التي تزيل const، لا يمكنك أبدًا تعديل كائن const من خلال مؤشرات أو مراجع const.

قيمة const-correctness — const موجودة، إلى حد كبير، في اكتشاف أخطاء المبرمجين.إذا أعلنت شيئًا ما على أنه const، فأنت تشير إلى أنك لا تعتقد أنه يجب تعديله - أو على الأقل، يجب ألا يتمكن أولئك الذين لديهم حق الوصول إلى إصدار const فقط من تعديله.يعتبر:

void foo(const int*);

كما هو معلن، foo ليس لديه إذن لتعديل العدد الصحيح الذي تشير إليه الوسيطة الخاصة به.

إذا لم تكن متأكدًا من سبب عدم صحة الكود الذي نشرته، ففكر في الكود التالي، الذي يختلف قليلاً عن كود HappyDude:

char *y;

char **a = &y; // a points to y
const char **b = a; // now b also points to y

// const protection has been violated, because:

const char x = 42; // x must never be modified
*b = &x; // the type of *b is const char *, so set it 
         //     with &x which is const char* ..
         //     ..  so y is set to &x... oops;
*y = 43; // y == &x... so attempting to modify const 
         //     variable.  oops!  undefined behavior!
cout << x << endl;

لا يمكن للأنواع غير الثابتة أن تتحول إلا إلى أنواع ثابتة بطرق معينة لمنع أي تحايل على "const" في نوع البيانات بدون تحويل صريح.

الكائنات التي تم الإعلان عنها في البداية const هي كائنات خاصة بشكل خاص - يمكن للمترجم أن يفترض أنها لا تتغير أبدًا.ومع ذلك، إذا كان من الممكن تعيين قيمة "a" لـ "b" بدون تحويل، فقد تحاول عن غير قصد تعديل متغير const.هذا لن يؤدي فقط إلى كسر الفحص الذي طلبت من المترجم إجراؤه، لمنعك من تغيير قيمة المتغيرات - بل سيسمح لك أيضًا بكسر تحسينات المترجم!

في بعض المترجمين، سيطبع هذا الرقم '42'، وفي بعض المترجمين '43'، وفي البعض الآخر، سيتعطل البرنامج.

تعديل-إضافة:

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

تعديل الحذف: تمت إزالة الخطأ المطبعي

هذا أمر مزعج، ولكن إذا كنت ترغب في إضافة مستوى آخر من إعادة التوجيه، فيمكنك غالبًا القيام بما يلي للضغط لأسفل إلى مؤشر إلى مؤشر:

char c = 'c';
char *p = &c;
char **a = &p;

const char *bi = *a;
const char * const * b = &bi;

لها معنى مختلف قليلًا، لكنها عادة ما تكون عملية، ولا تستخدم قالبًا.

لا أستطيع الحصول على خطأ عند إرسال char** ضمنيًا إلى const char * const *، على الأقل في MSVC 14 (VS2k5) وg++ 3.3.3.يُصدر إصدار مجلس التعاون الخليجي 3.3.3 تحذيرًا، ولست متأكدًا تمامًا من صحته.

اختبار.ج:

#include <stdlib.h> 
#include <stdio.h>
void foo(const char * const * bar)
{
    printf("bar %s null\n", bar ? "is not" : "is");
}

int main(int argc, char **argv) 
{
    char **x = NULL; 
    const char* const*y = x;
    foo(x);
    foo(y);
    return 0; 
}

الإخراج مع الترجمة كرمز C:cl /TC /W4 /Wp64 test.c

test.c(8) : warning C4100: 'argv' : unreferenced formal parameter
test.c(8) : warning C4100: 'argc' : unreferenced formal parameter

الإخراج مع الترجمة كرمز C++:cl /TP /W4 /Wp64 test.c

test.c(8) : warning C4100: 'argv' : unreferenced formal parameter
test.c(8) : warning C4100: 'argc' : unreferenced formal parameter

الإخراج مع دول مجلس التعاون الخليجي:دول مجلس التعاون الخليجي-اختبار الجدار.ج

test2.c: In function `main':
test2.c:11: warning: initialization from incompatible pointer type
test2.c:12: warning: passing arg 1 of `foo' from incompatible pointer type

الإخراج مع g++:g++ -اختبار الجدار.C

لا يوجد مخرج

أنا متأكد تمامًا من أن الكلمة الأساسية const لا تعني أن البيانات لا يمكن تغييرها/أنها ثابتة، فقط أنه سيتم التعامل مع البيانات على أنها للقراءة فقط.النظر في هذا:

const volatile int *const serial_port = SERIAL_PORT;

وهو رمز صالح.كيف يمكن أن تتعايش المتقلبة والثابتة؟بسيط.يخبر volatile المترجم أن يقرأ الذاكرة دائمًا عند استخدام البيانات ويخبر const المترجم بإنشاء خطأ عند محاولة الكتابة إلى الذاكرة باستخدام مؤشر serial_port.

هل يساعد const في تحسين المترجم؟لا.مُطْلَقاً.نظرًا لأنه يمكن إضافة الثبات إلى البيانات وإزالتها منها من خلال الإرسال، لا يستطيع المترجم معرفة ما إذا كانت البيانات الثابتة ثابتة بالفعل (نظرًا لأنه يمكن إجراء التحويل في وحدة ترجمة مختلفة).في C++ لديك أيضًا الكلمة الأساسية القابلة للتغيير لزيادة تعقيد الأمور.

char *const p = (char *) 0xb000;
//error: p = (char *) 0xc000;
char **q = (char **)&p;
*q = (char *)0xc000; // p is now 0xc000

ما يحدث عند محاولة الكتابة إلى الذاكرة التي تكون للقراءة فقط (ROM، على سبيل المثال) ربما لم يتم تعريفه في المعيار على الإطلاق.

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