سؤال

هذا السؤال يخرج C معلمو هناك:

في ج ، فمن الممكن أن يعلن مؤشر على النحو التالي:

char (* p)[10];

..والتي في الأساس الدول أن هذا المؤشر يشير إلى مجموعة من 10 أحرف.الشيء أنيق حول إعلان مؤشر مثل هذه هي التي سوف تحصل على وقت التحويل البرمجي خطأ عند محاولة تعيين المؤشر من مجموعة مختلفة من حجم الصفحة.فإنه سيتم أيضا تعطيك وقت التحويل البرمجي خطأ عند محاولة تعيين قيمة بسيطة شار مؤشر إلى p.حاولت هذا مع دول مجلس التعاون الخليجي ويبدو أن العمل مع ANSI ، C89 و C99.

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

void foo(char * p, int plen);

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

void foo(char (*p)[10]);

..أن القوة المتصل أن أعطيك العازلة من الحجم المحدد.

هذا يبدو مفيد جدا ولكن لم أر مؤشر أعلن مثل هذا في أي رمز من أي وقت مضى لقد ركض عبر.

سؤالي هو:هل هناك أي سبب لماذا الناس لا تعلن عن مؤشرات مثل هذا ؟ أنا لا رؤية واضحة شرك?

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

المحلول 2

وأود أن أضيف إلى الجواب AndreyT ل(في حالة أي شخص يتعثر على هذه الصفحة تبحث عن مزيد من المعلومات حول هذا الموضوع):

وكما قلت تبدأ للعب أكثر مع هذه التصريحات، وأنا أدرك أن هناك عائقا كبيرا المرتبطة بها في C (على ما يبدو ليس في C ++). ومن الشائع نسبيا أن يكون الوضع حيث كنت ترغب في إعطاء المتصل مؤشر CONST إلى المخزن مؤقت كنت قد كتبت في. لسوء الحظ، لم يكن ذلك ممكنا عندما يعلن مؤشر من هذا القبيل في C. وبعبارة أخرى، فإن مستوى C (6.7.3 - الفقرة 8) هو على خلاف مع شيء من هذا القبيل:


   int array[9];

   const int (* p2)[9] = &array;  /* Not legal unless array is const as well */

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

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

نصائح أخرى

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

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

void foo(char (*p)[10]);

(في C++ لغة ويتم ذلك أيضا مع المراجع

void foo(char (&p)[10]);

).

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

typedef int Vector3d[3];

void transform(Vector3d *vector);
/* equivalent to `void transform(int (*vector)[3])` */
...
Vector3d vec;
...
transform(&vec);

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

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

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

void foo(char p[], unsigned plen);

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

ومع ذلك ، إذا كان صفيف حجم ثابت ، ويمر على أنها مؤشر إلى عنصر

void foo(char p[])

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

سبب آخر قد تعيق اعتماد حجم ثابت صفيف يمر تقنية هيمنة السذاجة نهج الكتابة من تخصيصها المصفوفات.على سبيل المثال ، إذا كان البرنامج يستدعي ثابت المصفوفات من النوع char[10] (كما في المثال) ، متوسط المطور malloc مثل صفائف

char *p = malloc(10 * sizeof *p);

هذه المجموعة لا يمكن تمريرها إلى وظيفة كما أعلن

void foo(char (*p)[10]);

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

char (*p)[10] = malloc(sizeof *p);

هذا بالطبع يمكن أن تنتقل بسهولة إلى ما سبق أعلن foo

foo(p);

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

حسنا، ببساطة، C لا تفعل أشياء بهذه الطريقة. يتم تمرير صفيف من نوع T حولها باعتبارها المؤشر إلى T لأول مرة في مجموعة، وهذا كل ما تحصل عليه.

وهذا يسمح لبعض الخوارزميات باردة وأنيقة، مثل حلقات من خلال مجموعة مع عبارات مثل

*dst++ = *src++

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

ما يقترب من ما سألتم C هو لتمرير حول struct (من حيث القيمة) أو مؤشر إلى واحد (حسب المرجع). طالما تم استخدام نفس نوع البنية على كلا الجانبين من هذه العملية، فإن كلا من التعليمات البرمجية التي تسليم الإشارة والرمز الذي يستخدمه متفقون حول حجم يجري التعامل معها البيانات.

والبنية لديك يمكن أن تحتوي على كل ما البيانات التي تريدها. يمكن أن تحتوي مجموعة الخاصة بك من حجم واضحة المعالم.

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

ويمكنك الإعلان عن مجموعة من الأحرف بعدد من الطرق:

char p[10];
char* p = (char*)malloc(10 * sizeof(char));

والنموذج الأولي لوظيفة تأخذ صفيف من حيث القيمة هو:

void foo(char* p); //cannot modify p

وأو بالرجوع:

void foo(char** p); //can modify p, derefernce by *p[0] = 'f';

وأو عن طريق تركيب مجموعة:

void foo(char p[]); //same as char*

وأنا لا أوصي هذا الحل

typedef int Vector3d[3];

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

void foo(Vector3d a) {
   Vector3D b;
}

وحيث sizeof ل! = sizeof ب

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

ولكن أنا أيضا على أن صيغة و نموذج العقلية باستخدام المؤشرات هو أبسط و أسهل للتذكر.

وهنا بعض المعلومات التي كنت قد تأتي عبر.

  • الوصول إلى مجموعة يتطلب استخدام (*p)[]:

    void foo(char (*p)[10])
    {
        char c = (*p)[3];
        (*p)[0] = 1;
    }
    

    فمن المغري أن نستخدم المحلية المؤشر إلى شار بدلا من ذلك:

    void foo(char (*p)[10])
    {
        char *cp = (char *)p;
        char c = cp[3];
        cp[0] = 1;
    }
    

    ولكن هذا جزئيا هزيمة الغرض من استخدام النوع الصحيح.

  • واحد يجب أن نتذكر أن استخدام عنوان المشغل عند تعيين صفيف عنوان إلى مؤشر إلى الصفيف:

    char a[10];
    char (*p)[10] = &a;
    

    العنوان-مشغل يحصل على عنوان مجموعة كاملة في &a, مع النوع الصحيح لتعيين p.دون المشغل ، a يتم تحويلها تلقائيا إلى عنوان أول عنصر من المصفوفة كما في &a[0], الذي لديه نوع مختلف.

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

    أحد أسباب مشكلتي قد تكون تعلمت K&R C مرة أخرى في 80s ، التي لم تسمح باستخدام & المشغل على كل المصفوفات حتى الآن (على الرغم من أن بعض المجمعين تجاهل ذلك أو السكوت على جملة).والتي بالمناسبة قد تكون سبب آخر المؤشرات إلى صفائف لديهم صعوبة في الحصول على المعتمد:إلا أنها تعمل بشكل صحيح منذ ANSI C ، & مشغل القيد قد يكون سبب آخر أن نرى لهم محرجا جدا.

  • عندما typedef هو لا تستخدم لخلق نوع مؤشر إلى الصفيف (في ملف الرأس), ثم العالمية مؤشر إلى الصفيف يحتاج أكثر تعقيدا extern إعلان مشاركتها عبر الملفات:

    fileA:
    char (*p)[10];
    
    fileB:
    extern char (*p)[10];
    

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

لا يمكن أن كنت تستخدم فقط void foo(char p[10], int plen);؟

في بلدي مترجم (VS2008) فإنه يعامل char (*p)[10] باعتبارها مجموعة من المؤشرات الطابع، كما لو لم يكن هناك قوسين، حتى لو جمع كملف C. هو دعم مترجم لهذا "المتغير"؟ إذا كان الأمر كذلك وهذا هو السبب الرئيسي عدم استخدامه.

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