سؤال

في برنامج C أدناه، لا أفهم لماذا buf[0] = 'A' بعد أن أتصل بـ foo.ألا يقوم foo بالتمرير بالقيمة؟

#include <stdio.h>
#include <stdlib.h>

    void foo(char buf[])
    {
      buf[0] = 'A';
    }

    int main(int argc, char *argv[])
    {
      char buf[10];

      buf[0] = 'B';
      printf("before foo | buf[0] = %c\n", buf[0]);
      foo(buf);
      printf("after foo | buf[0] = %c\n", buf[0]);

      system("PAUSE"); 
      return 0;
      }

انتاج:

before foo | buf[0] = 'B' 
after foo | buf[0] = 'A'
هل كانت مفيدة؟

المحلول

void foo(char buf[])

بالضبط مثل

void foo(char* buf)

عندما تسميها ، foo(buf), ، يمكنك تمرير مؤشر حسب القيمة ، لذلك يتم إجراء نسخة من المؤشر.

تشير نسخة المؤشر إلى نفس الكائن مثل المؤشر الأصلي (أو ، في هذه الحالة ، إلى العنصر الأولي للمصفوفة).

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

نصائح أخرى

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

Array كمعلمة دالة تعادل مؤشر ، وبالتالي فإن الإعلان

void foo( char buf[] );

بالضبط مثل

void foo( char* buf );

حجة المصفوفة آنذاك تحلل إلى المؤشر إلى العنصر الأول.

يتم التعامل مع المصفوفات بشكل مختلف عن الأنواع الأخرى؛لا يمكنك تمرير مصفوفة "حسب القيمة" في لغة C.

معيار C99 عبر الإنترنت (مسودة n1256), ، القسم 6.3.2.1، "القيم والمصفوفات ومحددات الوظائف"، الفقرة 3:

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

في المكالمة

foo(buf);

تعبير المصفوفة buf ليس المعامل sizeof أو &, ، كما أنها ليست سلسلة حرفية يتم استخدامها لتهيئة مصفوفة، لذلك يتم تحويلها ضمنيًا ("الاضمحلال") من النوع "مصفوفة مكونة من 10 عناصر من char" إلى "مؤشر إلى char"، و عنوان يتم تمرير العنصر الأول إلى foo.ولذلك فإن أي شيء تفعله buf في foo() سوف تنعكس في buf مصفوفة في main().نظرًا لكيفية تعريف اشتراك المصفوفة، يمكنك استخدام عامل تشغيل منخفض على نوع المؤشر لذلك تبدو وكأنك تعمل مع نوع مصفوفة، لكنك لست كذلك.

في سياق إعلان معلمة الدالة، T a[] و T a[N] هي مرادفة ل T *a, ، ولكن هذا هو فقط الحالة التي يكون فيها ذلك صحيحا.

*char buf [] يعني في الواقع char ** لذلك أنت تمر حسب المؤشر/المرجع. هذا يمنحك أن buf هو مؤشر ، سواء في الأساسية() و فو() وظيفة.

لأنك تمر مؤشر إلى buf (بالقيمة). لذا فإن المحتوى الذي يتم توجيهه بواسطة buf يتغير.

مع مؤشرات الأمر مختلف. أنت تمر بالقيمة ، ولكن ما تمر به هو قيمة المؤشر ، والتي ليست هي نفس قيمة الصفيف.

لذلك ، لا تتغير قيمة المؤشر ، لكنك تقوم بتعديل ما يشير إليه.

المصفوفات والمؤشرات هي (تقريبا) نفس الشيء.

int* foo = malloc(...)

foo[2] بالضبط مثل *(foo+2*sizeof(int))

الحكاية: لقد كتبت

int main(int argc, char *argv[])

من القانوني أيضًا (سوف يجمع ويعمل على نفس المنوال) للكتابة

int main(int argc, char **argv)

و أيضا

int main(int argc, char argv[][])

فهي نفسها بفعالية. إنه أكثر تعقيدًا قليلاً من ذلك ، لأن الصفيف يعرف عدد العناصر التي لديها ، ومؤشر لا. لكنها تستخدم نفس الشيء.

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

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

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

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

يرجى ملاحظة شيء واحد ،

تصريح

void foo(char buf[])

يقول ، هذا سوف يستخدم [] تدوين. ليس عناصر الصفيف التي ستستخدمها.

إذا كنت ترغب في الإشارة إلى ذلك ، فأنت تريد الحصول على قيمة محددة ، فيجب عليك إعلان هذه الوظيفة كـ

void foo(char buf[X]); //where X would be a constant.

بالطبع هذا غير ممكن ، لأنه سيكون عديمة الفائدة (وظيفة للعمل في عنصر N-th من Array؟). ليس عليك أن تكتب المعلومات عن عنصر الصفيف الذي تريد الحصول عليه. كل ما تحتاجه هو إعلان بسيط:

voi foo(char value);

لذا...

void foo(char buf[])

هو إعلان يقول ما هو التدوين الذي تريد استخدامه ([] - جزء) ، ويحتوي أيضًا على مؤشر لبعض البيانات.

علاوة على ذلك ... ماذا تتوقع ... لقد أرسلت لتعمل اسم الصفيف

foo(buf);

وهو ما يعادل & buf [0]. لذلك ... هذا مؤشر.

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

يعد استخدام المصفوفات في معلمات الوظائف طريقة رائعة للإشارة إلى مستخدمي API بأن هذا الشيء يجب أن يكون كتلة من الذاكرة مجزأة إلى أجزاء بحجم بايت ، لكن لا تتوقع أن يرتموا المجمعون إذا قمت بالتهجئة char *foo char foo[] أو char foo[12] في معلمات الوظيفة. لن يفعلوا ذلك.

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