ما هي دلالات "تقييد" C99 فيما يتعلق بالمؤشرات إلى المؤشرات؟

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

سؤال

أنا أفعل الكثير من الحساب المصفوفة وترغب في الاستفادة من C99 restrict مؤشر المؤشر.

أرغب في إعداد مصفوفاتي كمؤشرات للمؤشرات للسماح بتراكم سهلة، مثل ذلك:

int **A = malloc (ncols * sizeof(int *));
A[0] = malloc (nrows * ncols * sizof(int));
for (int i=1; i < ncols; i++) {
    A[i] = A[0] + i*nrows;
}

الآن، للحصول على وظيفة الضرب المصفوفة

void mmultiply ( int nrows, int ncols, int **Out, int **A, int **B);

يجب أن أكون مؤشرات كلا من المؤشرات للحجج مقيدة؟ إنه بناء جملة صالح، لكنني أواجه صعوبة في تحديد ما إذا كان int *restrict *restrict يتصرف بطريقة مختلفة عن int **restrict.

بعد ذلك، مع مقيد المؤشرات بشكل صحيح، يتم الوصول إلى العناصر من خلال A[0][col*nrows + row] غير معرف؟ (أي، هل سيفترض المترجم أنني فقط الوصول إلى المصفوفة من خلال A[col][row] لقيم. row مثل ذلك row < nrow) أو يجب أن أظل ببساطة متسقة؟

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

المحلول

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

للسؤال الثاني، "نعم"، سوف يفترض أن أي شيء يمكن الوصول إليه من خلال مؤشر صف يتم الوصول إليه فقط من خلال مؤشر الصف.

يمكنك رمي const هناك أيضا.

أخيرا، إذا كان هذا هو دول مجلس التعاون الخليجي على -O2، أو -O3 أو -OS، يقوم المحول البرمجي بالفعل بإجراء تحليل الاسم المستعار يعتمد على أنواع. أنا متأكد من أن التحويل البرمجيات الأخرى تفعل ذلك أيضا. هذا يعني أن تقييد المؤشرات vs مفهومة بالفعل، ولم يترك فقط الصفائف التي قد تخزن مع بعضها البعض.

باختصار، سيتفترض المحسن أن المؤشرات لا يتم تخزينها في Ints، ويعرف أنها لا تفعل أي مؤشر يكتب أثناء الحلقة.

لذلك من المحتمل أن تحصل على نفس الرمز مع تقييد واحد فقط.

نصائح أخرى

يخبر المقيد الخارجي (الثاني) بالمترجم الذي لا يعرفه أي من صفائف المؤشرات (A، B، والخروج). يخبر المقيد الداخلي (الأول) بالمترجم الذي لا يعرفه أي من صفائف Ints (مدفوعة عن طريق عناصر صفائف المؤشرات).

إذا تمكنت من الوصول إلى كل من [0] [COL * Nrows + nrows] و [COL] [صف] ثم تنتهك تقييد الداخلية، لذلك قد تنكسر الأمور.

int **restrict تؤكد فقط أن الذاكرة الموجهة عن طريق الخروج، A و B لا تتداخل (باستثناء أن A و B يمكن أن تتداخل، على افتراض أن وظيفتك لا تعدل أي منهما). وهذا يعني صفائف المؤشرات. لا يؤكد أي شيء عن محتويات الذاكرة المدببة إلى الخارج، A، و B. FiltNote 117 في N1124 يقول:

إذا كان المعرف p لديه نوع (int ** تقييد)، فإن تعبير المؤشر P & P + 1 تستند إلى كائن المؤشر المحظور المعين بواسطة P، ولكن تعبيرات المؤشر * P و P [1] ليست كذلك.

عن طريق القياس مع const, ، أظن أن التصفيات مع restrict سيؤدي مرتين إلى تأكيد ما تريد، وهو أن أيا من القيم في نقاط الصفيف لتداخل الذاكرة. لكن قراءة المعيار، لا أستطيع أن أثبتت نفسي أنها فعلا. أعتقد أن "دع D أن يكون إعلان معرف عادي يوفر وسيلة لتعيين كائن ف كمؤشر مؤهل مؤهل لكتابة T" هل يعني بالفعل ذلك int *restrict *restrict A, ، ثم [0] و [1] هي كائنات مخصصة كمؤشر مقيد مؤهل إلى INT. لكنها قانونية ثقيلة جدا.

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

لذلك أنا لا أعرف حقا ما اكتسبته أكثر من صفيف C 2-D التقليدية، حيث تخصص فقط rows * cols * sizeof(int), وفهرس مع A[cols*row + col]. وبعد ثم من الواضح أنك تحتاج فقط إلى استخدام واحد من تقييده، وأي مترجم يقوم به أي شيء restrict سوف تكون قادرة على إعادة الطلب يقرأ من A و B عبر الكتابة إلى الخارج. بدون restrict, بالطبع، لا يمكن ذلك، لذلك من خلال القيام بما تفعله، فأنت ترمي نفسك على رحمة مترجمك. إذا لم يتمكن من التعامل مع تقييد مزدوج، فقط حالة تقييد واحد، ثم كلفك غير مباشر الخاص بك التحسين.

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

يتم الوصول إلى العناصر من خلال [0] [col * nrows + صف] غير محدد؟

نعم، إذا تم تعديل العنصر من قبل أحد الوصول، لأن هذا يجعل اسم مستعار [0] للذاكرة التي تم الوصول إليها أيضا عبر [COL]. سيكون ذلك على ما يرام إذا كان فقط A و B تقييد المؤشرات المؤهلة، ولكن ليس إذا كان [0] و [COL].

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

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