C / المتوسط: الذاكرة والمكونات الموثوقة / الدرس 9
البنى ونماذج البيانات
اجمع القيم المترابطة دون الخلط بين تمثيل الذاكرة والملكية والمساواة وتنسيق الملف الخارجي.
في هذه الصفحة
أهداف الدرس
- الحفاظ على الثوابت بين حقول البنية.
- شرح إسناد البنى ونسخ المؤشرات السطحي.
- استخدام التعدادات والاتحادات الموسومة لتمثيل البدائل.
الحقول سجل منطقي واحد
تجمع struct أعضاء مسماة لكل منها نوعه. قد يدخل المترجم حشوا بين الأعضاء وفي النهاية لتلبية المحاذاة. يتكون السجل المنطقي من قيم الأعضاء، وليس وعدا بأن البايتات مضغوطة أو ذات تخطيط عالمي. استخدم أسماء الحقول لوصف المجال بدلا من افتراضات ترتيبية.
يحدد النموذج المفيد علاقات الحقول. في مجال عددي لا يتجاوز الحد الأدنى الأعلى. وفي المخزن لا يتجاوز الطول السعة، وتتطلب السعة غير الصفرية تخزينا صالحا. يجب أن يحفظ المنشئ وكل تعديل هذه العلاقات؛ ويجعل كشف الحقول علنا كل المستدعين مسؤولين عنها.
العمل بسجل قيمي
يستخدم المثال الأول بنية تحتوي قيما فقط. ينسخ الإسناد قيم الأعضاء إلى سجل ثان؛ ولا يغير تعديل x في النسخة الأصل. تربط المهيئات المسماة القيم بأسماء الحقول فتوضح القصد عند تغيير ترتيب التصريح. وتُهيأ الحقول المحذوفة وفقا لقواعد تهيئة المجاميع المعتادة.
إذا احتوى السجل مصفوفات ثابتة مدمجة، ينسخ إسناد البنية عناصرها المخزنة كجزء من القيمة. يختلف ذلك عن بنية تحتوي مؤشرات إلى تخصيصات خارجية فقط. تنسخ الأخيرة قيم المؤشرات وقد تنشئ أسماء مشتركة، فتحتاج ملكيتها إلى عقد منفصل.
#include <stdio.h>
struct point { int x; int y; };
int main(void)
{
struct point first = {.x = 2, .y = 5};
struct point copy = first;
copy.x = 9;
printf("%d,%d %d,%d\n", first.x, first.y, copy.x, copy.y);
return 0;
}
2,5 9,5
يمتلك كائنا البنية أعضاء int مستقلة.
أعضاء المؤشرات تحتاج سياسة نسخ
إذا امتلكت البنية char *name يكرر الإسناد العادي العنوان لا المحارف المخصصة. وعد النسختين مالكين حصريين يؤدي إلى التحرير المزدوج. اختر اصطلاح منع النسخ أو عملية نسخ عميق صريحة أو ملكية مشتركة ببروتوكول مراجع صحيح أو نوع عرض مستعار دون مسؤولية تحرير.
لا تولد C منشئات أو هادمات تلقائيا. عرّف دوال التهيئة والتدمير، ووثق هل يقبل التدمير كائنا فارغا، وأعد الحقول إلى الثابت المحايد عند الحاجة. قد يختصر typedef اسم النوع، لكنه لا يضيف سلوك ملكية ولا يجعل الإسناد العرضي آمنا.
تمثيل بديل نشط واحد
يشارك union التخزين بين أعضائه. يضيف الاتحاد الموسوم enum يحدد العضو الذي يعامله البرنامج كنشط حاليا. يجب تحديث الوسم والحمولة معا وفق ثابت النوع. يوضح ذلك قيمة تكون إما عددا صحيحا أو حقيقيا بدلا من استخدام قيمة عددية سحرية.
يكتب المثال العضو الصحيح ولا يختاره إلا بعد فحص الوسم. في منطق تطبيق قابل للنقل لا تستخدم عضوا آخر من الاتحاد كحيلة تحويل عامة؛ لإعادة تفسير التمثيل قيود وقد تكشف تمثيلات اعتراض أو تمثيلات غير مناسبة. يعبر التحويل الصريح للقيمة عن عملية مختلفة أوضح عادة.
#include <stdio.h>
enum kind { INTEGER_VALUE, REAL_VALUE };
struct number {
enum kind tag;
union { int integer; double real; } data;
};
int main(void)
{
struct number value = {.tag = INTEGER_VALUE, .data.integer = 7};
if (value.tag == INTEGER_VALUE) printf("integer=%d\n", value.data.integer);
else if (value.tag == REAL_VALUE) printf("real=%.1f\n", value.data.real);
return 0;
}
integer=7
يختار الوسم العضو المهيأ بالعدد الصحيح سبعة.
تسلسل المعنى لا الحشو الخام
يربط كتابة sizeof(struct record) بايتا مباشرة إلى ملف التنسيق بالحشو وترتيب البايتات وتمثيل الأعداد وقرارات ABI للمترجم. وقد يكشف حشوا غير مهيأ أيضا. حدد الحقول الخارجية وترميزاتها صراحة، ثم رمز كل حقل وفكه مع فحوص المدى والطول. وقد يكون التنسيق النصي أبسط عندما لا يهيمن الحجم والسرعة.
المساواة دلالية كذلك. قد تختلف memcmp لبنيتين متساويتين منطقيا لأن بايتات الحشو لا يلزم أن تتطابق. قارن الأعضاء ذات المعنى وفق قاعدة مساواة المجال. وإذا وجد حقل عائم فعلى القاعدة معالجة التقريب والقيم الخاصة أيضا، بدلا من وراثة مقارنة بايتية عرضا.
الأخطاء الشائعة وإصلاحها
يسبب نسخ سجل تحريرا مزدوجا لاحقا.
- لماذا يحدث؟
- نُسخت المؤشرات سطحيا إلى مالكين ظاهريين.
- كيف تصلحه؟
- نفذ سياسة نسخ أو نقل صريحة.
- كيف تتجنب تكراره؟
- افصل السجلات المالكة من العروض المستعارة في الواجهات.
لا تُقرأ البنية المحفوظة على هدف آخر.
- لماذا يحدث؟
- اعتُبر التخطيط الخام تنسيقا قابلا للنقل.
- كيف تصلحه؟
- رمز الحقول بترتيب بايتات وعروض محددة.
- كيف تتجنب تكراره؟
- وثق التسلسل مستقلا عن نوع الذاكرة.
يقول الوسم حقيقيا لكن الشيفرة تقرأ الصحيح.
- لماذا يحدث؟
- لم يُفرض ثبات الوسم والحمولة.
- كيف تصلحه؟
- تفرع وفق الوسم واقرأ البديل الموثق فقط.
- كيف تتجنب تكراره؟
- ركز الإنشاء في دوال واختبر كل بديل.
اختبر فهمك
1. يمتلك سجل تسمية مخصصة وعددا. صمم تصرف النسخ العميق إذا فشل تخصيص التسمية الجديدة.
اعرض الحل المشروح
أبق الوجهة في حالة فارغة صالحة. حدد حجم التسمية وافحصه وخصص تخزينا جديدا وانسخ التسمية كاملة بعد نجاح التخصيص فقط. انسخ العدد كقيمة. إذا فشل التخصيص فأعد فشلا دون نقل مؤشر المصدر أو تعديله. يجب ألا تصبح الوجهة مالكا ثانيا للتسمية الأصلية.
2. لماذا قد يفشل اختبار مساواة بايتي لسجلي point متساويين على تنفيذ افتراضي؟ أعط المقارنة الصحيحة.
اعرض الحل المشروح
قد تختلف بايتات الحشو مع تطابق x وy. استخدم first.x == second.x && first.y == second.y للتعبير عن مساواة القيم. تقرأ هذه المقارنة الأعضاء ذات المعنى فقط وتبقى صحيحة إن تغير الحشو. وإذا عامل المجال الإحداثيات تقريبيا لاحقا فعدل المقارنة الدلالية عمدا بدلا من المساواة البايتية الخام.
أهم ما تعلمته
- يربط ثابت البنية حقولها وقواعد ملكيتها.
- ينسخ الإسناد قيم المؤشرات دون تكرار تخصيصاتها.
- عرّف التنسيقات الخارجية والمساواة الدلالية مستقلة عن الحشو.