c++ سعر صفائف هي مؤشرات؟



pointer معنى (6)

تكرار محتمل:
هو اسم صفيف مؤشر في C؟

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


Answer #1

في C ++ نوع الصفيف لديه "سمة الحجم"، لذلك ل

T a[10];
T b[20];

a و b أنواع مختلفة.

هذا يسمح لاستخدام التعليمات البرمجية مثل هذا

template<typename T, size_t N>
void foo(T (&a)[N])
{
   ...
}

Answer #2

وأكبر نقطة من الارتباك بين المصفوفات والمؤشرات تأتي من قرار K & R لجعل معلمات الدالة التي يتم اعتبارها كنوع صفيف تتصرف كما لو أنها تم الإعلان عنها كمؤشرات. الإعلانات

void foo(int a[]);
و
void foo(int *a);
هي ما يعادلها، كما هو (بقدر ما أستطيع أن أقول)
void foo(int a[5]);
على الرغم من أنني لست إيجابيا المترجم سيكون مطلوبا لقبول إشارة إلى [6] ضمن هذه المهمة الأخيرة. في سياقات أخرى، يقوم تعريف صفيف بتخصيص مساحة لعدد العناصر المشار إليه. لاحظ أنه نظرا لما يلي:

typedef int foo[1];

فإن أي إعلان من نوع فو تخصيص مساحة لعنصر واحد، ولكن أي محاولة لتمرير فو كمعلمة وظيفة بدلا من ذلك تمرير العنوان. شيء من خدعة مفيدة تعلمت في دراسة تنفيذ va_list.


Answer #3

لا، فإنها لا تنفذ بشكل مختلف. كلاهما يجد عناصر بنفس العملية الحسابية: a[i] في العنوان a + i*sizeof(a[0]) ، كما p[i] على العنوان p + i*sizeof(p[0]) .

ولكن، يتم التعامل معها بشكل مختلف من قبل نظام نوع . C ++ لديه معلومات الكتابة على المصفوفات التي يمكن رؤيتها من خلال sizeof operator (مثل C)، الاستدلال قالب، وظيفة التحميل الزائد، رتتي، وهلم جرا. في الأساس في أي مكان في اللغة التي تستخدم نوع المعلومات، فمن الممكن للمؤشرات والمصفوفات أن تتصرف بشكل مختلف.

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

في كل حالة، هناك بنية مختلفة وطريقة مختلفة للتفكير في المفهومين، ولكنها تؤدي إلى نفس رمز الجهاز في النهاية.


Answer #4

لا تعرف عن C ++. ل C، وجواب فاق إجابات أفضل بكثير مما كنت يمكن.

مقتطف صغير من c-فاق:

6.3 فما المقصود بعبارة `تكافؤ المؤشرات والمصفوفات 'في C؟

[...]

على وجه التحديد، حجر الزاوية في التكافؤ هو هذا التعريف الرئيسي:

تشير الإشارة إلى كائن من نوع صفيف من T الذي يظهر في تعبير تعطل (مع ثلاثة استثناءات) إلى مؤشر لعنصر الأول. نوع المؤشر الناتج هو مؤشر إلى T.

[...]


Answer #5

في C ++ وفقا ل C ++ معيار 4.2:

يمكن تحويل لفلو أو رفالو من نوع "صفيف N T" أو "صفيف مجهولة من T" إلى رفلو من نوع "مؤشر إلى T." والنتيجة هي مؤشر على العنصر الأول من الصفيف.


Answer #6

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

حيث ينشأ الارتباك هو كيف يعالج C تعبيرات الصفيف. N1570 :

6.3.2.1 القيم، والمصفوفات، والوظائف

...
3 ما لم يكن معامل تشغيل _Alignof عامل _Alignof أو عامل _Alignof & أو سلسلة حرفية تستخدم لتهيئة صفيف يتم تحويل تعبير يحتوي على نوع '' صفيف من النوع '' إلى تعبير مع نوع '' مؤشر لنوع '' يشير إلى العنصر الأولي للكائن صفيف وليس لفالو. إذا كان عنصر الصفيف يحتوي على فئة تخزين التسجيل، فإن السلوك غير محدد.

لنلق نظرة على الإعلانات التالية:

int arr[10] = {0,1,2,3,4,5,6,7,8,9};
int *parr = arr;

arr هو 10 عنصر مجموعة من int ؛ فإنه يشير إلى كتلة متجاورة من الذاكرة كبيرة بما فيه الكفاية لتخزين 10 قيم int . التعبير arr في الإعلان الثاني هو من نوع الصفيف، ولكن لأنه ليس من المعامل & أو sizeof وليس سلسلة حرفية، نوع التعبير يصبح "مؤشر إلى int "، والقيمة هي العنوان من العنصر الأول، أو &arr[0] .

parr هو مؤشر إلى إنت؛ فإنه يشير إلى كتلة من الذاكرة كبيرة بما فيه الكفاية لعقد عنوان كائن int واحد. يتم تهيئته للإشارة إلى العنصر الأول في arr كما هو موضح أعلاه.

فيما يلي خريطة افتراضية للذاكرة تبين العلاقة بين الاثنين (على افتراض أن 16 بتة بيت وعناوين 32 بت):

Object           Address         0x00  0x01  0x02  0x03
------           -------         ----------------------
   arr           0x10008000      0x00  0x00  0x00  0x01
                 0x10008004      0x00  0x02  0x00  0x03
                 0x10008008      0x00  0x04  0x00  0x05
                 0x1000800c      0x00  0x06  0x00  0x07
                 0x10008010      0x00  0x08  0x00  0x09
  parr           0x10008014      0x10  0x00  0x80  0x00

أنواع المسألة لأشياء مثل sizeof و & ؛ sizeof arr == 10 * sizeof (int) ، والتي في هذه الحالة هي 20، في حين sizeof parr == sizeof (int *) ، والتي في هذه الحالة هي 4. وبالمثل، فإن نوع التعبير &arr هو int (*)[10] ، أو مؤشر إلى صفيف مكون من 10 عناصر من int ، في حين أن نوع &parr هو int ** ، أو مؤشر إلى مؤشر إلى int .

لاحظ أن التعبيرات arr و &arr سوف تسفر عن نفس القيمة (عنوان العنصر الأول في arr )، ولكن أنواع التعبيرات مختلفة ( int * و int (*)[10] ، على التوالي). هذا يحدث فرقا عند استخدام مؤشر الحساب. على سبيل المثال، نظرا:

int arr[10] = {0,1,2,3,4,5,6,7,8,9};
int *p = arr;
int (*ap)[10] = &arr;

printf("before: arr = %p, p = %p, ap = %p\n", (void *) arr, (void *) p, (void *) ap);
p++;
ap++;
printf("after: arr = %p, p = %p, ap = %p\n", (void *) arr, (void *) p, (void *) ap);

يجب أن يقوم خط "قبل" بطباعة نفس القيم لكل التعبيرات الثلاثة (في خريطتنا الافتراضية، 0x10008000 ). يجب أن يظهر خط "بعد" ثلاث قيم مختلفة: 0x10008000 ، 0x10008002 (قاعدة زائد 0x10008002 ( sizeof (int) )، و 0x10008014 (قاعدة زائد 0x10008014 sizeof (int [10]) ).

لنعود الآن إلى الفقرة الثانية أعلاه: يتم تحويل تعبيرات الصفيف إلى أنواع المؤشرات في معظم الحالات. دعونا ننظر إلى التعبير الفرعي arr[i] . بما أن التعبير arr لا يظهر كمعامل لأي من sizeof أو & ، ولأنه ليس sizeof حرفيا يتم استخدامه لتهيئة صفيف آخر، يتم تحويل نوعه من "صفيف 10 عناصر من int " إلى "مؤشر إلى int " ، ويتم تطبيق العملية الفرعية على قيمة المؤشر هذه. في الواقع، عندما تنظر إلى تعريف لغة C، ترى اللغة التالية:

6.5.2.1 ترتيب الصفيف
...
2 تعبير بوستفيكس متبوعا بتعبير بين أقواس معقوفة [] عبارة عن تسمية مقسمة لعنصر من كائن مصفوفة. تعريف المشغل الفرعي [] هو أن E1 [E2] متطابق مع (* ((E1) + (E2))) . بسبب قواعد التحويل التي تنطبق على عامل التشغيل ثنائي، إذا كان E1 كائن مصفوفة (على نحو مكافئ، مؤشر إلى العنصر الأولي من كائن مصفوفة) و E2 هو عدد صحيح، E1 [E2] بتعيين العنصر E2-ث من E1 (العد من الصفر).

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

int foo(int *p, size_t size)
{
  int sum = 0;
  int i;
  for (i = 0; i < size; i++)
  {
    sum += p[i];
  }
  return sum;
}

int main(void)
{
  int arr[10] = {0,1,2,3,4,5,6,7,8,9};
  int result = foo(arr, sizeof arr / sizeof arr[0]);
  ...
}

يعمل بالطريقة التي يفعلها. main هو التعامل مع مجموعة من int ، في حين foo التعامل مع مؤشر إلى int ، ولكن كلاهما قادرون على استخدام المشغل الفرعي كما لو كانوا على حد سواء التعامل مع نوع صفيف.

ويعني هذا أيضا أن الترتيب الفرعي للمصفوفة هو تبادلي : على افتراض أن التعبير عبارة عن صفيف و i عبارة عن تعبير صحيح، و a[i] و i[a] تعبيران صالحان، وكلاهما سيؤدي إلى القيمة نفسها.





pointers