لماذا لا يكون عنوان المصفوفة قابلاً للتحويل إلى ** في لغة C؟


مرحبًا. لماذا عنوان المصفوفة (على سبيل المثال &) غير قابلة للتحويل إلى ** شركة؟

ما حاولت:

حاولت التالي ..

ج
#include <stdio.h>


int main()
{
    int** a;
    int b[5];
    b[1] = 123;
    
    //a = &b; //< results in output of warning "assignment to ‘int **’ from incompatible pointer type ‘int (*)[5]’"
    int* c = b;
    a = &c; //< this works
    printf("*a is %i\n", (*a)[1]);
    
    return 0;
}

أتوقع ذلك، كما b يمكن تحويلها إلى int*، سيكون من الممكن تحويل &b ل int**. ومع ذلك، يؤدي هذا إلى التحذير المذكور في مقطع التعليمات البرمجية أعلاه.

استخدام mingw gcc على نظام التشغيل Windows هذا (الكتابة ثم القراءة من a ** التي تم تعيينها لعنوان المصفوفة) عملت بالفعل مرة واحدة ولكن ليس مرة ثانية.

الحل 1

b عبارة عن مصفوفة: اسم المصفوفة هو مؤشر للعنصر الأول، لذلك من الناحية الفنية، &b هو مؤشر إلى مؤشر إلى عدد صحيح. لكن … المترجم الخاص بك لا يوافق لأنه (بشكل مبرر) يعتبره مؤشرًا لمجموعة من الأعداد الصحيحة. والمصفوفات عبارة عن سكر نحوي للمؤشر الحسابي الذي يحدث في الخلفية، لذا فهي لا تعمل تمامًا كما قد تعتقد.
يمكنك الالتفاف حوله عن طريق صنع b مؤشر إلى int بدلاً من ذلك:

ج
#include <stdio.h>
#include <malloc.h>
int main()
    {
    int** a;
    int* b = malloc(sizeof(int) * 5);
    b[1] = 123;
    
    a = &b; 
    printf("*a is %i\n", (*a)[1]);

    free(b);
    return 0;
    }

الحل 2

يعني هذا التحذير أنه يجب عليك تعيين نوع الإرسال الناتج بشكل صريح، مما يعني أنك تدرك أن نتيجة استخدام هذا المتغير في الكود يمكن أن تسبب تعطلًا أو تسربًا للذاكرة. كمثال رمز التعليقات من هذه المناقشة https://www.codeproject.com/Answers/5370174/C-language-I-dont-understand-multi- Dimensional-arr#answer3[^]:

int mat[3][3] = { {0,1,2},{3,4,5},{6,7,8} };
int **ptr = (int **)&mat;
printf("%d\n", mat[1][1]); /* prints "4" */
printf("%d\n", ptr[1][1]); /* seg faults! */ 

هنا لديك مصفوفة متعددة الأبعاد وقم بإلقائها إلى int**. سيعمل هذا الرمز على نظام x86 وسيتعطل على نظام x64 في المكان المحدد في التعليق.

لذلك، يوضح لك تحذير الإرسال أنه يجب عليك الاهتمام باستخدام المتغير الناتج – هذا كل شيء.

الحل 3

شكرًا لمكسيم كارتافينكوف على وجه الخصوص لأن الإشارة إلى انتهاك الوصول ساعدني في الاتجاه الصحيح.

باستخدام المثال الخاص بي ..

ج
#include <stdio.h>


int main()
{
    int a[5];
    a[1] = 123;
    a[2] = 456;
    
    printf("&a is %p\n", &a); //< type of &a is int(*)[], which apparently is some sort of special type
    printf("a is %p\n", a); //< type is int*
    // ^
    // both will print the same address

    printf("&(&a)[1] is %p\n", &(&a)[1]);
    printf("((char*)a) + sizeof(a) is %p\n", ((char*)a) + sizeof a);
    // ^
    // both will print the same address

    int** b = &a;

    printf("sizeof(int) is %i\n", sizeof(int));
    printf("sizeof(int**) is %i\n", sizeof(int**));
    // ^
    // in test case this printed..
    // sizeof(int) is 4
    // sizeof(int**) is 8
    
    printf("b[1] is %p\n", b[1]);
    // ^
    // printed..
    // b[1] is 0x1c8
    printf("b[1] is %i\n", b[1]); //< as indexing into array of int** goes to a[2] rather than a[1] because int** is twice the size of int
    // ^
    // printed..
    // b[1] is 456

    return 0;
}

وهكذا نشأ الارتباك الذي كنت أشعر به من توقع “أن &a (أين typeof(a) يكون int[5]) سيكون عنوان مؤشر مختلفًا على الرغم من أن هذا ليس ما حدث”.

ربما &a سيتطلب إنشاء متغير آخر من النوع int** ضمنيًا كصفيف على المكدس لا يتطلب مثل هذا المتغير لأنه يمكنه تخزين العناصر مباشرة بدلاً من ذلك؟ ربما هذا هو السبب &a بدلاً من ذلك، يُرجع فقط نفس المؤشر ولكن من نوع مختلف، لتجنب إنشاء متغير مخفي؟

الحل 4

من الأسهل استخدام طاقم الممثلين عند ضبط a على عنوان b:

سي ++
int** a;
int b[5];
b[0] = 1;
b[1] = 3;
b[2] = 5;
b[3] = 7;
b[4] = 9;

a = (int**)&b;
for (int i = 0; i < 5; i++)
{
    printf("a = %p, *a = %d\n", a, *a++);
}

コメント

タイトルとURLをコピーしました